A app de três camadas é uma simples aplicação de tarefas arquitetada como uma aplicação de três camadas simples:
- Back-end
- Base de dados – MySQL – Cloud SQL
- Colocar em cache – Redis – Cloud Memorystore
- Middleware/API
- API alojada em contentor – Golang – Cloud Run
- Front-end/IU
- IU alojada no contentor – Nginx + HTML/JS/CSS – Cloud Run
- Implementação
- Implementação contínua – Cloud Build
- Gestão de Secrets – Cloud Secret Manager
Começar
Clique no seguinte link para aceder a uma cópia do código fonte no Cloud Shell. Quando estiver lá, um único comando vai iniciar uma cópia funcional da aplicação no seu projeto.
Componentes de apps de três camadas
A arquitetura de apps de três camadas usa vários produtos. A lista seguinte apresenta os componentes, juntamente com mais informações sobre os componentes, incluindo links para vídeos relacionados, documentação do produto e visitas guiadas interativas.Scripts
O script de instalação usa um executável escrito em go
e ferramentas da CLI do Terraform para
usar um projeto vazio e instalar a aplicação no mesmo. A saída deve ser uma aplicação funcional e um URL para o endereço IP de equilíbrio de carga.
./main.tf
Ative os serviços
Os serviços Google Cloud estão desativados num projeto por predefinição. O ToDo requer que ative os seguintes serviços:
- Redes de serviços e acesso à VPC sem servidor: permite que o Cloud Run comunique com o SQL e o Redis na rede privada, mantendo estes servidores inacessíveis a chamadas externas provenientes da API.
- Cloud Build: cria imagens de contentores e implementa no Cloud Run
- Cloud Memorystore: fornece uma camada de cache para a aplicação.
- Cloud Run: a ferramenta sem servidor que vai alojar os contentores e fornecer URLs a partir dos quais aceder à aplicação.
- Cloud SQL: armazenamento de base de dados para a aplicação
- Cloud Storage: usado pelo Cloud Build e para carregar o esquema na base de dados
- Cloud Secret Manager: usado para injetar os IPs do anfitrião para SQL e Redis no Cloud Build para o Cloud Run.
- Artifact Registry: armazena as imagens Docker para utilização com o 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
}
Defina autorizações
O comando seguinte define as funções e as autorizações de IAM que permitem ao Cloud Build implementar serviços.
- Ative a conta de serviço do Cloud Build para implementar no Cloud Run
- Ative a conta de serviço do Cloud Build para definir o acesso VPN para o Cloud Run
- Ative a conta de serviço do Cloud Build para realizar atividades da conta de serviço
- Ative a conta de serviço do Cloud Build para agir em nome da conta de serviço do Compute
- Ative a conta de serviço do Cloud Build para publicar no Cloud Run
- Permita que a conta de serviço do Cloud Build consuma segredos
- Permita que a conta de serviço do Cloud Build armazene contentores no 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]
}
Crie uma rede para a instância do SQL
O seguinte comando permite que o Cloud SQL seja acessível a partir do 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]
}
Crie um conetor de acesso à VPC
Associa o Cloud Run à base de dados e ao armazenamento em 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]
}
Crie um servidor Redis
Configura e inicializa uma instância do servidor 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]
}
Crie um servidor SQL
O seguinte comando configura e inicializa uma instância do servidor SQL.
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}"
}
}
Crie um repositório do Artifact Registry
O comando seguinte armazena imagens do Docker para utilização com o 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]
}
Crie segredos
O comando seguinte armazena dados de anfitriões Redis e SQL em segredos da nuvem.
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]
}
Crie um artefacto para middleware
O comando seguinte cria a imagem do Docker e aloja-a no 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
]
}
Implemente o contentor da API no Cloud Run
O comando seguinte usa o Cloud Build para iniciar um serviço no Cloud Run através do contentor que acabou de criar.
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
]
}
Abra o serviço da API Cloud Run para que seja legível a nível mundial.
Esta camada de API da aplicação é chamada pelo navegador do utilizador, mas, por predefinição, os serviços do Cloud Run não são públicos. Para que os utilizadores possam utilizar este serviço, temos de abrir autorizações nestes serviços para que sejam acessíveis a todos.
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
}
Cria artefacto para a parte frontal
O comando seguinte cria a imagem do Docker e aloja-a no 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
]
}
Implemente o contentor de front-end no Cloud Run
O comando seguinte usa o Cloud Build para iniciar um serviço no Cloud Run através do contentor que acabámos de criar
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]
}
Abra o serviço de front-end do Cloud Run para ser legível a nível mundial
Este é o front-end da aplicação, que vai renderizar o HTML/JS/CSS com o qual o utilizador interage com a aplicação. Por predefinição, os serviços do Cloud Run não são públicos. Para que esta aplicação funcione, temos de abrir autorizações nestes serviços para serem acessíveis a todos.
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
Inicialize o esquema da base de dados
Este comando cria um contentor do Cloud Storage temporário para carregar o esquema para o 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
Crie um contentor de API
Este código cria uma imagem do Docker para a camada de software intermédio.
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']
Substituições
O código seguinte cria variáveis com valores predefinidos para que estes valores possam ser alterados no momento da implementação.
substitutions:
_REGION: us-central1
_BASENAME: todo
./code/frontend/clouldbuild.yaml
Conteúdo do código de massagem
O front-end é HTML/JS/CSS completamente estático. A app tem de apontar para o URL do serviço de API que acabámos de criar, mas aos serviços do Cloud Run é atribuído um URL com uma string aleatória. Este "script de processamento" captura esse URL aleatório e injeta-o no código do JS estático neste contentor.
name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: bash
args: [ './massage.sh', '$_REGION' ]
Crie um contentor de API
O código seguinte cria uma imagem do Docker para a camada intermédia:
name: 'gcr.io/cloud-builders/docker'
args: [ 'build', '-t', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/fe', '.' ]
Envie o contentor da API para o Artifact Registry
O envio do contentor para o Artifact Registry permite que o Cloud Run obtenha a imagem e a publique.
name: 'gcr.io/cloud-builders/docker'
args: ['push', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/fe']
Substituições
Crie uma variável com um valor predefinido para que estes valores possam ser alterados no momento da implementação.
substitutions:
_REGION: us-central1
_BASENAME: todo
./code/frontend/massage.sh
Edite JavaScript
Este comando injeta o ponto final do middleware no JavaScript da interface de utilizador.
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
Conclusão
Agora, tem uma aplicação de tarefas simples de 3 camadas em execução no Cloud Run no seu projeto. Também tem todo o código para modificar ou expandir esta solução de acordo com o seu ambiente.