Implemente no Compute Engine

Este guia explica como fazer implementações azul/verde sem tempo de inatividade em grupos de instâncias geridos (MIGs) do Compute Engine através do Cloud Build e do Terraform.

O Cloud Build permite-lhe automatizar uma variedade de processos de programadores, incluindo a criação e a implementação de aplicações em vários Google Cloud tempos de execução como o Compute Engine, Google Kubernetes Engine, GKE Enterprise, e funções do Cloud Run.

Os GIGs do Compute Engine permitem-lhe operar aplicações em várias máquinas virtuais (VMs) idênticas. Pode tornar as suas cargas de trabalho escaláveis e altamente disponíveis tirando partido dos serviços de MIG automatizados, incluindo: escalamento automático, autocura, implementação regional (várias zonas) e atualização automática. Com o modelo de implementação contínua azul/verde, vai aprender a transferir gradualmente o tráfego de utilizadores de um MIG (azul) para outro MIG (verde), ambos em execução na produção.

Antes de começar

  • Enable the Cloud Build, Cloud Run, Artifact Registry, and Resource Manager APIs.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  • Tenha o código-fonte da aplicação pronto. O código fonte tem de estar armazenado num repositório, como o GitHub ou o Bitbucket.

  • Para executar os comandos gcloud nesta página, instale a CLI do Google Cloud.

Autorizações de gestão de identidade e de acesso necessárias

  1. Na Google Cloud consola, aceda à página Autorizações do Cloud Build:

    Aceda a Autorizações

  2. Para a conta de serviço do Cloud Build especificada ou a conta de serviço do Cloud Build predefinida, defina o estado das seguintes funções como Ativado:

    • Administrador de instâncias do Compute v1 (roles/compute.instanceAdmin) | Permite que o Cloud Build implemente novas instâncias no Compute Engine.
    • Administrador de armazenamento (roles/storage.admin) | Permite a leitura e a escrita a partir do Cloud Storage.
    • Escritor do Artifact Registry (roles/artifactregistry.writer) | Permite extrair imagens do Artifact Registry e escrever no mesmo.
    • Escritor de registos (roles/logging.logWriter) | Permite que as entradas de registo sejam escritas no Cloud Logging.
    • Editor do Cloud Build (roles/cloudbuild.builds.editor) | Permite que a sua conta de serviço execute compilações.

Vista geral do design

O diagrama seguinte mostra o modelo de implementação azul-verde usado no exemplo de código descrito neste documento:

Modelo azul/verde

A um nível elevado, este modelo inclui os seguintes componentes:

  • Dois pools de VMs do Compute Engine: azul e verde.
  • Três balanceadores de carga HTTP(S) externos:
    • Um equilibrador de carga azul/verde que encaminha o tráfego dos utilizadores finais para o conjunto azul ou verde de instâncias de VM.
    • Um balanceador de carga azul que encaminha o tráfego de engenheiros de controlo de qualidade e programadores para o conjunto de instâncias de VMs azuis.
    • Um equilibrador de carga verde que encaminha o tráfego de engenheiros de CQ e programadores para o conjunto de instâncias verde.
  • Dois conjuntos de utilizadores:
    • Utilizadores finais que têm acesso ao equilibrador de carga azul-verde, que os direciona para o conjunto de instâncias azul ou verde.
    • Engenheiros de controlo de qualidade e programadores que precisam de acesso a ambos os conjuntos de pools para fins de desenvolvimento e testes. Podem aceder aos balanceadores de carga azuis e verdes, que os encaminham para o conjunto de instâncias azuis e o conjunto de instâncias verdes, respetivamente.

Os conjuntos de VMs azuis e verdes são implementados como MIGs do Compute Engine, e os endereços IP externos são encaminhados para as VMs no MIG através de equilibradores de carga HTTP(s) externos. O exemplo de código descrito neste documento usa o Terraform para configurar esta infraestrutura.

O diagrama seguinte ilustra as operações do programador que ocorrem na implementação:

Fluxo de operações do programador

No diagrama anterior, as setas vermelhas representam o fluxo de arranque que ocorre quando configura a infraestrutura de implementação pela primeira vez, e as setas azuis representam o fluxo de GitOps que ocorre durante cada implementação.

Para configurar esta infraestrutura, executa um script de configuração que inicia o processo de arranque 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 nos Cloud Source Repositories denominado copy-of-gcp-mig-simple e copia o código fonte do repositório de exemplo do GitHub para o repositório nos Cloud Source Repositories.
  • Cria dois acionadores do Cloud Build com os nomes apply e destroy.

O acionador apply está anexado a um ficheiro do Terraform denominado main.tfvars nos Cloud Source Repositories. Este ficheiro contém as variáveis do Terraform que representam os balanceadores de carga azuis e verdes.

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

  • Cria dois GIGs do Compute Engine (um para o verde e outro para o azul), quatro instâncias de VM do Compute Engine (duas para o GIG verde e duas para o GIG 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 pode usar para ver as aplicações implementadas nas instâncias azuis e verdes.

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

Objetivos

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

  • Realize implementações azul/verde nas instâncias de VM.

Custos

Neste documento, usa os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custos com base na sua utilização prevista, use a calculadora de preços.

Os novos Google Cloud utilizadores podem ser elegíveis para uma avaliação gratuita.

Quando terminar as tarefas descritas neste documento, pode evitar a faturação contínua eliminando os recursos que criou. Para mais informações, consulte o artigo Limpe.

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. Se estiver a usar um fornecedor de identidade (IdP) externo, tem primeiro de iniciar sessão na CLI gcloud com a sua identidade federada.

  4. Para inicializar a CLI gcloud, execute o seguinte comando:

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

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.
    • 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. Verify that billing is enabled for your Google Cloud project.

  7. Enable the Cloud Build, Cloud Run, Artifact Registry, and Resource Manager APIs:

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    gcloud services enable cloudbuild.googleapis.com run.googleapis.com artifactregistry.googleapis.com cloudresourcemanager.googleapis.com
  8. Install the Google Cloud CLI.

  9. Se estiver a usar um fornecedor de identidade (IdP) externo, tem primeiro de iniciar sessão na CLI gcloud com a sua identidade federada.

  10. Para inicializar a CLI gcloud, execute o seguinte comando:

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

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.
    • 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.

  12. Verify that billing is enabled for your Google Cloud project.

  13. Enable the Cloud Build, Cloud Run, Artifact Registry, and Resource Manager APIs:

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    gcloud services enable cloudbuild.googleapis.com run.googleapis.com artifactregistry.googleapis.com cloudresourcemanager.googleapis.com
  14. Experimentar

    1. Execute o script de configuração a partir do repositório de exemplos de código da 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 utilizador, introduza yes.

      O script termina a execução em alguns segundos.

    3. Na Google Cloud consola, abra a página Histórico de compilações do Cloud Build:

      Abra a página Criar histórico

    4. Clique na compilação mais recente.

      É apresentada a página Detalhes da compilação, que mostra um pipeline do Cloud Build com três passos de compilação: o primeiro passo de compilação cria um repositório nos Cloud Source Repositories, o segundo passo clona o conteúdo do repositório de exemplo no GitHub para os Cloud Source Repositories e o terceiro passo adiciona dois acionadores de compilação.

    5. Abra os Cloud Source Repositories:

      Abrir Cloud Source Repositories

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

      No separador Histórico na parte inferior da página, é apresentado um commit com a descrição A copy of https://github.com/GoogleCloudPlatform/cloud-build-samples.git criado pelo Cloud Build para criar um repositório com o nome copy-of-gcp-mig-simple.

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

      Abra a página Acionadores

    8. São apresentados dois acionadores de compilação com os nomes apply e destroy. O acionador apply está anexado ao ficheiro infra/main.tfvars no ramo main. Este acionador é executado sempre que o ficheiro é atualizado. O acionador destroy é um acionador manual.

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

      1. Na janela do terminal, crie e navegue para uma pasta com o nome 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 para 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 ficheiro atualizado:

        git add .
        
      6. Consolide o ficheiro:

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

        git push
        

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

    10. Abra os Cloud Source Repositories:

      Abrir Cloud Source Repositories

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

      Verá a confirmação com a descrição Promote green no separador Histórico na parte inferior da página.

    12. Para ver a execução do acionador apply, abra a página Histórico de compilação na consola Google Cloud :

      Abra a página Criar histórico

    13. Abra a página Detalhes da compilação clicando na primeira compilação.

      É apresentada a pipeline de acionadores apply com dois passos de compilação. O primeiro passo de compilação executa o comando Terraform apply para criar os recursos do Compute Engine e de balanceamento de carga para a implementação. O segundo passo de compilação imprime o endereço IP onde pode ver a aplicação em execução.

    14. Abra o endereço IP correspondente ao MIG verde num navegador. É apresentada uma captura de ecrã semelhante à seguinte que mostra a implementação:

      Implementação

    15. Aceda à 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 ver as quatro instâncias de VM:

      Abra 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:

      Abra a página Endereços IP externos

    Compreender o código

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

    • Código fonte relacionado com o script de configuração.
    • Código fonte relacionado com os pipelines do Cloud Build.
    • Código fonte relacionado com os modelos do Terraform.

    Script de configuração

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

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

      • Cria um novo repositório nos Cloud Source Repositories.
      • Copia o código fonte do repositório GitHub de exemplo para o novo repositório nos Cloud Source Repositories.
      • Cria os acionadores de compilação 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 ficheiros de configuração do Cloud Build que o script de configuração usa para configurar os recursos para o fluxo GitOps. apply.cloudbuild.yaml contém dois passos de compilação:

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

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

    As funções tf_install_in_cloud_build_step, tf_apply, describe_deployment e tf_destroy estão definidas no ficheiro bash_utils.sh. Os ficheiros de configuração de compilação 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 seguinte mostra a função tf_install_in_cloud_build_step definida em bash_utils.sh. Os ficheiros de configuração da compilação chamam esta função para instalar o Terraform rapidamente. Cria um contentor do Cloud Storage para registar o estado 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 seguinte fragmento do código mostra a função tf_apply definida em bash_utils.sh. 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 ficheiro 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 seguinte fragmento do código mostra a função describe_deployment definida em bash_utils.sh. Usa o gcloud compute addresses describe para obter os endereços IP dos balanceadores de carga através do nome e imprimi-los.

    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 seguinte fragmento do código mostra a função tf_destroy definida em bash_utils.sh. 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

    Vai encontrar todos os ficheiros de configuração e variáveis do Terraform na pasta copy-of-gcp-mig-simple/infra/.

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

    O seguinte fragmento do código mostra o conteúdo de infra/main.tfvars. Contém três variáveis: duas que determinam que versão da aplicação implementar nos conjuntos azul e verde, e uma variável para a cor ativa: azul ou verde. As alterações a este ficheiro acionam a implementação.

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

    Segue-se um fragmento do código de infra/main.tf. Neste fragmento:

    • Uma variável está definida para o Google Cloud projeto.
    • A Google está definida como fornecedor do Terraform.
    • Uma variável é definida para o espaço de nomes. Todos os objetos criados pelo Terraform têm o prefixo desta variável para que seja possível implementar várias versões da aplicação 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 associações para as variáveis no ficheiro 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 seguinte fragmento do código de infra/main.tf mostra a instanciação do módulo de divisão. Este módulo recebe a cor ativa para que o balanceador de carga do divisor saiba em que MIG implementar a aplicação.

    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 seguinte fragmento do código de infra/main.tf define dois módulos idênticos para MIGs azuis e verdes. 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 ficheiro splitter/main.tf define os objetos criados para o MIG do separador. Segue-se um fragmento de código de splitter/main.tf que contém a lógica para alternar entre o MIG verde e o azul. É suportado pelo serviço google_compute_region_backend_service, que pode encaminhar 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 encaminhar.

    O código seguinte encaminha 100% do tráfego para a cor especificada, mas pode atualizar este código para a implementação canary para encaminhar o tráfego para um subconjunto dos utilizadores.

    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 ficheiro mig/main.tf define os objetos relativos aos MIGs azuis e verdes. O seguinte fragmento do código deste ficheiro define o modelo de instância do Compute Engine que é usado para criar os conjuntos de VMs. Tenha em atenção que este modelo de instância tem a propriedade do ciclo de vida do Terraform definida como create_before_destroy. Isto acontece porque, quando atualiza a versão do conjunto, não pode usar o modelo para criar a nova versão dos conjuntos quando ainda está a ser usado pela versão anterior do conjunto. No entanto, se a versão mais antiga do conjunto for destruída antes de criar o novo modelo, vai existir um período em que os conjuntos estão inativos. Para evitar este cenário, definimos o ciclo de vida do Terraform como create_before_destroy para que a versão mais recente de um conjunto de VMs seja criada primeiro antes de a versão mais antiga ser 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 incorrer em custos na sua conta do Google Cloud pelos recursos usados neste tutorial, elimine o projeto que contém os recursos ou mantenha o projeto e elimine os recursos individuais.

    Elimine recursos individuais

    1. Elimine os recursos do Compute Engine criados pelo acionador de aplicação:

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

        Abra a página Acionadores

      2. Na tabela Acionadores, localize a linha correspondente ao acionador destroy e clique em Executar. Quando o acionador conclui a execução, os recursos criados pelo acionador apply são eliminados.

    2. Elimine os recursos criados durante o arranque executando o seguinte comando na janela do terminal:

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

    Elimine o projeto

      Delete a Google Cloud project:

      gcloud projects delete PROJECT_ID

    O que se segue?