Implantar no Compute Engine


Este guia explica como realizar implantações azul/verde sem inatividade em grupos gerenciados de instâncias (MIGs, na sigla em inglês) 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 implantação de aplicativos em vários Google Cloud ambientes de execução como Compute Engine, Google Kubernetes Engine, GKE Enterprise e funções do Cloud Run.

Com os MIGs do Compute Engine, é possível 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 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 encaminha o tráfego de usuários finais para o pool azul ou verde de instâncias de VM.
    • Um balanceador de carga azul que roteia o tráfego de engenheiros de controle de qualidade e desenvolvedores para o pool de instâncias de VM azul.
    • Um balanceador de carga verde que roteia o tráfego de engenheiros de controle de qualidade 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 para o pool de instâncias azul ou verde.
    • Engenheiros de controle de qualidade e desenvolvedores 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 encaminham para o pool de instâncias azul e 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 acontecem na implantação:

Fluxo de operações de desenvolvedores

No diagrama acima, as setas vermelhas representam o fluxo de bootstrap 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 bootstrap 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 amostra 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 um 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.
  • Imprime os endereços IP que podem ser usados para ver os aplicativos implantados nas instâncias azul e verde.

O gatilho de exclusão é acionado manualmente para excluir todos os recursos criados pelo gatilho 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.

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

Custos

Neste documento, você vai 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. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  4. To initialize the gcloud CLI, run the following command:

    gcloud init
  5. 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.

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

  7. Install the Google Cloud CLI.

  8. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  9. To initialize the gcloud CLI, run the following command:

    gcloud init
  10. 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.

  11. 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 yes.

    O script termina de ser executado em alguns segundos.

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

    Abra a página "Histórico de builds"

  4. Clique no build mais recente.

    A página Detalhes da 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 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 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 gatilho apply está anexado ao arquivo infra/main.tfvars na ramificação main. Esse gatilho é 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 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 mudanças 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 Histórico, na parte de baixo da página.

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

    Abra a página "Histórico de builds"

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

    Você vai ver o pipeline de gatilho apply com duas etapas de build. A primeira etapa de build executa "terraform apply" para criar os recursos do Compute Engine e de balanceamento de carga para a implantação. A segunda etapa de build imprime o endereço IP em que você pode ver o aplicativo 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 ver os grupos de instâncias azul e verde:

    Abra a página "Grupo de instâncias".

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

    Abra a página "Instância 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 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 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. Essa função é necessária para que o Cloud Build crie e configure os componentes do GitOps necessários 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 o Cloud Source Repositories no seu projeto e clone o conteúdo do repositório de exemplo do GitHub no Cloud Source Repositories.
  • Gera um pipeline do Cloud Build chamado bootstrap.cloudbuild.yaml inline, que:

    • Cria um repositório no Cloud Source Repositories.
    • Copia o código-fonte do repositório de amostra do GitHub para o novo repositório no Cloud Source Repositories.
    • Cria os gatilhos de build de aplicação e destruição.
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 chamar essas funções.
  • 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 de 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 de build chamam essa função para instalar o Terraform de maneira dinâmica. 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. 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 e variáveis de configuração do Terraform estão na pasta copy-of-gcp-mig-simple/infra/.

  • main.tf: este é 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 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 de infra/main.tf. Neste snippet:

  • Uma variável é definida para o projeto 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 para que várias versões do aplicativo possam ser implantadas no mesmo projeto e os nomes dos objetos não entrem em conflito entre si.
  • 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 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 em 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 a seguir de infra/main.tf define dois módulos idênticos para MIGs azuis e verdes. Ele usa 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 splitter. Confira abaixo 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 é 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 a quantidade de tráfego a ser roteada.

O código a seguir encaminha 100% do tráfego para a cor especificada, mas você pode atualizar esse código para uma implantação canário e 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 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 acontece porque, ao atualizar a versão do pool, não é possível usar o modelo para criar a nova versão dos pools enquanto ele ainda está sendo usado pela versão anterior do pool. Mas 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 ficarã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 Gatilhos, localize a linha correspondente ao gatilho destroy e clique em Executar. Quando o gatilho termina a execução, os recursos criados por ele são excluídos.

  2. Exclua os recursos criados durante o bootstrapping 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