אפליקציה תלת-שכבתית היא אפליקציית מטלות פשוטה שתוכננה כאפליקציה תלת-שכבתית רגילה:
- בק-אנד
- מסד נתונים – MySQL – Cloud SQL
- שמירה במטמון – Redis – Cloud Memorystore
- Middleware/API
- API שמתארח בקונטיינר – Golang – Cloud Run
- חלק קדמי/ממשק משתמש
- ממשק משתמש שמתארח בקונטיינר – Nginx + HTML/JS/CSS – Cloud Run
- פריסה
- פריסה רציפה (CD) – Cloud Build
- ניהול סודות – Cloud Secret Manager
שנתחיל?
כדי להעתיק את קוד המקור ב-Cloud Shell, לוחצים על הקישור הבא. אחרי שמגיעים לשם, פקודה אחת תתחיל הרצה של עותק עובד של האפליקציה בפרויקט.
רכיבים של אפליקציה עם שלוש שכבות
ארכיטקטורת האפליקציה בת שלוש השכבות משתמשת בכמה מוצרים. ברשימה הבאה מפורטים הרכיבים, וגם מידע נוסף על הרכיבים, כולל קישורים לסרטונים קשורים, למסמכי מוצר ולמדריכים אינטראקטיביים.סקריפטים
סקריפט ההתקנה משתמש בקובץ הפעלה שנכתב ב-go ובכלים של Terraform CLI כדי לקחת פרויקט ריק ולהתקין בו את האפליקציה. הפלט צריך להיות אפליקציה פעילה וכתובת URL לכתובת ה-IP של איזון העומסים.
./main.tf
הפעלת שירותים
שירותי Google Cloud מושבתים בפרויקט כברירת מחדל. כדי להשתמש ב-ToDo, צריך להפעיל את השירותים הבאים:
- Service Networking & Serverless VPC Access – מאפשרים ל-Cloud Run לתקשר עם SQL ו-Redis ברשת פרטית, כך שהשרתים האלה לא נגישים לקריאות חיצוניות שמגיעות מממשק ה-API.
- Cloud Build – יוצר תמונות של קונטיינרים ופורס אותן ב-Cloud Run
- Cloud Memorystore – מספק שכבת מטמון לאפליקציה.
- Cloud Run – הכלי בלי שרת (serverless) שיארח את הקונטיינרים ויספק כתובות URL לגישה לאפליקציה.
- Cloud SQL – אחסון מסד נתונים לאפליקציה
- Cloud Storage – משמש את Cloud Build, וגם לטעינת הסכימה במסד הנתונים
- Cloud Secret Manager – משמש להחדרת כתובות ה-IP של המארחים עבור SQL ו-Redis אל Cloud Build עבור Cloud Run.
- Artifact Registry – מאחסן את קובצי האימג' של Docker לשימוש עם Cloud Build.
variable "gcp_service_list" {
description = "The list of apis necessary for the project"
type = list(string)
default = [
"compute.googleapis.com",
"cloudapis.googleapis.com",
"vpcaccess.googleapis.com",
"servicenetworking.googleapis.com",
"cloudbuild.googleapis.com",
"sql-component.googleapis.com",
"sqladmin.googleapis.com",
"storage.googleapis.com",
"secretmanager.googleapis.com",
"run.googleapis.com",
"artifactregistry.googleapis.com",
"redis.googleapis.com"
]
}
resource "google_project_service" "all" {
for_each = toset(var.gcp_service_list)
project = var.project_number
service = each.key
disable_on_destroy = false
}
הגדרת ההרשאות
הפקודה הבאה מגדירה הרשאות ותפקידים ב-IAM שמאפשרים ל-Cloud Build לפרוס שירותים.
- הפעלת חשבון השירות של Cloud Build כדי לפרוס ל-Cloud Run
- הפעלת חשבון השירות של Cloud Build כדי להגדיר גישת VPN ל-Cloud Run
- הפעלת האפשרות לחשבון השירות של Cloud Build לבצע פעולות בחשבון השירות
- הפעלת חשבון השירות של Cloud Build כדי שיפעל בשם חשבון השירות של Compute
- הפעלת האפשרות לחשבון השירות ב-Cloud Build לפרסם ב-Cloud Run
- הפעלת חשבון השירות של Cloud Build כדי להשתמש בסודות
- הפעלת חשבון השירות של Cloud Build כדי לאחסן קונטיינרים ב-Artifact Registry
variable "build_roles_list" {
description = "The list of roles that build needs for"
type = list(string)
default = [
"roles/run.developer",
"roles/vpaccess.user",
"roles/iam.serviceAccountUser",
"roles/run.admin",
"roles/secretmanager.secretAccessor",
"roles/artifactregistry.admin",
]
}
resource "google_project_iam_member" "allbuild" {
for_each = toset(var.build_roles_list)
project = var.project_number
role = each.key
member = "serviceAccount:${local.sabuild}"
depends_on = [google_project_service.all]
}
יצירת רשת עבור מופע SQL
הפקודה הבאה מאפשרת גישה ל-Cloud SQL מ-Cloud Run:
resource "google_compute_global_address" "google_managed_services_vpn_connector" {
name = "google-managed-services-vpn-connector"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 16
network = local.defaultnetwork
project = var.project_id
depends_on = [google_project_service.all]
}
resource "google_service_networking_connection" "vpcpeerings" {
network = local.defaultnetwork
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.google_managed_services_vpn_connector.name]
}
יצירת חיבור לרשת VPC
חיבור Cloud Run למסד נתונים ולשמירת נתונים במטמון
resource "google_vpc_access_connector" "connector" {
provider = google-beta
project = var.project_id
name = "vpc-connector"
ip_cidr_range = "10.8.0.0/28"
network = "default"
region = var.region
depends_on = [google_compute_global_address.google_managed_services_vpn_connector, google_project_service.all]
}
יצירת שרת Redis
הגדרת מופע של שרת Redis ואתחול שלו.
resource "google_redis_instance" "todo_cache" {
authorized_network = local.defaultnetwork
connect_mode = "DIRECT_PEERING"
location_id = var.zone
memory_size_gb = 1
name = "${var.basename}-cache"
project = var.project_id
redis_version = "REDIS_6_X"
region = var.region
reserved_ip_range = "10.137.125.88/29"
tier = "BASIC"
transit_encryption_mode = "DISABLED"
depends_on = [google_project_service.all]
}
יצירת שרת SQL
הפקודה הבאה מגדירה ומפעילה מכונת שרת SQL.
resource "google_sql_database_instance" "todo_database" {
name="${var.basename}-db-${random_id.id.hex}"
database_version = "MYSQL_5_7"
region = var.region
project = var.project_id
settings {
tier = "db-g1-small"
disk_autoresize = true
disk_autoresize_limit = 0
disk_size = 10
disk_type = "PD_SSD"
ip_configuration {
ipv4_enabled = false
private_network = local.defaultnetwork
}
location_preference {
zone = var.zone
}
}
deletion_protection = false
depends_on = [
google_project_service.all,
google_service_networking_connection.vpcpeerings
]
# This handles loading the schema after the database installs.
provisioner "local-exec" {
working_dir = "${path.module}/code/database"
command = "./load_schema.sh ${var.project_id} ${google_sql_database_instance.todo_database.name}"
}
}
יצירת מאגר Artifact Registry
הפקודה הבאה שומרת תמונות Docker לשימוש ב-Cloud Run.
resource "google_artifact_registry_repository" "todo_app" {
provider = google-beta
format = "DOCKER"
location = var.region
project = var.project_id
repository_id = "${var.basename}-app"
depends_on = [google_project_service.all]
}
יצירת סודות
הפקודה הבאה מאחסנת נתוני מארח של Redis ו-SQL ב-Cloud Secrets.
resource "google_secret_manager_secret" "redishost" {
project = var.project_number
replication {
automatic = true
}
secret_id = "redishost"
depends_on = [google_project_service.all]
}
resource "google_secret_manager_secret_version" "redishost" {
enabled = true
secret = "projects/${var.project_number}/secrets/redishost"
secret_data = google_redis_instance.todo_cache.host
depends_on = [google_project_service.all, google_redis_instance.todo_cache, google_secret_manager_secret.redishost]
}
resource "google_secret_manager_secret" "sqlhost" {
project = var.project_number
replication {
automatic = true
}
secret_id = "sqlhost"
depends_on = [google_project_service.all]
}
resource "google_secret_manager_secret_version" "sqlhost" {
enabled = true
secret = "projects/${var.project_number}/secrets/sqlhost"
secret_data = google_sql_database_instance.todo_database.private_ip_address
depends_on = [google_project_service.all, google_sql_database_instance.todo_database, google_secret_manager_secret.sqlhost]
}
יצירת פריט מידע שנוצר בתהליך פיתוח (Artifact) לתוכנת ביניים
הפקודה הבאה יוצרת את קובץ האימג' של Docker ומארחת אותו ב-Artifact Registry: ./code/frontend/clouldbuild.yaml
resource "null_resource" "cloudbuild_api" {
provisioner "local-exec" {
working_dir = "${path.module}/code/middleware"
command = "gcloud builds submit . --substitutions=_REGION=${var.region},_BASENAME=${var.basename}"
}
depends_on = [
google_artifact_registry_repository.todo_app,
google_secret_manager_secret_version.redishost,
google_secret_manager_secret_version.sqlhost,
google_project_service.all
]
}
פריסת קונטיינר של API ב-Cloud Run
הפקודה הבאה משתמשת ב-Cloud Build כדי להפעיל שירות ב-Cloud Run באמצעות הקונטיינר שיצרתם.
resource "google_cloud_run_service" "api" {
name = "${var.basename}-api"
location = var.region
project = var.project_id
template {
spec {
containers {
image = "${var.region}-docker.pkg.dev/${var.project_id}/${var.basename}-app/api"
env {
name = "REDISHOST"
value_from {
secret_key_ref {
name = google_secret_manager_secret.redishost.secret_id
key = "latest"
}
}
}
env {
name = "todo_host"
value_from {
secret_key_ref {
name = google_secret_manager_secret.sqlhost.secret_id
key = "latest"
}
}
}
env {
name = "todo_user"
value = "todo_user"
}
env {
name = "todo_pass"
value = "todo_pass"
}
env {
name = "todo_name"
value = "todo"
}
env {
name = "REDISPORT"
value = "6379"
}
}
}
metadata {
annotations = {
"autoscaling.knative.dev/maxScale" = "1000"
"run.googleapis.com/cloudsql-instances" = google_sql_database_instance.todo_database.connection_name
"run.googleapis.com/client-name" = "terraform"
"run.googleapis.com/vpc-access-egress" = "all"
"run.googleapis.com/vpc-access-connector" = google_vpc_access_connector.connector.id
}
}
}
autogenerate_revision_name = true
depends_on = [
null_resource.cloudbuild_api,
google_project_iam_member.secretmanager_secretAccessor
]
}
פותחים את שירות Cloud Run API כדי שיהיה ניתן לקריאה על ידי כל העולם.
הדפדפן של המשתמש ישלח קריאה לשכבת ה-API הזו של האפליקציה, אבל כברירת מחדל שירותי Cloud Run לא פתוחים לציבור. כדי שהמשתמשים יוכלו להשתמש בשירות הזה, אנחנו צריכים לפתוח את ההרשאות בשירותים האלה כדי שיהיו נגישים לכולם.
resource "google_cloud_run_service_iam_policy" "noauth_api" {
location = google_cloud_run_service.api.location
project = google_cloud_run_service.api.project
service = google_cloud_run_service.api.name
policy_data = data.google_iam_policy.noauth.policy_data
}
יצירת ארטיפקט לממשק קצה
הפקודה הבאה יוצרת את קובץ האימג' של Docker ומארחת אותו ב-Artifact Registry: ./code/frontend/clouldbuild.yaml
resource "null_resource" "cloudbuild_fe" {
provisioner "local-exec" {
working_dir = "${path.module}/code/frontend"
command = "gcloud builds submit . --substitutions=_REGION=${var.region},_BASENAME=${var.basename}"
}
depends_on = [
google_artifact_registry_repository.todo_app,
google_cloud_run_service.api
]
}
פריסת קונטיינר של חזית האתר ב-Cloud Run
הפקודה הבאה משתמשת ב-Cloud Build כדי להתחיל הרצה של שירות ב-Cloud Run באמצעות הקונטיינר שיצרנו
resource "google_cloud_run_service" "fe" {
name = "${var.basename}-fe"
location = var.region
project = var.project_id
template {
spec {
containers {
image = "${var.region}-docker.pkg.dev/${var.project_id}/${var.basename}-app/fe"
ports {
container_port = 80
}
}
}
}
depends_on = [null_resource.cloudbuild_fe]
}
פתיחת שירות ממשק קצה של Cloud Run לקריאה על ידי כל העולם
זהו ממשק הקצה של האפליקציה, שדרכו מוצגים קובצי ה-HTML/JS/CSS שהמשתמשים מקיימים איתם אינטראקציה. כברירת מחדל, שירותי Cloud Run לא פתוחים לציבור. כדי שהאפליקציה הזו תפעל, אנחנו צריכים לפתוח הרשאות בשירותים האלה כדי שיהיו נגישים לכולם.
resource "google_cloud_run_service_iam_policy" "noauth_fe" {
location = google_cloud_run_service.fe.location
project = google_cloud_run_service.fe.project
service = google_cloud_run_service.fe.name
policy_data = data.google_iam_policy.noauth.policy_data
}
./code/database/load_schema.sh
הפעלת סכימת מסד הנתונים
הפקודה הזו יוצרת קטגוריה זמנית ב-Cloud Storage כדי להעלות את הסכימה ל-Cloud SQL.
PROJECT=$1
SQLNAME=$2
SQLSERVICEACCOUNT=$(gcloud sql instances describe $SQLNAME --format="value(serviceAccountEmailAddress)" | xargs)
gcloud storage buckets create gs://$PROJECT-temp
gcloud storage cp schema.sql gs://$PROJECT-temp/schema.sql
gcloud storage buckets add-iam-policy-binding gs://$PROJECT-temp/ --member=serviceAccount:$SQLSERVICEACCOUNT --role=roles/storage.objectViewer
gcloud sql import sql $SQLNAME gs://$PROJECT-temp/schema.sql -q
gcloud storage rm gs://$PROJECT-temp --recursive
./code/middleware/clouldbuild.yaml
יצירת מאגר API
הקוד הזה יוצר קובץ אימג' של Docker לשכבת התוכנה.
name: 'gcr.io/cloud-builders/docker'
args: [ 'build', '-t', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/api', '.' ]
```
#### Push API container to Artifact Registry
Pushing the container to Artifact Registry makes it possible for Cloud Run to
get the image and serve it.
``` yaml
name: 'gcr.io/cloud-builders/docker'
args: ['push', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/api']
החלפות
הקוד הבא יוצר משתנים עם ערכי ברירת מחדל, כדי שאפשר יהיה לשנות את הערכים האלה בזמן הפריסה.
substitutions:
_REGION: us-central1
_BASENAME: todo
./code/frontend/clouldbuild.yaml
תוכן קוד העיסוי
ממשק הקצה הוא HTML/JS/CSS סטטי לחלוטין. האפליקציה צריכה להפנות לכתובת ה-URL של שירות ה-API שיצרנו, אבל לשירותי Cloud Run מוקצית כתובת URL עם מחרוזת אקראית. הסקריפט הזה של 'עיבוד נתונים' מתעד את כתובת ה-URL האקראית ומחדיר אותה לקוד של ה-JS הסטטי במאגר התגים הזה.
name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: bash
args: [ './massage.sh', '$_REGION' ]
יצירת מאגר API
הקוד הבא יוצר קובץ אימג' של Docker לשכבת התוכנה:
name: 'gcr.io/cloud-builders/docker'
args: [ 'build', '-t', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/fe', '.' ]
העברת קונטיינר של Push API אל Artifact Registry
העברת הקונטיינר ל-Artifact Registry מאפשרת ל-Cloud Run לקבל את האימג' ולהציג אותו.
name: 'gcr.io/cloud-builders/docker'
args: ['push', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/fe']
החלפות
יוצרים משתנה עם ערך ברירת מחדל כדי שאפשר יהיה לשנות את הערכים האלה בזמן הפריסה.
substitutions:
_REGION: us-central1
_BASENAME: todo
./code/frontend/massage.sh
עריכת JavaScript
הפקודה הזו מזריקה את נקודת הקצה של תוכנת הביניים ל-JavaScript של ממשק הקצה.
API=$(gcloud run services describe todo-api --region=$1 --format="value(status.url)")
stripped=$(echo ${API/https:\/\//})
sed -i"" -e "s/127.0.0.1:9000/$stripped/" www/js/main.js
סיכום
עכשיו יש לכם אפליקציית רשימת מטלות פשוטה עם 3 רמות שפועלת ב-Cloud Run בפרויקט שלכם. יש לכם גם את כל הקוד כדי לשנות או להרחיב את הפתרון הזה כך שיתאים לסביבה שלכם.