Implante no Compute Engine


Neste guia, explicamos como executar implantações azul-verde sem inatividade em grupos gerenciados de instâncias (MIGs) do Compute Engine usando o Cloud Build e o Terraform.

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

Os MIGs do Compute Engine permitem operar aplicativos em várias máquinas virtuais (VMs) idênticas. É possível tornar suas cargas de trabalho escalonáveis e altamente disponíveis aproveitando os serviços de MIG automatizados, incluindo: 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ê 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

No diagrama a seguir, mostramos o modelo de implantação azul-verde usado pelo exemplo de código descrito neste documento:

Modelo azul-verde

De modo geral, 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 roteia o tráfego de usuários finais para o pool azul ou verde das instâncias de VM.
    • Um balanceador de carga azul que encaminha o tráfego de engenheiros e desenvolvedores de controle de qualidade para o pool de instâncias de VM azul.
    • Um balanceador de carga verde que encaminha o tráfego de engenheiros e desenvolvedores de controle de qualidade para o pool de instâncias Green.
  • Dois conjuntos de usuários:
    • Usuários finais que têm acesso ao balanceador de carga azul/verde, que os aponta para o pool de instâncias azul ou verde.
    • Engenheiros e desenvolvedores de controle de qualidade que precisam de acesso aos dois conjuntos de pools para fins de desenvolvimento e teste. Eles podem acessar os balanceadores de carga azul e verde, que os encaminha para o pool de instâncias azul e para o pool de instâncias verde, respectivamente.

Os pools de VMs azul e verde são implementados como MIGs do Compute Engine, e os endereços IP externo 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 do 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 exemplos do GitHub para o repositório no Cloud Source Repositories.
  • Cria dois gatilhos do Cloud Build chamados apply e destroy.

O gatilho apply está anexado a um arquivo do Terraform chamado main.tfvars no Cloud Source Repositories. 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 azul), os três balanceadores de carga (azul, verde e o divisor) e três endereços IP públicos.
  • Imprime os endereços IP que podem ser usados para ver os aplicativos implantados nas instâncias azul e verde.

O gatilho 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.

  • Executar 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. Faça login na sua conta do Google Cloud. Se você começou a usar o Google Cloud agora, crie uma conta para avaliar o desempenho de nossos produtos em situações reais. Clientes novos também recebem US$ 300 em créditos para executar, testar e implantar cargas de trabalho.
  2. Instale a CLI do Google Cloud.
  3. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  4. Crie ou selecione um projeto do Google Cloud.

    • Crie um projeto do Google Cloud:

      gcloud projects create PROJECT_ID

      Substitua PROJECT_ID por um nome para o projeto do Google Cloud que você está criando.

    • Selecione o projeto do Google Cloud que você criou:

      gcloud config set project PROJECT_ID

      Substitua PROJECT_ID pelo nome do projeto do Google Cloud.

  5. Verifique se a cobrança está ativada para o seu projeto do Google Cloud.

  6. Instale a CLI do Google Cloud.
  7. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  8. Crie ou selecione um projeto do Google Cloud.

    • Crie um projeto do Google Cloud:

      gcloud projects create PROJECT_ID

      Substitua PROJECT_ID por um nome para o projeto do Google Cloud que você está criando.

    • Selecione o projeto do Google Cloud que você criou:

      gcloud config set project PROJECT_ID

      Substitua PROJECT_ID pelo nome do projeto do Google Cloud.

  9. Verifique se a cobrança está ativada para o seu projeto do Google Cloud.

Testar

  1. Execute o script de configuração no 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 solicitar o consentimento do usuário, insira yes.

    A execução do script termina em alguns segundos.

  3. No console do Google Cloud, abra a página Histórico da versão do Cloud Build:

    Abrir a página "Histórico do build"

  4. Clique no build mais recente.

    Você verá a página Detalhes da compilação, que mostra um pipeline do Cloud Build com três etapas: a primeira cria um repositório no Cloud Source Repositories, a segunda clona o conteúdo do repositório de amostra 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 Histórico, na parte inferior da página, você verá uma confirmação com a descrição A copy of https://github.com/GoogleCloudPlatform/cloud-build-samples.git feita 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ê verá dois gatilhos de build chamados apply e destroy. O gatilho apply é anexado ao arquivo infra/main.tfvars na ramificação main. Esse acionador é executado sempre que o arquivo é atualizado. O gatilho 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 por push:

      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.

    Você verá a confirmação com a descrição Promote green na guia History na parte inferior da página.

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

    Abrir a página "Histórico do build"

  13. Abra a página Detalhes da versão clicando no primeiro build.

    Você vai notar o pipeline do gatilho apply com duas etapas de build. A primeira etapa de build executa o Terraform apply para criar os recursos do Compute Engine e de balanceamento de carga para a implantação. A segunda etapa da criação 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ê verá uma captura de tela semelhante à seguinte que mostra a implantação:

    Deployment

  15. Acesse a página Grupo de instâncias do Compute Engine para ver 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 ver as quatro instâncias de VM:

    Abrir a página da instância de VM

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

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

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

O código-fonte deste exemplo de código inclui:

  • Código-fonte relacionado ao script de configuraçã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 realiza as seguintes operações:

  • Ativa as APIs Cloud Build, Resource Manager, Compute Engine e Cloud Source Repositories.
  • Concede o papel do IAM roles/editor à conta de serviço do Cloud Build no projeto. Esse papel é necessário para que o Cloud Build crie e configure os componentes GitOps necessários para a implantação.
  • Concede o papel do IAM roles/source.admin à conta de serviço do Cloud Build no projeto. Esse papel é necessário para que a conta de serviço do Cloud Build crie o Cloud Source Repositories no seu projeto e clone o conteúdo do repositório de amostra do GitHub no seu Cloud Source Repositories.
  • 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 amostra para o novo repositório no Cloud Source Repositories.
    • Cria os acionadores de build apply e destroy.
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. apply.cloudbuild.yaml contém duas etapas 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.
  • 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 rapidamente. 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. Primeiro, ele chama terraform init, que carrega todos os módulos e bibliotecas personalizadas, e depois 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 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 de configuração e variáveis do Terraform estão na pasta copy-of-gcp-mig-simple/infra/.

  • main.tf: é o arquivo de configuração do Terraform
  • main.tfvars: esse 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 azul e verde. Os MIGs azul e verde são idênticos. Portanto, eles 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 será implantada nos pools azul e verde e uma variável para a cor ativa: azul ou verde. As alterações feitas 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 de 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 essa variável como prefixo. Assim, várias versões do aplicativo podem ser implantadas no mesmo projeto e os nomes dos objetos não entram em conflito.
  • As variáveis MIG_VER_BLUE, MIG_VER_BLUE e MIG_ACTIVE_COLOR são as vinculações das 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 de infra/main.tf a seguir mostra a instanciação do módulo divisor. 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 de infra/main.tf a seguir define dois módulos idênticos para MIGs azuis e verdes. Ele recebe a cor, a rede e a sub-rede, que são definidas no módulo 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 criados para o MIG do divisor. Veja a seguir um snippet de código de splitter/main.tf que contém a lógica para alternar entre o MIG verde e o azul. Ele tem o suporte do 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 a ser roteado.

O código a seguir direciona 100% do tráfego para a cor especificada, mas é possível atualizar esse código para implantação canário para rotear 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 pertencentes aos MIGs azul e verde. O snippet de código a seguir desse arquivo define o modelo de instância do Compute Engine usado para criar os pools de VMs. Observe que esse modelo de instância tem a propriedade de ciclo de vida do Terraform definida 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 ela ainda está sendo usada pela versão anterior do pool. No entanto, se a versão mais antiga do pool for destruída antes de criar o novo modelo, haverá um período em que os pools ficarão desativados. 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 Acionadores, localize a linha correspondente ao acionador destroy e clique em Executar. Quando o gatilho concluir a execução, os recursos criados por ele apply serão excluídos.

  2. Exclua os recursos criados durante a 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)
    

Exclua o projeto

    Exclua um projeto do Google Cloud:

    gcloud projects delete PROJECT_ID

A seguir