Aplikasi Tiga Tingkat

+

Arsitektur

Aplikasi Tiga Tingkat adalah aplikasi daftar tugas sederhana yang dirancang sebagai aplikasi 3 tingkat vanilla:

  • Backend
    • Database - MySQL - Cloud SQL
    • Penyimpanan dalam Cache - Redis - Cloud Memorystore
  • Middleware/API
    • API yang dihosting container - Golang - Cloud Run
  • Front End/UI
    • UI yang dihosting container - Nginx + HTML/JS/CSS - Cloud Run
  • Deployment
    • Deployment Berkelanjutan - Cloud Build
    • Pengelolaan Secret - Cloud Secret Manager

Mulai

Klik link berikut untuk melihat salinan kode sumber di Cloud Shell. Setelah di sana, satu perintah akan membuat salinan aplikasi yang berfungsi di project Anda.

Buka di Cloud Shell

Melihat kode sumber di GitHub


Komponen Aplikasi Tiga Tingkat

Arsitektur Aplikasi Tiga Tingkat menggunakan beberapa produk. Berikut adalah daftar komponen, beserta informasi selengkapnya tentang komponen tersebut, termasuk link ke video terkait, dokumentasi produk, dan panduan interaktif.
Video Dokumen Panduan
Cloud SQL Cloud SQL adalah SQL terkelola yang menyediakan MySQL, SQL Server, atau Postgres untuk lapisan database aplikasi Anda.
Cloud Memorystore Cloud Memorystore, Redis terkelola, menyediakan lapisan penyimpanan data ke dalam cache untuk aplikasi Anda.
Cloud Run Cloud Run memungkinkan Anda menjalankan aplikasi dalam container, tetapi dengan cara serverless, tanpa harus mengonfigurasi jumlah instance, prosesor, atau memori. Mengupload penampung, mendapatkan URL.
Cloud Build Cloud Build adalah alat yang memaketkan container dan men-deploynya agar tersedia sebagai layanan Cloud Run.
Secret Manager Cloud Secret Manager menyimpan detail sensitif tentang aplikasi untuk proses build.

Skrip

Skrip penginstalan menggunakan file yang dapat dieksekusi yang ditulis di go dan alat Terraform CLI untuk mengambil project kosong dan menginstal aplikasi di dalamnya. Output-nya harus berupa aplikasi yang berfungsi dan URL untuk alamat IP load balancing.

./main.tf

Mengaktifkan layanan

Layanan Google Cloud dinonaktifkan di project secara default. ToDo mengharuskan Anda mengaktifkan layanan berikut:

  • Service Networking & Serverless VPC Access - memungkinkan Cloud Run untuk Berkomunikasi dengan SQL dan Redis di jaringan pribadi, sehingga server ini tidak dapat diakses dari panggilan luar yang berasal dari API.
  • Cloud Build - membuat image container dan men-deploy ke Cloud Run
  • Cloud Memorystore - menyediakan lapisan penyimpanan cache untuk aplikasi.
  • Cloud Run - alat serverless yang akan menghosting container, dan menyediakan URL untuk mengakses aplikasi.
  • Cloud SQL - penyimpanan database untuk aplikasi
  • Cloud Storage - digunakan oleh Cloud Build, dan untuk memuat skema di database
  • Cloud Secret Manager - digunakan untuk memasukkan IP host untuk SQL dan Redis ke Cloud Build untuk Cloud Run.
  • Artifact Registry - menyimpan image Docker untuk digunakan dengan 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
}

Menetapkan izin

Perintah berikut menetapkan Peran dan Izin IAM yang memungkinkan Cloud Build men-deploy layanan.

  • Mengaktifkan Akun Layanan Cloud Build untuk men-deploy ke Cloud Run
  • Mengaktifkan Akun Layanan Cloud Build untuk menetapkan Akses VPN untuk Cloud Run
  • Mengaktifkan Akun Layanan Cloud Build untuk melakukan aktivitas Akun Layanan
  • Mengaktifkan Akun Layanan Cloud Build untuk bertindak atas nama Akun Layanan Compute
  • Mengaktifkan Akun Layanan Cloud Build untuk memublikasikan ke Cloud Run
  • Mengaktifkan Akun Layanan Cloud Build untuk menggunakan secret
  • Mengaktifkan Akun Layanan Cloud Build untuk menyimpan penampung di 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]
}

Membuat jaringan untuk instance SQL

Perintah berikut memungkinkan Cloud SQL diakses dari 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]
}

Membuat konektor akses VPC

Menghubungkan Cloud Run ke Database dan Cache

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]
}

Membuat Server Redis

Mengonfigurasi dan menginisialisasi instance server 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]
}

Membuat server SQL

Perintah berikut mengonfigurasi dan menginisialisasi instance server 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}"
    }
}

Membuat repositori Artifact Registry

Perintah berikut menyimpan Image Docker untuk digunakan dengan 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]
}

Membuat secret

Perintah berikut menyimpan data host Redis dan SQL di 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]
}

Membuat artefak untuk middleware

Perintah berikut membuat image Docker dan menghostingnya di 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
  ]
}

Men-deploy container API ke Cloud Run

Perintah berikut menggunakan Cloud Build untuk meluncurkan layanan di Cloud Run menggunakan container yang baru saja Anda build.

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
    ]
} 

Buka Layanan Cloud Run API agar dapat dibaca oleh semua orang.

Lapisan API aplikasi ini akan dipanggil oleh browser pengguna, tetapi secara default, layanan Cloud Run tidak bersifat publik. Agar pengguna dapat menggunakan layanan ini, kami harus membuka izin pada layanan ini agar dapat diakses oleh semua orang.

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
}

Membuat artefak untuk frontend

Perintah berikut membuat image Docker dan menghostingnya di 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
    ]
}

Men-deploy container frontend ke Cloud Run

Perintah berikutnya menggunakan Cloud Build untuk meluncurkan layanan di Cloud Run menggunakan container yang baru saja kita build

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]
}

Membuka layanan frontend Cloud Run agar dapat dibaca oleh semua orang

Ini adalah frontend aplikasi, yang akan merender HTML/JS/CSS yang digunakan pengguna untuk berinteraksi dengan aplikasi - secara default, layanan Cloud Run tidak bersifat publik. Agar aplikasi ini berfungsi, kita harus membuka izin pada layanan ini agar dapat diakses oleh semua orang.

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

Melakukan inisialisasi skema database

Perintah ini membuat bucket Cloud Storage sementara untuk mengupload skema ke 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

Membuat penampung API

Kode ini membuat image Docker untuk lapisan middleware.

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']  

Substitusi

Kode berikut membuat variabel dengan nilai default sehingga nilai ini dapat diubah pada waktu deployment.

substitutions:
  _REGION: us-central1
  _BASENAME: todo

./code/frontend/clouldbuild.yaml

Konten kode pijat

Front end sepenuhnya berupa HTML/JS/CSS statis. Aplikasi harus mengarah ke URL untuk layanan API yang baru saja kita buat, tetapi layanan Cloud Run diberi URL dengan string acak. 'Skrip massage' ini mengambil URL acak tersebut dan memasukkannya ke dalam kode JS statis di penampung ini.

./code/frontend/massage.sh

name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: bash
args: [ './massage.sh', '$_REGION' ]

Membuat penampung API

Kode berikut membuat image Docker untuk lapisan middleware:

name: 'gcr.io/cloud-builders/docker'
args: [ 'build', '-t', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/fe', '.' ]

Mengirim container API ke Artifact Registry

Mengirim container ke Artifact Registry memungkinkan Cloud Run mendapatkan image dan menayangkannya.

name: 'gcr.io/cloud-builders/docker'
args: ['push', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/fe']

Substitusi

Buat variabel dengan default sehingga nilai ini dapat diubah pada waktu deployment.

substitutions:
  _REGION: us-central1
  _BASENAME: todo

./code/frontend/massage.sh

Mengedit JavaScript

Perintah ini memasukkan endpoint untuk Middleware ke dalam JavaScript frontend.

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

Kesimpulan

Sekarang Anda memiliki aplikasi daftar tugas 3 tingkat sederhana yang berjalan di Cloud Run dalam project Anda. Anda juga memiliki semua kode untuk mengubah atau memperluas solusi ini agar sesuai dengan lingkungan Anda.