Dreistufige App

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

Architektur

Three Tier App ist eine einfache To-do-Anwendung, die als Vanilla-3-Anwendung konzipiert wurde:

  • Back-End
    • Datenbank – MySQL – Cloud SQL
    • Caching – Redis – Cloud Memorystore
  • Middleware/API
    • Container gehostete API – Golang – Cloud Run
  • Front-End/UI
    • Gehostete Container-UI – Nginx + HTML/JS/CSS – Cloud Run
  • Bereitstellung
    • Kontinuierliche Bereitstellung – Cloud Build
    • Secret Management – Cloud Secret Manager

Jetzt starten

Klicken Sie auf den folgenden Link, um eine Kopie des Quellcodes in Cloud Shell zu erhalten. Dort wird mit einem einzigen Befehl eine funktionierende Kopie der Anwendung in Ihrem Projekt erstellt.

In Cloud Shell öffnen

Quellcode auf GitHub ansehen


App-Komponenten mit drei Ebenen

In der Architektur mit drei Ebenen werden mehrere Produkte verwendet. Im Folgenden sind die Komponenten und weitere Informationen zu den Komponenten aufgeführt, darunter Links zu ähnlichen Videos, Produktdokumentationen und interaktiven Schritt-für-Schritt-Anleitungen.
Video Docs Schritt-für-Schritt-Anleitungen
Cloud SQL Cloud SQL ist ein verwaltetes SQL, das MySQL, SQL Server oder Postgres für die Datenbankebene Ihrer Anwendungen bereitstellt.
Cloud Memorystore Cloud Memorystore, verwaltetes Redis, bietet die Caching-Ebene für Ihre Anwendungen.
Cloud Run Mit Cloud Run können Sie Anwendungen in einem Container, aber serverlos ausführen, ohne die Anzahl der Instanzen, Prozessoren oder des Arbeitsspeichers konfigurieren zu müssen. Laden Sie einen Container hoch und rufen Sie eine URL ab.
Cloud Build Cloud Build ist ein Tool, mit dem die Container verpackt und als Cloud Run-Dienste bereitgestellt werden.
Secret Manager Cloud Secret Manager speichert vertrauliche Informationen zur Anwendung für den Build-Prozess.

Skripts

Das Installationsskript verwendet eine ausführbare Datei, die in go und den Terraform-Befehlszeilentools geschrieben wurde, um ein leeres Projekt zu übernehmen und die Anwendung darin zu installieren. Die Ausgabe sollte eine funktionierende Anwendung und eine URL für die IP-Adresse des Load-Balancings sein.

./main.tf

Dienste aktivieren

Google Cloud-Dienste sind in einem Projekt standardmäßig deaktiviert. Für ToDo müssen Sie die folgenden Dienste aktivieren:

  • Dienstnetzwerke und serverlose VPC-Zugriffe: Ermöglicht Cloud Run die Kommunikation mit SQL und Redis im privaten Netzwerk, sodass diese Server von externen Aufrufen über die API nicht zugänglich sind.
  • Cloud Build: Erstellt Container-Images und stellt sie in Cloud Run bereit.
  • Cloud Memorystore – stellt eine Caching-Ebene für die Anwendung bereit.
  • Cloud Run: Das serverlose Tool, in dem die Container gehostet werden, und URLs, über die auf die Anwendung zugegriffen werden kann.
  • Cloud SQL – Datenbankspeicher für die Anwendung
  • Cloud Storage – wird von Cloud Build verwendet und das Schema in der Datenbank geladen
  • Cloud Secret Manager: Wird zum Einfügen der Host-IP-Adressen für SQL und Redis in Cloud Build for Cloud Run verwendet.
  • Artifact Registry – speichert die Docker-Images zur Verwendung mit 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
}

Berechtigungen festlegen

Mit dem folgenden Befehl werden IAM-Rollen und -Berechtigungen festgelegt, mit denen Cloud Build Dienste bereitstellen kann.

  • Cloud Build-Dienstkonto zum Bereitstellen in Cloud Run aktivieren
  • Cloud Build-Dienstkonto aktivieren, um den VPN-Zugriff für Cloud Run festzulegen
  • Cloud Build-Dienstkonto aktivieren, um Dienstkontoaktivitäten auszuführen
  • Cloud Build-Dienstkonto aktivieren, um im Namen des Compute-Dienstkontos zu handeln
  • Cloud Build-Dienstkonto zum Veröffentlichen in Cloud Run aktivieren
  • Cloud Build-Dienstkonto aktivieren, um Secrets zu nutzen
  • Cloud Build-Dienstkonto aktivieren, um Container in Artifact Registry zu speichern
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]
}

Netzwerk für SQL-Instanz erstellen

Mit dem folgenden Befehl kann auf Cloud SQL über Cloud Run zugegriffen werden:

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-Zugriffsconnector erstellen

Verbindet Cloud Run mit Datenbank und Caching

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-Server erstellen

Konfiguriert und initialisiert eine Redis-Serverinstanz.

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 erstellen

Mit dem folgenden Befehl wird eine SQL Server-Instanz konfiguriert und initialisiert.

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-Repository erstellen

Mit dem folgenden Befehl werden Docker-Images zur Verwendung mit Cloud Run gespeichert.

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

Secrets erstellen

Mit dem folgenden Befehl werden Redis- und SQL-Hostdaten in Cloud Secrets gespeichert.

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

Artefakt für Middleware erstellen

Der folgende Befehl erstellt das Docker-Image und hostet es in 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-Container in Cloud Run bereitstellen

Im folgenden Befehl wird Cloud Build verwendet, um einen Dienst in Cloud Run mit dem soeben erstellten Container einzurichten.

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

Öffnen Sie den Cloud Run API-Dienst, um ihn weltweit lesbar zu machen.

Diese API-Ebene der Anwendung wird vom Browser des Nutzers aufgerufen, aber Cloud Run-Dienste sind standardmäßig nicht öffentlich. Damit Nutzer diesen Dienst nutzen können, müssen wir Berechtigungen für diese Dienste öffnen, damit sie weltweit zugänglich sind.

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
}

Artefakt für Front-End erstellen

Der folgende Befehl erstellt das Docker-Image und hostet es in 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
    ]
}

Front-End-Container in Cloud Run bereitstellen

Der nächste Befehl verwendet Cloud Build, um einen Dienst in Cloud Run mithilfe des soeben erstellten Containers einzurichten

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-Front-End-Dienst für Weltlese öffnen

Dies ist das Front-End der Anwendung, das das HTML/JS/CSS rendert, über das der Nutzer mit der Anwendung interagiert. Standardmäßig sind Cloud Run-Dienste nicht öffentlich. Damit diese Anwendung funktioniert, müssen wir Berechtigungen für diese Dienste öffnen, damit sie weltweit zugänglich sind.

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

Datenbankschema initialisieren

Mit diesem Befehl wird ein temporärer Cloud Storage-Bucket erstellt, um das Schema in Cloud SQL hochzuladen.

PROJECT=$1
SQLNAME=$2

SQLSERVICEACCOUNT=$(gcloud sql instances describe $SQLNAME --format="value(serviceAccountEmailAddress)" | xargs)
gsutil mb gs://$PROJECT-temp
gsutil cp schema.sql gs://$PROJECT-temp/schema.sql
gsutil iam ch serviceAccount:$SQLSERVICEACCOUNT:objectViewer gs://$PROJECT-temp/
gcloud sql import sql $SQLNAME gs://$PROJECT-temp/schema.sql -q
gsutil rm gs://$PROJECT-temp/schema.sql
gsutil rb gs://$PROJECT-temp

./code/middleware/clouldbuild.yaml

API-Container erstellen

Dieser Code erstellt ein Docker-Image für die Middleware-Ebene.

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

Ersetzungen

Mit dem folgenden Code werden Variablen mit Standardwerten erstellt, die bei der Bereitstellung geändert werden können.

substitutions:
  _REGION: us-central1
  _BASENAME: todo

./code/frontend/clouldbuild.yaml

Inhalt des Massagecodes

Das Front-End ist komplett statisches HTML/JS/CSS. Die Anwendung muss auf die URL des soeben erstellten API-Dienstes verweisen. Cloud Run-Diensten wird jedoch eine URL mit einem zufälligen String zugewiesen. Dieses „Massageskript“ erfasst diese zufällige URL und fügt sie in den Code des statischen JavaScript-Codes in diesem Container ein.

./code/frontend/massage.sh

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

API-Container erstellen

Mit dem folgenden Code wird ein Docker-Image für die Middleware-Ebene erstellt:

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

API-Container per Push an Artifact Registry übertragen

Wenn Sie den Container per Push an Artifact Registry übertragen, kann Cloud Run das Image abrufen und bereitstellen.

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

Ersetzungen

Erstellen Sie eine Variable mit einem Standardwert, damit diese Werte bei der Bereitstellung geändert werden können.

substitutions:
  _REGION: us-central1
  _BASENAME: todo

./code/frontend/massage.sh

JavaScript bearbeiten

Mit diesem Befehl wird der Endpunkt für die Middleware in den JavaScript-Code des Front-Ends eingefügt.

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

Fazit

Sie haben jetzt eine einfache To-do-Anwendung mit 3 Ebenen, die in Cloud Run in Ihrem Projekt ausgeführt wird. Außerdem haben Sie den gesamten Code, um diese Lösung an Ihre Umgebung anzupassen oder zu erweitern.