Implantar no Compute Engine


Este guia explica como realizar implantações azul-verde sem tempo de inatividade em grupos de instâncias gerenciadas (MIGs) do Compute Engine usando o Cloud Build e o Terraform.

O Cloud Build permite automatizar vários processos de desenvolvedor, incluindo a criação e a implantação de aplicativos em vários ambientes de execução Google Cloud , como o Compute Engine, o Google Kubernetes Engine, o GKE Enterprise e as funções do Cloud Run.

Os MIGs do Compute Engine permitem operar aplicativos em várias máquinas virtuais (VMs) idênticas. É possível tornar as cargas de trabalho escalonáveis e altamente disponíveis aproveitando serviços de MIG automatizados, como escalonamento automático, recuperação automática, implantação regional (várias zonas) e atualização automática. Usando o modelo de implantação contínua azul-verde, você vai aprender a transferir gradualmente o tráfego de usuários de um MIG (azul) para outro (verde), ambos em execução na produção.

Visão geral do design

O diagrama a seguir mostra o modelo de implantação azul/verde usado pelo exemplo de código descrito neste documento:

Modelo azul-verde

Em um nível alto, esse modelo inclui os seguintes componentes:

  • Dois pools de VMs do Compute Engine: azul e verde.
  • Três balanceadores de carga HTTP(S) externos:
    • Um balanceador de carga azul/verde, que encaminha o tráfego dos usuários finais para o pool azul ou verde de instâncias de VM.
    • Um balanceador de carga Blue que roteia o tráfego de engenheiros de QA e desenvolvedores para o pool de instâncias de VM Blue.
    • Um balanceador de carga verde que encaminha o tráfego de engenheiros de QA e desenvolvedores para o pool de instâncias verdes.
  • Dois conjuntos de usuários:
    • Usuários finais que têm acesso ao balanceador de carga azul-verde, que os direciona ao pool de instâncias azul ou verde.
    • Engenheiros de controle de qualidade e desenvolvedores que precisam de acesso a ambos os conjuntos de pools para desenvolvimento e testes. Eles podem acessar os balanceadores de carga azul e verde, que os encaminham para o pool de instâncias azul e verde, respectivamente.

Os pools de VMs azuis e verdes são implementados como MIGs do Compute Engine, e endereços IP externos são roteados para as VMs no MIG usando balanceadores de carga HTTP(S) externos. O exemplo de código descrito neste documento usa o Terraform para configurar essa infraestrutura.

O diagrama a seguir ilustra as operações do desenvolvedor que ocorrem na implantação:

Fluxo de operações do desenvolvedor

No diagrama acima, as setas vermelhas representam o fluxo de inicialização que ocorre quando você configura a infraestrutura de implantação pela primeira vez, e as setas azuis representam o fluxo do GitOps que ocorre durante cada implantação.

Para configurar essa infraestrutura, execute um script de configuração que inicia o processo de inicialização e configura os componentes para o fluxo do GitOps.

O script de configuração executa um pipeline do Cloud Build que realiza as seguintes operações:

  • Cria um repositório no Cloud Source Repositories chamado copy-of-gcp-mig-simple e copia o código-fonte do repositório de exemplo do GitHub para o repositório no Cloud Source Repositories.
  • Cria dois gatilhos do Cloud Build chamados apply e destroy.

O acionador apply é anexado a um arquivo do Terraform chamado main.tfvars nos repositórios de origem do Cloud. Esse arquivo contém as variáveis do Terraform que representam os balanceadores de carga azul e verde.

Para configurar a implantação, atualize as variáveis no arquivo main.tfvars. O gatilho apply executa um pipeline do Cloud Build que executa tf_apply e realiza as seguintes operações:

  • Cria dois MIGs do Compute Engine (um para verde e outro para azul), quatro instâncias de VM do Compute Engine (duas para o MIG verde e duas para o MIG azul), os três balanceadores de carga (azul, verde e o divisor) e três endereços IP públicos.
  • Mostra os endereços IP que podem ser usados para conferir os aplicativos implantados nas instâncias azul e verde.

O acionador de destruição é acionado manualmente para excluir todos os recursos criados pelo acionador de aplicação.

Objetivos

  • Use o Cloud Build e o Terraform para configurar balanceadores de carga HTTP(S) externos com back-ends de grupo de instâncias de VM do Compute Engine.

  • Realize implantações azul-verde nas instâncias de VM.

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Saiba mais em Limpeza.

Antes de começar

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.
  3. To initialize the gcloud CLI, run the following command:

    gcloud init
  4. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Install the Google Cloud CLI.
  7. To initialize the gcloud CLI, run the following command:

    gcloud init
  8. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  9. Make sure that billing is enabled for your Google Cloud project.

Testar

  1. Execute o script de configuração do repositório de exemplo de código do Google:

    bash <(curl https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-build-samples/main/mig-blue-green/setup.sh)
    
  2. Quando o script de configuração pedir o consentimento do usuário, digite sim.

    O script termina de ser executado em alguns segundos.

  3. No console do Google Cloud, abra a página Histórico de build do Cloud Build:

    Abrir a página "Histórico de criações"

  4. Clique no build mais recente.

    A página Detalhes do build aparece, mostrando um pipeline do Cloud Build com três etapas de build: a primeira cria um repositório no Cloud Source Repositories, a segunda clona o conteúdo do repositório de exemplo no GitHub para o Cloud Source Repositories, e a terceira adiciona dois gatilhos de build.

  5. Abra o Cloud Source Repositories:

    Abrir o Cloud Source Repositories

  6. Na lista de repositórios, clique em copy-of-gcp-mig-simple.

    Na guia History, na parte de baixo da página, você vai encontrar um commit com a descrição A copy of https://github.com/GoogleCloudPlatform/cloud-build-samples.git feito pelo Cloud Build para criar um repositório chamado copy-of-gcp-mig-simple.

  7. Abra a página Gatilhos do Cloud Build:

    Abrir a página "Gatilhos"

  8. Você vai encontrar dois gatilhos de build chamados apply e destroy. O acionador apply está anexado ao arquivo infra/main.tfvars na ramificação main. Esse acionador é executado sempre que o arquivo é atualizado. O acionador destroy é manual.

  9. Para iniciar o processo de implantação, atualize o arquivo infra/main.tfvars:

    1. Na janela do terminal, crie e navegue até uma pasta chamada deploy-compute-engine:

      mkdir ~/deploy-compute-engine
      cd ~/deploy-compute-engine
      
    2. Clone o repositório copy-of-gcp-mig-simple:

      gcloud source repos clone copy-of-mig-blue-green
      
    3. Navegue até o diretório clonado:

      cd ./copy-of-mig-blue-green
      
    4. Atualize infra/main.tfvars para substituir o azul por verde:

      sed -i'' -e 's/blue/green/g' infra/main.tfvars
      
    5. Adicione o arquivo atualizado:

      git add .
      
    6. Faça a confirmação do arquivo:

      git commit -m "Promote green"
      
    7. Envie o arquivo:

      git push
      

      Fazer alterações em infra/main.tfvars aciona a execução do gatilho apply, que inicia a implantação.

  10. Abra o Cloud Source Repositories:

    Abrir o Cloud Source Repositories

  11. Na lista de repositórios, clique em copy-of-gcp-mig-simple.

    O commit com a descrição Promote green vai aparecer na guia History, na parte de baixo da página.

  12. Para conferir a execução do gatilho apply, abra a página Histórico da build no console do Google Cloud:

    Abrir a página "Histórico de criações"

  13. Clique no primeiro build para abrir a página Detalhes do build.

    Você vai encontrar o pipeline de gatilho apply com duas etapas de build. A primeira etapa de build executa o Terraform apply para criar o Compute Engine e os recursos de balanceamento de carga para a implantação. A segunda etapa de build imprime o endereço IP em que o aplicativo está em execução.

  14. Abra o endereço IP correspondente ao MIG verde em um navegador. Você vai ver uma captura de tela semelhante a esta mostrando a implantação:

    Implantação

  15. Acesse a página Grupo de instâncias do Compute Engine para conferir os grupos de instâncias azul e verde:

    Abrir a página "Grupo de instâncias"

  16. Abra a página Instâncias de VM para conferir as quatro instâncias de VM:

    Abrir a página "Instâncias de VM"

  17. Abra a página Endereços IP externos para conferir os três balanceadores de carga:

    Abra a página "Endereços IP externos"

Noções básicas sobre o código

O código-fonte para este exemplo inclui:

  • Código-fonte relacionado ao script de configuração.
  • O código-fonte relacionado aos pipelines do Cloud Build.
  • Código-fonte relacionado aos modelos do Terraform.

Script de configuração

setup.sh é o script de configuração que executa o processo de inicialização e cria os componentes para a implantação azul-verde. O script executa as seguintes operações:

  • Ativa as APIs Cloud Build, Resource Manager, Compute Engine e Cloud Source Repositories.
  • Concede o papel roles/editor do IAM à conta de serviço do Cloud Build no seu projeto. Esse papel é necessário para que o Cloud Build crie e configure os componentes necessários do GitOps para a implantação.
  • Concede o papel roles/source.admin do IAM à conta de serviço do Cloud Build no seu projeto. Essa função é necessária para que a conta de serviço do Cloud Build crie os repositórios do Cloud Source no seu projeto e clone o conteúdo do repositório do GitHub de exemplo para os repositórios do Cloud Source.
  • Gera um pipeline do Cloud Build chamado bootstrap.cloudbuild.yaml inline, que:

    • Cria um novo repositório no Cloud Source Repositories.
    • Copia o código-fonte do repositório do GitHub de exemplo para o novo repositório no Cloud Source Repositories.
    • Cria os gatilhos de aplicação e destruição de build.
set -e

BLUE='\033[1;34m'
RED='\033[1;31m'
GREEN='\033[1;32m'
NC='\033[0m'

echo -e "\n${GREEN}######################################################"
echo -e "#                                                    #"
echo -e "#  Zero-Downtime Blue/Green VM Deployments Using     #"
echo -e "#  Managed Instance Groups, Cloud Build & Terraform  #"
echo -e "#                                                    #"
echo -e "######################################################${NC}\n"

echo -e "\nSTARTED ${GREEN}setup.sh:${NC}"

echo -e "\nIt's ${RED}safe to re-run${NC} this script to ${RED}recreate${NC} all resources.\n"
echo "> Checking GCP CLI tool is installed"
gcloud --version > /dev/null 2>&1

readonly EXPLICIT_PROJECT_ID="$1"
readonly EXPLICIT_CONSENT="$2"

if [ -z "$EXPLICIT_PROJECT_ID" ]; then
    echo "> No explicit project id provided, trying to infer"
    PROJECT_ID="$(gcloud config get-value project)"
else
    PROJECT_ID="$EXPLICIT_PROJECT_ID"
fi

if [ -z "$PROJECT_ID" ]; then
    echo "ERROR: GCP project id was not provided as parameter and could not be inferred"
    exit 1
else
    readonly PROJECT_NUM="$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')"
    if [ -z "$PROJECT_NUM" ]; then
        echo "ERROR: GCP project number could not be determined"
        exit 1
    fi
    echo -e "\nYou are about to:"
    echo -e "  * modify project ${RED}${PROJECT_ID}/${PROJECT_NUM}${NC}"
    echo -e "  * ${RED}enable${NC} various GCP APIs"
    echo -e "  * make Cloud Build ${RED}editor${NC} of your project"
    echo -e "  * ${RED}execute${NC} Cloud Builds and Terraform plans to create"
    echo -e "  * ${RED}4 VMs${NC}, ${RED}3 load balancers${NC}, ${RED}3 public IP addresses${NC}"
    echo -e "  * incur ${RED}charges${NC} in your billing account as a result\n"
fi

if [ "$EXPLICIT_CONSENT" == "yes" ]; then
  echo "Proceeding under explicit consent"
  readonly CONSENT="$EXPLICIT_CONSENT"
else
    echo -e "Enter ${BLUE}'yes'${NC} if you want to proceed:"
    read CONSENT
fi

if [ "$CONSENT" != "yes" ]; then
    echo -e "\nERROR: Aborted by user"
    exit 1
else
    echo -e "\n......................................................"
    echo -e "\n> Received user consent"
fi

#
# Executes action with one randomly delayed retry.
#
function do_with_retry {
    COMMAND="$@"
    echo "Trying $COMMAND"
    (eval $COMMAND && echo "Success on first try") || ( \
        echo "Waiting few seconds to retry" &&
        sleep 10 && \
        echo "Retrying $COMMAND" && \
        eval $COMMAND \
    )
}

echo "> Enabling required APIs"
# Some of these can be enabled later with Terraform, but I personally
# prefer to do all API enablement in one place with gcloud.
gcloud services enable \
    --project=$PROJECT_ID \
    cloudbuild.googleapis.com \
    cloudresourcemanager.googleapis.com \
    compute.googleapis.com \
    sourcerepo.googleapis.com \
    --no-user-output-enabled \
    --quiet

echo "> Adding Cloud Build to roles/editor"
gcloud projects add-iam-policy-binding \
    "$PROJECT_ID" \
    --member="serviceAccount:$PROJECT_NUM@cloudbuild.gserviceaccount.com" \
    --role='roles/editor' \
    --condition=None \
    --no-user-output-enabled \
    --quiet

echo "> Adding Cloud Build to roles/source.admin"
gcloud projects add-iam-policy-binding \
    "$PROJECT_ID" \
    --member="serviceAccount:$PROJECT_NUM@cloudbuild.gserviceaccount.com" \
    --condition=None \
    --role='roles/source.admin' \
    --no-user-output-enabled \
    --quiet

echo "> Configuring bootstrap job"
rm -rf "./bootstrap.cloudbuild.yaml"
cat <<'EOT_BOOT' > "./bootstrap.cloudbuild.yaml"
tags:
- "mig-blue-green-bootstrapping"
steps:
- id: create_new_cloud_source_repo
  name: "gcr.io/cloud-builders/gcloud"
  script: |
    #!/bin/bash
    set -e

    echo "(Re)Creating source code repository"

    gcloud source repos delete \
        "copy-of-mig-blue-green" \
        --quiet || true

    gcloud source repos create \
        "copy-of-mig-blue-green" \
        --quiet

- id: copy_demo_source_into_new_cloud_source_repo
  name: "gcr.io/cloud-builders/gcloud"
  env:
    - "PROJECT_ID=$PROJECT_ID"
    - "PROJECT_NUMBER=$PROJECT_NUMBER"
  script: |
    #!/bin/bash
    set -e

    readonly GIT_REPO="https://github.com/GoogleCloudPlatform/cloud-build-samples.git"

    echo "Cloning demo source repo"
    mkdir /workspace/from/
    cd /workspace/from/
    git clone $GIT_REPO ./original
    cd ./original

    echo "Cloning new empty repo"
    mkdir /workspace/to/
    cd /workspace/to/
    gcloud source repos clone \
        "copy-of-mig-blue-green"
    cd ./copy-of-mig-blue-green

    echo "Making a copy"
    cp -r /workspace/from/original/mig-blue-green/* ./

    echo "Setting git identity"
    git config user.email \
        "$PROJECT_NUMBER@cloudbuild.gserviceaccount.com"
    git config user.name \
        "Cloud Build"

    echo "Commit & push"
    git add .
    git commit \
        -m "A copy of $GIT_REPO"
    git push

- id: add_pipeline_triggers
  name: "gcr.io/cloud-builders/gcloud"
  env:
    - "PROJECT_ID=$PROJECT_ID"
  script: |
    #!/bin/bash
    set -e

    echo "(Re)Creating destroy trigger"
    gcloud builds triggers delete "destroy" --quiet || true
    gcloud builds triggers create manual \
        --name="destroy" \
        --repo="https://source.developers.google.com/p/$PROJECT_ID/r/copy-of-mig-blue-green" \
        --branch="master" \
        --build-config="pipelines/destroy.cloudbuild.yaml" \
        --repo-type=CLOUD_SOURCE_REPOSITORIES \
        --quiet

    echo "(Re)Creating apply trigger"
    gcloud builds triggers delete "apply" --quiet || true
    gcloud builds triggers create cloud-source-repositories \
        --name="apply" \
        --repo="copy-of-mig-blue-green" \
        --branch-pattern="master" \
        --build-config="pipelines/apply.cloudbuild.yaml" \
        --included-files="infra/main.tfvars" \
        --quiet

EOT_BOOT

echo "> Waiting API enablement propagation"
do_with_retry "(gcloud builds list --project "$PROJECT_ID" --quiet && gcloud compute instances list --project "$PROJECT_ID" --quiet && gcloud source repos list --project "$PROJECT_ID" --quiet) > /dev/null 2>&1" > /dev/null 2>&1

echo "> Executing bootstrap job"
gcloud beta builds submit \
    --project "$PROJECT_ID" \
    --config ./bootstrap.cloudbuild.yaml \
    --no-source \
    --no-user-output-enabled \
    --quiet
rm ./bootstrap.cloudbuild.yaml

echo -e "\n${GREEN}All done. Now you can:${NC}"
echo -e "  * manually run 'apply' and 'destroy' triggers to manage deployment lifecycle"
echo -e "  * commit change to 'infra/main.tfvars' and see 'apply' pipeline trigger automatically"

echo -e "\n${GREEN}Few key links:${NC}"
echo -e "  * Dashboard: https://console.cloud.google.com/home/dashboard?project=$PROJECT_ID"
echo -e "  * Repo: https://source.cloud.google.com/$PROJECT_ID/copy-of-mig-blue-green"
echo -e "  * Cloud Build Triggers: https://console.cloud.google.com/cloud-build/triggers;region=global?project=$PROJECT_ID"
echo -e "  * Cloud Build History: https://console.cloud.google.com/cloud-build/builds?project=$PROJECT_ID"

echo -e "\n............................."

echo -e "\n${GREEN}COMPLETED!${NC}"

Pipelines do Cloud Build

apply.cloudbuild.yaml e destroy.cloudbuild.yaml são os arquivos de configuração do Cloud Build que o script de configuração usa para configurar os recursos do fluxo do GitOps. O apply.cloudbuild.yaml contém duas etapas de build:

  • Etapa de build tf_apply build que chama a função tf_install_in_cloud_build_step, que instala o Terraform. tf_apply que cria os recursos usados no fluxo do GitOps. As funções tf_install_in_cloud_build_step e tf_apply são definidas em bash_utils.sh, e a etapa de build usa o comando source para chamá-las.
  • Etapa de build describe_deployment que chama a função describe_deployment, que imprime os endereços IP dos balanceadores de carga.

destroy.cloudbuild.yaml chama tf_destroy, que exclui todos os recursos criados por tf_apply.

As funções tf_install_in_cloud_build_step, tf_apply, describe_deployment e tf_destroy são definidas no arquivo bash_utils.sh. Os arquivos de configuração do build usam o comando source para chamar as funções.

steps:
  - id: run-terraform-apply
    name: "gcr.io/cloud-builders/gcloud"
    env:
      - "PROJECT_ID=$PROJECT_ID"
    script: |
      #!/bin/bash
      set -e
      source /workspace/lib/bash_utils.sh
      tf_install_in_cloud_build_step
      tf_apply

  - id: describe-deployment
    name: "gcr.io/cloud-builders/gcloud"
    env:
      - "PROJECT_ID=$PROJECT_ID"
    script: |
      #!/bin/bash
      set -e
      source /workspace/lib/bash_utils.sh
      describe_deployment

tags:
  - "mig-blue-green-apply"
steps:
  - id: run-terraform-destroy
    name: "gcr.io/cloud-builders/gcloud"
    env:
      - "PROJECT_ID=$PROJECT_ID"
    script: |
      #!/bin/bash
      set -e
      source /workspace/lib/bash_utils.sh
      tf_install_in_cloud_build_step
      tf_destroy

tags:
  - "mig-blue-green-destroy"

O código a seguir mostra a função tf_install_in_cloud_build_step definida em bash_utils.sh. Os arquivos de configuração do build chamam essa função para instalar o Terraform em tempo real. Ele cria um bucket do Cloud Storage para registrar o status do Terraform.

function tf_install_in_cloud_build_step {
    echo "Installing deps"
    apt update
    apt install \
        unzip \
        wget \
        -y

    echo "Manually installing Terraform"
    wget https://releases.hashicorp.com/terraform/1.3.4/terraform_1.3.4_linux_386.zip
    unzip -q terraform_1.3.4_linux_386.zip
    mv ./terraform /usr/bin/
    rm -rf terraform_1.3.4_linux_386.zip

    echo "Verifying installation"
    terraform -v

    echo "Creating Terraform state storage bucket $BUCKET_NAME"
    gcloud storage buckets create \
        "gs://$BUCKET_NAME" || echo "Already exists..."

    echo "Configure Terraform provider and state bucket"
cat <<EOT_PROVIDER_TF > "/workspace/infra/provider.tf"
terraform {
  required_version = ">= 0.13"
  backend "gcs" {
    bucket = "$BUCKET_NAME"
  }
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = ">= 3.77, < 5.0"
    }
  }
}
EOT_PROVIDER_TF

    echo "$(cat /workspace/infra/provider.tf)"
}

O snippet de código a seguir mostra a função tf_apply definida em bash_utils.sh. Ele primeiro chama terraform init, que carrega todos os módulos e bibliotecas personalizadas e, em seguida, executa terraform apply para carregar as variáveis do arquivo main.tfvars.

function tf_apply {
    echo "Running Terraform init"
    terraform \
        -chdir="$TF_CHDIR" \
        init

    echo "Running Terraform apply"
    terraform \
        -chdir="$TF_CHDIR" \
        apply \
        -auto-approve \
        -var project="$PROJECT_ID" \
        -var-file="main.tfvars"
}

O snippet de código a seguir mostra a função describe_deployment definida em bash_utils.sh. Ele usa gcloud compute addresses describe para buscar os endereços IP dos balanceadores de carga usando o nome e os imprime.

function describe_deployment {
    NS="ns1-"
    echo -e "Deployment configuration:\n$(cat infra/main.tfvars)"
    echo -e \
      "Here is how to connect to:" \
      "\n\t* active color MIG: http://$(gcloud compute addresses describe ${NS}splitter-address-name --region=us-west1 --format='value(address)')/" \
      "\n\t* blue color MIG: http://$(gcloud compute addresses describe ${NS}blue-address-name --region=us-west1 --format='value(address)')/" \
      "\n\t* green color MIG: http://$(gcloud compute addresses describe ${NS}green-address-name --region=us-west1 --format='value(address)')/"
    echo "Good luck!"
}

O snippet de código a seguir mostra a função tf_destroy definida em bash_utils.sh. Ele chama terraform init, que carrega todos os módulos e bibliotecas personalizadas e, em seguida, executa terraform destroy, que descarrega as variáveis do Terraform.

function tf_destroy {
    echo "Running Terraform init"
    terraform \
        -chdir="$TF_CHDIR" \
        init

    echo "Running Terraform destroy"
    terraform \
        -chdir="$TF_CHDIR" \
        destroy \
        -auto-approve \
        -var project="$PROJECT_ID" \
        -var-file="main.tfvars"
}

Modelos do Terraform

Todos os arquivos e variáveis de configuração do Terraform estão na pasta copy-of-gcp-mig-simple/infra/.

  • main.tf: é o arquivo de configuração do Terraform
  • main.tfvars: este arquivo define as variáveis do Terraform.
  • mig/ e splitter/: essas pastas contêm os módulos que definem os balanceadores de carga. A pasta mig/ contém o arquivo de configuração do Terraform que define o MIG para os balanceadores de carga azuis e verdes. Os MIGs azul e verde são idênticos, portanto, são definidos uma vez e instanciados para os objetos azul e verde. O arquivo de configuração do Terraform para o balanceador de carga do divisor está na pasta splitter/ .

O snippet de código a seguir mostra o conteúdo de infra/main.tfvars. Ele contém três variáveis: duas que determinam qual versão do aplicativo implantar nos pools azul e verde e uma variável para a cor ativa: azul ou verde. As mudanças nesse arquivo acionam a implantação.

MIG_VER_BLUE     = "v1"
MIG_VER_GREEN    = "v1"
MIG_ACTIVE_COLOR = "blue"

Confira a seguir um snippet de código do infra/main.tf. Neste snippet:

  • Uma variável é definida para o projeto do Google Cloud.
  • O Google está definido como o provedor do Terraform.
  • Uma variável é definida para o namespace. Todos os objetos criados pelo Terraform têm o prefixo dessa variável para que várias versões do aplicativo possam ser implantadas no mesmo projeto e os nomes dos objetos não colidam uns com os outros.
  • As variáveis MIG_VER_BLUE, MIG_VER_BLUE e MIG_ACTIVE_COLOR são as vinculações para as variáveis no arquivo infra/main.tfvars.
variable "project" {
  type        = string
  description = "GCP project we are working in."
}

provider "google" {
  project = var.project
  region  = "us-west1"
  zone    = "us-west1-a"
}

variable "ns" {
  type        = string
  default     = "ns1-"
  description = "The namespace used for all resources in this plan."
}

variable "MIG_VER_BLUE" {
  type        = string
  description = "Version tag for 'blue' deployment."
}

variable "MIG_VER_GREEN" {
  type        = string
  description = "Version tag for 'green' deployment."
}

variable "MIG_ACTIVE_COLOR" {
  type        = string
  description = "Active color (blue | green)."
}

O snippet de código a seguir de infra/main.tf mostra a instanciação do módulo de divisão. Esse módulo recebe a cor ativa para que o balanceador de carga do divisor saiba qual MIG implantar o aplicativo.

module "splitter-lb" {
  source               = "./splitter"
  project              = var.project
  ns                   = "${var.ns}splitter-"
  active_color         = var.MIG_ACTIVE_COLOR
  instance_group_blue  = module.blue.google_compute_instance_group_manager_default.instance_group
  instance_group_green = module.green.google_compute_instance_group_manager_default.instance_group
}

O snippet de código abaixo de infra/main.tf define dois módulos idênticos para MIGs azuis e verdes. Ele recebe a cor, a rede e a sub-rede definidas no módulo de divisor.

module "blue" {
  source                               = "./mig"
  project                              = var.project
  app_version                          = var.MIG_VER_BLUE
  ns                                   = var.ns
  color                                = "blue"
  google_compute_network               = module.splitter-lb.google_compute_network
  google_compute_subnetwork            = module.splitter-lb.google_compute_subnetwork_default
  google_compute_subnetwork_proxy_only = module.splitter-lb.google_compute_subnetwork_proxy_only
}

module "green" {
  source                               = "./mig"
  project                              = var.project
  app_version                          = var.MIG_VER_GREEN
  ns                                   = var.ns
  color                                = "green"
  google_compute_network               = module.splitter-lb.google_compute_network
  google_compute_subnetwork            = module.splitter-lb.google_compute_subnetwork_default
  google_compute_subnetwork_proxy_only = module.splitter-lb.google_compute_subnetwork_proxy_only
}

O arquivo splitter/main.tf define os objetos que são criados para o MIG do divisor. O snippet de código a seguir de splitter/main.tf contém a lógica para alternar entre o MIG verde e o azul. Ele é apoiado pelo serviço google_compute_region_backend_service, que pode rotear o tráfego para duas regiões de back-end: var.instance_group_blue ou var.instance_group_green. capacity_scaler define quanto do tráfego será roteado.

O código a seguir encaminha 100% do tráfego para a cor especificada, mas você pode atualizar esse código para a implantação canário para encaminhar o tráfego para um subconjunto de usuários.

resource "google_compute_region_backend_service" "default" {
  name                  = local.l7-xlb-backend-service
  region                = "us-west1"
  load_balancing_scheme = "EXTERNAL_MANAGED"
  health_checks         = [google_compute_region_health_check.default.id]
  protocol              = "HTTP"
  session_affinity      = "NONE"
  timeout_sec           = 30
  backend {
    group           = var.instance_group_blue
    balancing_mode  = "UTILIZATION"
    capacity_scaler = var.active_color == "blue" ? 1 : 0
  }
  backend {
    group           = var.instance_group_green
    balancing_mode  = "UTILIZATION"
    capacity_scaler = var.active_color == "green" ? 1 : 0
  }
}

O arquivo mig/main.tf define os objetos relacionados às MIGs azul e verde. O snippet de código abaixo define o modelo de instância do Compute Engine usado para criar os pools de VM. Observe que o ciclo de vida da propriedade do modelo de instância está definido como create_before_destroy. Isso ocorre porque, ao atualizar a versão do pool, não é possível usar o modelo para criar a nova versão dos pools quando ele ainda está sendo usado pela versão anterior do pool. No entanto, se a versão mais antiga do pool for destruída antes da criação do novo modelo, haverá um período em que os pools estarão inativos. Para evitar esse cenário, definimos o ciclo de vida do Terraform como create_before_destroy para que a versão mais recente de um pool de VMs seja criada primeiro antes que a versão mais antiga seja destruída.

resource "google_compute_instance_template" "default" {
  name = local.l7-xlb-backend-template
  disk {
    auto_delete  = true
    boot         = true
    device_name  = "persistent-disk-0"
    mode         = "READ_WRITE"
    source_image = "projects/debian-cloud/global/images/family/debian-10"
    type         = "PERSISTENT"
  }
  labels = {
    managed-by-cnrm = "true"
  }
  machine_type = "n1-standard-1"
  metadata = {
    startup-script = <<EOF
    #! /bin/bash
    sudo apt-get update
    sudo apt-get install apache2 -y
    sudo a2ensite default-ssl
    sudo a2enmod ssl
    vm_hostname="$(curl -H "Metadata-Flavor:Google" \
    http://169.254.169.254/computeMetadata/v1/instance/name)"
    sudo echo "<html><body style='font-family: Arial; margin: 64px; background-color: light${var.color};'><h3>Hello, World!<br><br>version: ${var.app_version}<br>ns: ${var.ns}<br>hostname: $vm_hostname</h3></body></html>" | \
    tee /var/www/html/index.html
    sudo systemctl restart apache2
    EOF
  }
  network_interface {
    access_config {
      network_tier = "PREMIUM"
    }
    network    = var.google_compute_network.id
    subnetwork = var.google_compute_subnetwork.id
  }
  region = "us-west1"
  scheduling {
    automatic_restart   = true
    on_host_maintenance = "MIGRATE"
    provisioning_model  = "STANDARD"
  }
  tags = ["load-balanced-backend"]

  # NOTE: the name of this resource must be unique for every update;
  #       this is wy we have a app_version in the name; this way
  #       new resource has a different name vs old one and both can
  #       exists at the same time
  lifecycle {
    create_before_destroy = true
  }
}

Limpar

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto que os contém ou mantenha o projeto e exclua os recursos individuais.

Excluir recursos individuais

  1. Exclua os recursos do Compute Engine criados pelo gatilho de aplicação:

    1. Abra a página Gatilhos do Cloud Build:

      Abrir a página "Gatilhos"

    2. Na tabela Triggers, localize a linha correspondente ao gatilho destroy e clique em Run. Quando a execução do gatilho é concluída, os recursos criados pelo gatilho aplicar são excluídos.

  2. Exclua os recursos criados durante o inicialização executando o seguinte comando na janela do terminal:

    bash <(curl https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-build-samples/main/mig-blue-green/teardown.sh)
    

Excluir o projeto

    Delete a Google Cloud project:

    gcloud projects delete PROJECT_ID

A seguir