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 contenitore - 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 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 su di essi, inclusi link a video correlati, documentazione del prodotto e walkthrough interattivi.Script
Lo script di installazione utilizza un file eseguibile scritto in go
e gli strumenti Terraform CLI per
prendere un progetto vuoto e installarvi l'applicazione. L'output deve essere un'applicazione funzionante 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 poter utilizzare ToDo, devi attivare i seguenti servizi:
- Service Networking e accesso VPC serverless: consente a Cloud Run di comunicare con SQL e Redis su una rete privata, mantenendo questi server inaccessibili dalle 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 e fornirà 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 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
- Consenti all'account di servizio Cloud Build di impostare l'accesso VPN per Cloud Run
- Consenti all'account di servizio Cloud Build di eseguire attività dell'account di servizio
- Consenti all'account di servizio Cloud Build di agire per conto dell'account di servizio Compute
- Attivare l'account di servizio Cloud Build per la pubblicazione su Cloud Run
- Consenti all'account di servizio Cloud Build di utilizzare i secret
- Consenti all'account di servizio Cloud Build di 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 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 archivia 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]
}
Creare un 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 dell'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
}
Creare un artefatto per il frontend
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 contenitore 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 in modo che sia leggibile da tutti
Si tratta del frontend dell'applicazione, che eseguirà il rendering di 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 aprire le autorizzazioni su questi servizi in modo che 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
Inizializzare lo 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
Crea un contenitore API
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. Questo "script di massaggio" acquisisce l'URL casuale e lo inserisce nel codice JS statico in questo contenitore.
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 contenitore dell'API in Artifact Registry
L'invio del contenitore ad Artifact Registry consente a Cloud Run di recuperare l'immagine e di 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
Modificare 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 liste di cose da fare a tre livelli in esecuzione su Cloud Run nel tuo progetto. Hai anche tutto il codice per modificare o estendere questa soluzione in base al tuo ambiente.