3 階層アプリ

アーキテクチャ

3 階層アプリは、標準 3 層アプリケーションとして設計されたシンプルな ToDo アプリケーションです。

  • バックエンド
    • データベース - MySQL - Cloud SQL
    • キャッシュ - Redis - Cloud Memorystore
  • ミドルウェア / API
    • コンテナでホストされる API - Golang - Cloud Run
  • フロントエンド/UI
    • コンテナでホストされる UI - Nginx + HTML / JS / CSS - Cloud Run
  • デプロイ
    • 継続的デプロイ - Cloud Build
    • Secret 管理 - Cloud Secret Manager

使ってみる

Cloud Shell でソースコードのコピーへの次のリンクをクリックします。その後、1 つのコマンドでプロジェクト内のアプリケーションの作業コピーがスピンアップされます。

Cloud Shell で開く

GitHub でソースコードを見る


3 階層アプリのコンポーネント

3 階層アプリ アーキテクチャでは、いくつかのプロダクトを使用しています。 以下に、関連動画、プロダクト ドキュメント、インタラクティブ チュートリアルへのリンクを含めた、コンポーネントの詳細を示します。
動画 ドキュメント チュートリアル
Cloud SQL Cloud SQL は、アプリケーションのデータベース レイヤ用のマネージド SQL(MySQL、SQL Server、Postgres)を提供します。
Cloud Memorystore マネージド Redis である Cloud Memorystore は、アプリケーションのキャッシュ レイヤを提供します。
Cloud Run Cloud Run を使用すると、コンテナ内でアプリケーションを実行できますが、サーバーレスで、インスタンス、プロセッサ、メモリの数を構成する必要はありません。コンテナをアップロードし、URL を取得します。
Cloud Build Cloud Build は、コンテナをパッケージ化し、Cloud Run サービスとして利用できるようにデプロイするツールです。
Secret Manager Cloud Secret Manager は、ビルドプロセスに関するアプリケーションの機密情報を格納します。

スクリプト

インストール スクリプトでは、go と Terraform CLI ツールで記述された実行ファイルを使用して、空のプロジェクトを作成し、そこにアプリケーションをインストールします。出力は、機能するアプリケーションとロード バランシング IP アドレスの URL になります。

./main.tf

サービスを有効化する

Google Cloud サービスは、デフォルトではプロジェクトで無効になっています。ToDo では、次のサービスを有効にする必要があります。

  • サービス ネットワーキングとサーバーレス VPC アクセス - Cloud Run がプライベート ネットワークで SQL と Redis と通信できるようにし、API からの外部呼び出しによってこれらのサーバーにアクセスできないようにします。
  • Cloud Build - コンテナ イメージを作成して Cloud Run にデプロイします。
  • Cloud Memorystore - アプリケーション用のキャッシュ レイヤを提供します。
  • Cloud Run - コンテナをホストするサーバーレス ツールであり、アプリケーションにアクセスするための URL を提供します。
  • Cloud SQL - アプリケーション用のデータベース ストレージ
  • Cloud Storage - Cloud Build によって使用され、データベースにスキーマを読み込みます
  • Cloud Secret Manager - SQL と Redis のホスト IP を Cloud Run用の Cloud Build に挿入するために使用されます。
  • Artifact Registry - Cloud Build で使用する Docker イメージを保存します。
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
}

権限を設定

次のコマンドは、Cloud Build にサービスのデプロイを許可する IAM のロールと権限を設定します。

  • Cloud Build サービス アカウントを有効にして Cloud Run にデプロイする
  • Cloud Build サービス アカウントを有効にして Cloud Run の VPN アクセスを設定する
  • Cloud Build サービス アカウントを有効にしてサービス アカウント アクティビティを実行する
  • Cloud Build サービス アカウントを有効にして Compute サービス アカウントに代わって動作する
  • Cloud Build サービス アカウントを有効にして Cloud Run に公開する
  • Cloud Build サービス アカウントを有効にしてシークレットを使用する
  • Cloud Build サービス アカウントを有効にsite 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 Run から Cloud SQL にアクセスできるようになります。

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 Server を作成する

次のコマンドは、SQL Server インスタンスを構成して初期化します。

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 リポジトリを作成する

次のコマンドは、Cloud Run で使用するために Docker イメージを保存します。

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

Secret を作成する

次のコマンドは、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]
}

ミドルウェア用のアーティファクトを作成する

次のコマンドは、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 上でサービスをスピンアップします。

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 Service を公開読み取り可能にします。

こちらのアプリケーションの 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 SQL にアップロードするための一時的な Cloud Storage バケットを作成します。

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 です。アプリは、先ほど作成した API サービスの URL を参照する必要がありますが、Cloud Run サービスにはランダム文字列を含む URL が割り当てられます。「メッセージ スクリプト」は、そのランダム化された URL をキャプチャし、このコンテナ内の静的 JS のコードに挿入します。

./code/frontend/massage.sh

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

API コンテナを Artifact Registry に push する

コンテナを Artifact Registry に push すると、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

まとめ

これで、プロジェクトの Cloud Run で実行されているシンプルな 3 層の ToDo アプリケーションが完成しました。環境に合わせてこのソリューションを変更または拡張するためのコードもすべて用意されています。