App a tre livelli

Architettura

Three Tier App è una semplice applicazione per la gestione delle attività progettata come applicazione a tre livelli standard:

  • Backend
    • Database - MySQL - Cloud SQL
    • Memorizzazione nella cache - Redis - Cloud Memorystore
  • Middleware/API
    • API ospitata in un container - Golang - Cloud Run
  • Front-end/UI
    • Interfaccia utente ospitata in un contenitore - Nginx + HTML/JS/CSS - Cloud Run
  • Deployment
    • Deployment continuo - Cloud Build
    • Gestione dei secret - Cloud Secret Manager

Inizia

Fai clic sul seguente link per una copia del codice sorgente in Cloud Shell. Una volta un singolo comando avvierà una copia funzionante dell'applicazione progetto...

Apri in Cloud Shell

Visualizza il codice sorgente su GitHub


Componenti dell'app a tre livelli

L'architettura delle app a tre livelli utilizza diversi prodotti. Di seguito sono elencati i componenti, insieme a ulteriori informazioni su di essi, inclusi link a video correlati, documentazione del prodotto e walkthrough interattivi.
Video Documenti Procedure dettagliate
Cloud SQL Cloud SQL è un servizio SQL gestito che fornisce MySQL, SQL Server o Postgres per il livello di database delle tue applicazioni.
Cloud Memorystore Cloud Memorystore, Redis gestito, fornisce il livello di memorizzazione nella cache per le tue applicazioni.
Cloud Run Cloud Run ti consente di eseguire applicazioni in un contenitore, ma in modo serverless, senza dover configurare il numero di istanze, processori o memoria. Carica un contenitore, ricevi un URL.
Cloud Build Cloud Build è lo strumento che pacchettizza i container e ne esegue il deployment in modo che siano disponibili come servizi Cloud Run.
Secret Manager Cloud Secret Manager archivia dettagli sensibili sull'applicazione per il processo di compilazione.

Script

Lo script di installazione usa un eseguibile scritto negli strumenti dell'interfaccia a riga di comando go e Terraform per installare l'applicazione al suo interno in un progetto vuoto. L'output dovrebbe essere un e un URL per l'indirizzo IP del bilanciamento del carico.

./main.tf

Attiva i servizi

I servizi Google Cloud sono disabilitati in un progetto per impostazione predefinita. Per utilizzare ToDo, devi attivare i seguenti servizi:

  • Networking di servizi e Accesso VPC serverless: consente a Cloud Run di Comunica con SQL e Redis su una rete privata mantenendo questi server inaccessibili da chiamate esterne provenienti dall'API.
  • Cloud Build: crea immagini container ed esegue il deployment in Cloud Run
  • Cloud Memorystore: fornisce un livello di memorizzazione nella cache per l'applicazione.
  • Cloud Run: lo strumento serverless che ospiterà i container. fornire URL da cui accedere all'applicazione.
  • Cloud SQL: spazio di archiviazione del database per l'applicazione
  • Cloud Storage: utilizzato da Cloud Build e per caricare lo schema nel database
  • Cloud Secret Manager: utilizzato per iniettare gli IP host per SQL e Redis in Cloud Build per Cloud Run.
  • Artifact Registry: memorizza le immagini Docker da utilizzare con 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
}

Imposta autorizzazioni

Il seguente comando imposta i ruoli e le autorizzazioni IAM che consentono a Cloud Build di eseguire il deployment dei servizi.

  • Abilita l'account di servizio Cloud Build per eseguire il deployment in Cloud Run
  • Abilita l'account di servizio Cloud Build per impostare l'accesso VPN per Cloud Run
  • Consenti all'account di servizio Cloud Build di eseguire attività dell'account di servizio
  • Abilitare l'account di servizio Cloud Build per agire per conto dell'account di servizio Compute
  • Abilita l'account di servizio Cloud Build per pubblicare in Cloud Run
  • Abilita l'account di servizio Cloud Build per utilizzare i secret
  • Abilita l'account di servizio Cloud Build per archiviare container in 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]
}

Crea la rete per l'istanza SQL

Il seguente comando consente di accedere a Cloud SQL da 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]
}

Crea un connettore di accesso VPC

Collega Cloud Run al database e alla memorizzazione nella 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]
}

Crea il server Redis

Configura e inizializza un'istanza del 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]
}

Crea SQL Server

Il seguente comando configura e inizializza un'istanza 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}"
    }
}

Crea un repository Artifact Registry

Il seguente comando memorizza le immagini Docker per l'utilizzo con 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]
}

Creare secret

Il seguente comando memorizza i dati degli host Redis e SQL in 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]
}

Creazione artefatto per middleware

Il comando seguente crea l'immagine Docker e la ospita su 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
  ]
}

Esegui il deployment del container API in Cloud Run

Il seguente comando utilizza Cloud Build per avviare un servizio su Cloud Run utilizzando il contenitore appena creato.

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

Apri il servizio API Cloud Run in modo che sia leggibile da tutti.

Questo livello API dell'applicazione verrà chiamato dal browser dell'utente, ma per impostazione predefinita i servizi Cloud Run non sono pubblici. Affinché gli utenti possano usufruire di questo servizio, dobbiamo aprire le autorizzazioni su questi servizi in modo che siano accessibili a tutti.

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
}

Crea artefatto per front-end

Il seguente comando crea l'immagine Docker e la ospita su 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
    ]
}

Esegui il deployment del container frontend in Cloud Run

Il comando successivo utilizza Cloud Build per avviare un servizio su Cloud Run utilizzando il container che abbiamo appena creato

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

Apri il servizio frontend Cloud Run per essere leggibile da tutto il mondo

Questo è il front-end dell'applicazione, che eseguirà il rendering dei file HTML/JS/CSS. tramite il quale l'utente interagisce con l'applicazione: per impostazione predefinita, i servizi Cloud Run non sono pubblici. Per far funzionare questa applicazione dobbiamo aprire affinché tutti possano accedere a questi servizi.

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

Inizializza schema del database

Questo comando crea un bucket Cloud Storage temporaneo per caricare lo schema in 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

Container API Build

Questo codice crea un'immagine Docker per il livello 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']  

Sostituzioni

Il codice seguente crea variabili con valori predefiniti in modo che questi valori possano essere modificati al momento del deployment.

substitutions:
  _REGION: us-central1
  _BASENAME: todo

./code/frontend/clouldbuild.yaml

Massaggiare i contenuti del codice

Il front-end è completamente statico HTML/JS/CSS. L'app deve puntare all'URL del servizio API che abbiamo appena creato, ma ai servizi Cloud Run viene assegnato un URL con una stringa randomizzata. "Testo per il massaggio" acquisisce l'URL casuale e lo inserisce nel codice del codice JS statico in questo container.

./code/frontend/massage.sh

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

Crea un contenitore API

Il seguente codice crea un'immagine Docker per il livello middleware:

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

Esegui il push del container API in Artifact Registry

Il push del container in Artifact Registry consente a Cloud Run di ottenere l'immagine e pubblicarla.

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

Sostituzioni

Crea una variabile con un valore predefinito in modo che questi valori possano essere modificati al momento del deployment.

substitutions:
  _REGION: us-central1
  _BASENAME: todo

./code/frontend/massage.sh

Modifica JavaScript

Questo comando inserisce l'endpoint per il middleware nel codice JavaScript del front-end.

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

Conclusione

Ora hai una semplice applicazione di promemoria a 3 livelli in esecuzione su Cloud Esegui nel progetto. Hai anche tutto il codice per modificare o estendere questa soluzione in base al tuo ambiente.