L'app a tre livelli è una semplice applicazione di promemoria progettata come applicazione a 3 livelli Vanilla:
- Backend
- Database - MySQL - Cloud SQL
- Memorizzazione nella cache - Redis - Cloud Memorystore
- Middleware/API
- API ospitata in container - Golang - Cloud Run
- Front-end/UI
- UI ospitata in container - Nginx + HTML/JS/CSS - Cloud Run
- Deployment
- Deployment continuo - Cloud Build
- Gestione dei secret - Cloud Secret Manager
Inizia
Fai clic sul link seguente a una copia del codice sorgente in Cloud Shell. Da lì, un singolo comando avvierà una copia funzionante dell'applicazione nel tuo progetto.
Visualizza il codice sorgente su GitHub
Componenti dell'app a tre livelli
L'architettura dell'app a tre livelli utilizza diversi prodotti. Di seguito sono elencati i componenti, insieme a ulteriori informazioni sui componenti, tra cui link a video correlati, documentazione del prodotto e procedure dettagliate interattive.Script
Lo script di installazione utilizza un eseguibile scritto in go
e negli strumenti dell'interfaccia a riga di comando di Terraform per acquisire un progetto vuoto e installare l'applicazione al suo interno. L'output deve essere un'applicazione funzionante e un URL per l'indirizzo IP di bilanciamento del carico.
./main.tf
Abilitazione dei servizi
I servizi Google Cloud sono disabilitati in un progetto per impostazione predefinita. Da fare è necessario abilitare i seguenti servizi:
- Networking di servizi e accesso VPC serverless: consente a Cloud Run di comunicare con SQL e Redis sulla 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 ospita i container e fornisce gli 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 inserire gli IP dell'host per SQL e Redis in Cloud Build per Cloud Run.
- Artifact Registry: archivia 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
- Abilita l'account di servizio Cloud Build per eseguire attività dell'account di servizio
- Abilita l'account di servizio Cloud Build ad 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 consumare i secret
- Abilita l'account di servizio Cloud Build per archiviare i 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 networking per un'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 connettore di accesso VPC
Connette 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 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 repository Artifact Registry
Il comando seguente archivia le immagini Docker da utilizzare 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]
}
Crea secret
Il seguente comando archivia i dati host Redis e SQL in Cloud Secret.
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]
}
Crea artefatto per il middleware
Il seguente comando 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 container che hai 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 per la leggibilità in tutto il mondo.
Questo livello API dell'applicazione verrà chiamato dal browser dell'utente, ma per impostazione predefinita i servizi Cloud Run non sono pubblici. Per consentire agli utenti di utilizzare questo servizio, dobbiamo concedere le autorizzazioni necessarie a questi servizi affinché 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 il 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 di un 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 la leggibilità in tutto il mondo
Questo è il front-end dell'applicazione, che eseguirà il rendering del codice HTML/JS/CSS con cui l'utente interagisce con l'applicazione. Per impostazione predefinita i servizi Cloud Run non sono pubblici. Affinché questa applicazione funzioni, dobbiamo concedere le autorizzazioni per questi servizi affinché siano accessibili a tutti.
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)
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
Container API di 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
Contenuto codice massaggio
Il front-end è HTML/JS/CSS completamente statico. L'app deve puntare all'URL del servizio API appena creato, ma ai servizi Cloud Run viene assegnato un URL con una stringa casuale. Questo "script di massaggio" acquisisce l'URL randomizzato e lo inserisce nel codice del codice JS statico in questo container.
name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: bash
args: [ './massage.sh', '$_REGION' ]
Container API di build
Il codice seguente 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 ad Artifact Registry consente a Cloud Run di ottenere l'immagine e gestirla.
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 in una data/ora di 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 puoi usare una semplice applicazione di promemoria a tre livelli in esecuzione su Cloud Run nel tuo progetto. Inoltre, hai a disposizione tutto il codice per modificare o estendere questa soluzione in base all'ambiente.