Aplicativo de três níveis

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Arquitetura

O Aplicativo de três níveis é um aplicativo de tarefas simples, arquitetado como um aplicativo de baunilha de três níveis:

  • Back-end
    • Banco de dados – MySQL – Cloud SQL
    • Armazenamento em cache - Redis - Cloud Memorystore
  • Middleware/API
    • API Container Container, Golang, Cloud Run
  • Front-end/IU
    • IU hospedada do contêiner - Nginx + HTML/JS/CSS – Cloud Run
  • Implantação
    • Implantação contínua – Cloud Build
    • Gerenciamento de secrets – Secret Manager do Cloud

Começar

Clique no link a seguir para copiar o código-fonte no Cloud Shell. Depois disso, um único comando ativará uma cópia de trabalho do aplicativo no projeto.

Abrir no Cloud Shell

Veja o código-fonte no GitHub


Componentes de apps de três níveis

A arquitetura dos apps de três níveis usa vários produtos. Veja a seguir os componentes, além de mais informações sobre eles, incluindo links para vídeos relacionados, documentação do produto e tutoriais interativos.
Vídeo Documentos Instruções
Cloud SQL O Cloud SQL é um SQL gerenciado fornecendo MySQL, SQL Server ou Postgres para a camada de banco de dados dos seus aplicativos.
Cloud Memorystore O Cloud Memorystore, Redis gerenciado, fornece a camada de armazenamento em cache para seus aplicativos.
Cloud Run O Cloud Run permite executar aplicativos em um contêiner, mas sem servidor, sem precisar configurar o número de instâncias, processadores ou memória. Faça upload de um contêiner e receba um URL.
Cloud Build O Cloud Build é a ferramenta que empacota os contêineres e os implanta para ficarem disponíveis como serviços do Cloud Run.
Secret Manager O Cloud Secret Manager armazena detalhes confidenciais sobre o aplicativo no processo de compilação.

Scripts

O script de instalação usa um executável escrito em go e as ferramentas da CLI do Terraform para pegar um projeto vazio e instalar o aplicativo nele. A saída precisa ser um aplicativo de trabalho e um URL para o endereço IP do balanceamento de carga.

./main.tf

Ativar serviços

Por padrão, os serviços do Google Cloud estão desativados em um projeto. A ToDo exige que você ative os seguintes serviços:

  • Rede de serviços e acesso VPC sem servidor: permite que o Cloud Run se comunique com o SQL e o Redis na rede privada, mantendo esses servidores inacessíveis a partir de chamadas externas provenientes da API.
  • Cloud Build: cria imagens de contêiner e implanta no Cloud Run.
  • Cloud Memorystore: oferece uma camada de armazenamento em cache para o aplicativo.
  • Cloud Run: a ferramenta sem servidor que hospeda os contêineres e fornece URLs para acessar o aplicativo.
  • Cloud SQL: armazenamento de banco de dados para o aplicativo
  • Cloud Storage: usado pelo Cloud Build e para carregar o esquema no banco de dados
  • Cloud Secret Manager: usado para injetar os IPs de host para SQL e Redis no Cloud Build para Cloud Run.
  • Artifact Registry: armazena as imagens do Docker para uso 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
}

Definir permissões

O comando a seguir define papéis e permissões do IAM que permitem que o Cloud Build implante serviços.

  • Ative a conta de serviço do Cloud Build para implantar 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
  • Permitir que a conta de serviço do Cloud Build atue em nome da conta de serviço do Compute
  • Permitir que a conta de serviço do Cloud Build publique no Cloud Run
  • Ative a conta de serviço do Cloud Build para consumir secrets
  • Ative a conta de serviço do Cloud Build para armazenar contêineres 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]
}

Criar rede para a instância do SQL

O comando a seguir permite que o Cloud SQL seja acessível pelo 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]
}

Criar conector de acesso à VPC

Conecta o Cloud Run ao banco 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]
}

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

Criar servidor SQL

O comando a seguir 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}"
    }
}

Criar repositório do Artifact Registry

O comando a seguir armazena imagens do Docker para uso 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]
}

Criar secrets

O comando a seguir armazena dados de host do Redis e do SQL no 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]
}

Criar artefato para middleware

O comando a seguir cria a imagem do Docker e a hospeda 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
  ]
}

Implantar o contêiner de API no Cloud Run

O comando a seguir usa o Cloud Build para ativar um serviço no Cloud Run usando o contêiner que você 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 ser legível para todos.

Essa camada de API do aplicativo será chamada pelo navegador do usuário, mas, por padrão, os serviços do Cloud Run não são públicos. Para que os usuários consumam esse serviço, precisamos abrir permissões neles para que possam ser acessadas pelo mundo todo.

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
}

Criar artefato para front-end

O comando a seguir cria a imagem do Docker e a hospeda 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
    ]
}

Implante um contêiner de front-end no Cloud Run

O próximo comando usa o Cloud Build para ativar um serviço no Cloud Run usando o contêiner que acabamos 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 por todos

Esse é o front-end do aplicativo, que renderiza o HTML/JS/CSS por meio do qual o usuário interage com o aplicativo. Por padrão, os serviços do Cloud Run não são públicos. Para que esse aplicativo funcione, é preciso abrir permissões nesses serviços para que possam ser acessadas pelo mundo.

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

Inicializar um esquema de banco de dados

Esse comando cria um bucket temporário do Cloud Storage para fazer upload do esquema no 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

Criar contêiner de API

Esse código cria uma imagem do Docker para a camada de 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']

Substituições

O código a seguir cria variáveis com valores padrão para que esses valores possam ser alterados no momento da implantação.

substitutions:
  _REGION: us-central1
  _BASENAME: todo

./code/frontend/clouldbuild.yaml

Conteúdo do código de massagem

O front-end é totalmente HTML/JS/CSS. O app precisa indicar o URL do serviço da API que acabamos de criar, mas os serviços do Cloud Run recebem um URL com uma string aleatória. Esse "script de massoterapia" captura esse URL aleatório e o injeta no código do JS estático nesse contêiner.

./code/frontend/massage.sh (link em inglês)

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

Criar contêiner de API

O código abaixo cria uma imagem do Docker para a camada de middleware:

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

Enviar o contêiner da API para o Artifact Registry

Enviar o contêiner para o Artifact Registry permite que o Cloud Run receba a imagem e a exiba.

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 padrão para que esses valores possam ser alterados um tempo de implantação.

substitutions:
  _REGION: us-central1
  _BASENAME: todo

./code/frontend/massage.sh

Editar JavaScript

Esse comando injeta o endpoint do middleware no JavaScript do 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

Conclusão

Agora você tem um aplicativo simples de tarefas de três níveis em execução no Cloud Run no seu projeto. Você também tem todo o código para modificar ou estender essa solução para se adequar ao seu ambiente.