Entrega contínua no estilo GitOps com o Cloud Build


Nesta página, explicamos como criar um pipeline de integração e entrega contínuas (CI/CD, na sigla em inglês) no Google Cloud usando apenas produtos hospedados e a famosa metodologia GitOps.

Há muito tempo que os engenheiros do Google armazenam arquivos de configuração e implantação em nosso repositório de código-fonte principal. Esta metodologia é descrita no capítulo 8 do livro Engenharia de Confiabilidade do Site ( Beyer et. Al., 2016 ), e foi demonstrada por Kelsey Hightower durante sua apresentação feita no Google Cloud Next de 2017 (em inglês).

Uma parte fundamental do GitOps é a ideia de “ambientes como código”: descrever as implementações de maneira declarativa, usando arquivos, como manifestos do Kubernetes, armazenados em um repositório Git.

Neste tutorial, você vai criar um pipeline de CI/CD que gera automaticamente uma imagem de contêiner com base no código confirmado, armazena a imagem no Artifact Registry, atualiza um manifesto do Kubernetes em um repositório Git e implanta o aplicativo no Google Kubernetes Engine (GKE) usando esse manifesto.

Arquitetura do pipeline de CI/CD

Neste tutorial, usaremos dois repositórios Git:

  • Repositório do aplicativo: contém o código-fonte do próprio aplicativo.
  • Repositório do ambiente: contém os manifestos para a implantação no Kubernetes.

Quando você envia uma alteração para o repositório do app, o pipeline do Cloud Build executa testes, cria uma imagem de contêiner e a envia para o Artifact Registry. Depois de enviar a imagem, o Cloud Build atualiza o manifesto de implantação e o envia para o repositório do env. Isso aciona outro pipeline do Cloud Build que aplica o manifesto ao cluster do GKE e, se bem-sucedido, armazena o manifesto em outra ramificação do repositório do env.

Mantemos os repositórios do aplicativo e do ambiente separados porque eles têm utilidades e ciclos de vida diferentes. O repositório do aplicativo é dedicado a um aplicativo específico e é usado principalmente por pessoas reais. Os principais usuários do repositório do env são sistemas automatizados, como o Cloud Build, e ele pode ser compartilhado por vários aplicativos. O repositório do env pode ter várias ramificações, cada uma mapeando para um ambiente específico (você usará apenas o de produção neste tutorial). Ele cita uma imagem de contêiner específica, o que não acontece com o repositório do env.

Ao terminar este tutorial, você terá um sistema em que facilmente poderá:

  • distinguir entre implantações com falha e bem-sucedidas observando o histórico do Cloud Build;
  • acessar o manifesto usado atualmente observando o branch de produção do repositório do ambiente;
  • reverter para qualquer versão anterior bastando executar novamente a versão correspondente do Cloud Build.

Fluxo do pipeline de CI/CD

Sobre este tutorial

Neste tutorial, usamos o Cloud Source Repositories para hospedar repositórios do Git. No entanto, é possível chegar aos mesmos resultados com produtos de terceiros, como GitHub, Bitbucket ou GitLab.

Este pipeline não implementa um mecanismo de validação antes da implantação. Se você usa o GitHub, o Bitbucket ou o GitLab, é possível modificar o pipeline para usar uma solicitação de envio com essa finalidade.

Recomendamos o Spinnaker para as equipes que querem implementar padrões avançados de implantação (azul/verde, análise de teste canário, várias nuvens etc.). No entanto, o conjunto de recursos dessa solução talvez não seja necessário para que organizações e projetos menores tenham uma estratégia de CI/CD bem-sucedida. Neste tutorial, você aprenderá como criar um ajuste de pipeline de CI/CD para aplicativos hospedados no GKE com ferramentas.

Para simplificar, este tutorial usa um único ambiente, o de produção, no repositório do env, mas é possível estendê-lo à implantação em vários ambientes, se necessário.

Objetivos

  • Criar repositórios Git no Cloud Source Repositories.
  • Criar uma imagem de contêiner com o Cloud Build e armazená-la no Artifact Registry.
  • Criar um pipeline de CI.
  • Criar um pipeline de CD.
  • Testar o pipeline de CI/CD.

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. Selecione ou crie um projeto do Google Cloud.

    Acessar "Gerenciar recursos"

  2. Ative o faturamento no projeto.

    Ativar faturamento

  3. Abra o Cloud Shell para executar os comandos listados neste tutorial. O Cloud Shell é um ambiente shell interativo para o Google Cloud que permite gerenciar projetos e recursos a partir do navegador da Web.

    Acessar o Cloud Shell

  4. Se o comando gcloud config get-value project não retornar o ID do projeto que foi selecionado, configure o Cloud Shell para usar seu projeto.

    gcloud config set project [PROJECT_ID]
    
  5. No Cloud Shell, ative as APIs necessárias.

    gcloud services enable container.googleapis.com \
        cloudbuild.googleapis.com \
        sourcerepo.googleapis.com \
        artifactregistry.googleapis.com
    
  6. Crie um repositório do Docker do Artifact Registry chamado my-repository na região us-central1 para armazenar as imagens de contêiner.

    gcloud artifacts repositories create my-repository \
      --repository-format=docker \
      --location=us-central1
    
  7. No Cloud Shell, crie um cluster do GKE que você usará para implantar o aplicativo de amostra deste tutorial.

    Piloto automático

    Crie um cluster do Autopilot chamado hello-cloudbuild:

    gcloud container clusters create-auto hello-cloudbuild \
        --region us-central1
    

    Padrão

    Crie um cluster padrão de um nó chamado hello-cloudbuild:

    gcloud container clusters create hello-cloudbuild \
        --num-nodes 1 --region us-central1
    
  8. Se você nunca usou o Git no Cloud Shell, configure-o com seu nome e endereço de e-mail. O Git usará essas informações para identificar você como autor das confirmações a serem criadas no Cloud Shell.

    git config --global user.email "YOUR_EMAIL_ADDRESS"
    git config --global user.name "YOUR_NAME"
    

Ao concluir este tutorial, exclua os recursos criados para evitar o faturamento contínuo. Veja mais detalhes em Como fazer a limpeza.

Como criar repositórios Git no Cloud Source Repositories

Nesta seção, você criará os dois repositórios Git, do aplicativo e do ambiente, que serão usados neste tutorial e inicializará o repositório do aplicativo com um código de amostra.

  1. No Cloud Shell, crie os dois repositórios Git.

    gcloud source repos create hello-cloudbuild-app
    gcloud source repos create hello-cloudbuild-env
    
  2. Clone o código de amostra do GitHub.

    cd ~
    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    
  3. Configure o Cloud Source Repositories como remoto.

    PROJECT_ID=$(gcloud config get-value project)
    git remote add google \
        "https://source.developers.google.com/p/${PROJECT_ID}/r/hello-cloudbuild-app"
    

O código clonado contém um aplicativo "Hello World".

from flask import Flask
app = Flask('hello-cloudbuild')

@app.route('/')
def hello():
  return "Hello World!\n"

if __name__ == '__main__':
  app.run(host = '0.0.0.0', port = 8080)

Como criar uma imagem de contêiner com o Cloud Build

O código que você clonou contém o Dockerfile a seguir.

FROM python:3.7-slim
RUN pip install flask
WORKDIR /app
COPY app.py /app/app.py
ENTRYPOINT ["python"]
CMD ["/app/app.py"]

Com esse Dockerfile, crie uma imagem de contêiner com o Cloud Build e armazene-a no Artifact Registry.

  1. No Cloud Shell, crie uma versão do Cloud Build com base na confirmação mais recente usando o comando abaixo.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    COMMIT_ID="$(git rev-parse --short=7 HEAD)"
    gcloud builds submit --tag="us-central1-docker.pkg.dev/${PROJECT_ID}/my-repository/hello-cloudbuild:${COMMIT_ID}" .
    

    O Cloud Build faz o streaming dos registros gerados pela criação da imagem do contêiner para seu terminal quando você executa esse comando.

  2. Depois de concluir a versão, verifique se a nova imagem do contêiner está realmente disponível no Artifact Registry.

    Acessar o Artifact Registry

    Imagem hello-cloudbuild no Artifact Registry

Como criar o pipeline de integração contínua

Nesta seção, você configurará o Cloud Build para executar automaticamente um teste de unidade pequeno, construirá a imagem do contêiner e, por fim, enviará essa imagem para o Artifact Registry. Enviar uma nova confirmação para o Cloud Source Repositories aciona automaticamente esse pipeline. O arquivo cloudbuild.yaml incluído no código é a configuração do pipeline.

steps:
# This step runs the unit tests on the app
- name: 'python:3.12-slim'
  id: Test
  entrypoint: /bin/sh
  args:
  - -c
  - 'pip install flask && python test_app.py -v'

# This step builds the container image.
- name: 'gcr.io/cloud-builders/docker'
  id: Build
  args:
  - 'build'
  - '-t'
  - 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:$SHORT_SHA'
  - '.'

# This step pushes the image to Artifact Registry
# The PROJECT_ID and SHORT_SHA variables are automatically
# replaced by Cloud Build.
- name: 'gcr.io/cloud-builders/docker'
  id: Push
  args:
  - 'push'
  - 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:$SHORT_SHA'
  1. Abra a página Gatilhos do Cloud Build.

    Acessar acionadores

  2. Clique em Criar gatilho.

  3. Preencha as opções a seguir:

    • No campo Nome, digite hello-cloudbuild.
    • Em Evento, selecione Mover para uma ramificação.
    • Em Origem, selecione hello-cloudbuild-app como Repositório e ^master$ como Ramificação.
    • Em Configuração da compilação, selecione arquivo de configuração do Cloud Build.
    • No campo local do arquivo de configuração do Cloud Build, digite cloudbuild.yaml depois do /.
  4. Clique em Criar para salvar o gatilho de compilação.

    Dica: se você precisar criar acionadores de versão para vários projetos, use a API Build Triggers.

  5. No Cloud Shell, envie o código do aplicativo para o Cloud Source Repositories para acionar o pipeline de CI no Cloud Build.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git push google master
    
  6. Abra o console do Cloud Build.

    Acessar o Cloud Build

    Suas versões recém-executadas e concluídas serão exibidas. Se quiser, clique na versão para acompanhar a execução e examinar os registros.

Como criar o pipeline de entrega contínua

O Cloud Build também é usado para o pipeline de entrega contínua. Esse pipeline é executado sempre que uma confirmação é enviada para o branch candidato do repositório hello-cloudbuild-env. O pipeline aplica a versão nova do manifesto ao cluster do Kubernetes e, se bem-sucedido, copia o manifesto para o branch de produção. Esse processo tem as seguintes propriedades:

  • O branch candidato é um histórico das tentativas de implantação.
  • O branch de produção é um histórico das implantações bem-sucedidas.
  • É possível ver as implantações bem-sucedidas e com falha no Cloud Build.
  • É possível reverter para qualquer implantação anterior, bastando executar novamente a versão correspondente no Cloud Build. A reversão também atualiza o branch de produção para refletir fielmente o histórico de implantações.

Você modificará o pipeline de integração contínua para atualizar o branch candidato do repositório hello-cloudbuild-env, acionando o pipeline de entrega contínua.

Como conceder acesso do GKE ao Cloud Build

Para implantar o aplicativo no cluster do Kubernetes, o Cloud Build precisa do papel de gerenciamento de identidade e acesso do desenvolvedor do Kubernetes Engine.

Shell

No Cloud Shell, execute o comando a seguir:

PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} --format='get(projectNumber)')"
gcloud projects add-iam-policy-binding ${PROJECT_NUMBER} \
    --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
    --role=roles/container.developer

Console

  1. No Console do Google Cloud, abra a página "Configurações do Cloud Build":

    Abrir as configurações do Cloud Build

    A página Permissões da conta de serviço é exibida:

    Captura de tela da página de permissões da conta de serviço

  2. Defina o status do papel Desenvolvedor do Kubernetes Engine como Ativado.

Como inicializar o repositório hello-cloudbuild-env

É necessário inicializar o repositório hello-cloudbuild-env com dois branches, de hello-cloudbuild-env e hello-cloudbuild-env, e um arquivo de configuração do Cloud Build que descreve o processo de implantação.

  1. No Cloud Shell, clone o repositório hello-cloudbuild-env e crie o branch de hello-cloudbuild-env.

    cd ~
    gcloud source repos clone hello-cloudbuild-env
    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git checkout -b production
    
  2. Copie o arquivo cloudbuild-delivery.yaml disponível no repositório hello-cloudbuild-app e confirme a alteração.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    cp ~/hello-cloudbuild-app/cloudbuild-delivery.yaml ~/kubernetes-engine-samples/management/gitops-style-delivery/cloudbuild.yaml
    git add .
    git commit -m "Create cloudbuild.yaml for deployment"
    

    O arquivo cloudbuild-delivery.yaml descreve o processo de implantação a ser executado no Cloud Build. Isso é feito em duas etapas:

    1. O Cloud Build aplica o manifesto no cluster do GKE.

    2. Se bem-sucedido, o Cloud Build copia o manifesto no branch de produção.

    steps:
    # This step deploys the new version of our container image
    # in the hello-cloudbuild Kubernetes Engine cluster.
    - name: 'gcr.io/cloud-builders/kubectl'
      id: Deploy
      args:
      - 'apply'
      - '-f'
      - 'kubernetes.yaml'
      env:
      - 'CLOUDSDK_COMPUTE_REGION=us-central1'
      - 'CLOUDSDK_CONTAINER_CLUSTER=hello-cloudbuild'
    
    # This step copies the applied manifest to the production branch
    # The COMMIT_SHA variable is automatically
    # replaced by Cloud Build.
    - name: 'gcr.io/cloud-builders/git'
      id: Copy to production branch
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        set -x && \
        # Configure Git to create commits with Cloud Build's service account
        git config user.email $(gcloud auth list --filter=status:ACTIVE --format='value(account)') && \
        # Switch to the production branch and copy the kubernetes.yaml file from the candidate branch
        git fetch origin production && git checkout production && \
        git checkout $COMMIT_SHA kubernetes.yaml && \
        # Commit the kubernetes.yaml file with a descriptive commit message
        git commit -m "Manifest from commit $COMMIT_SHA
        $(git log --format=%B -n 1 $COMMIT_SHA)" && \
        # Push the changes back to Cloud Source Repository
        git push origin production
  3. Crie um branch candidato e envie os dois branches para que fiquem disponíveis no Cloud Source Repositories.

    git checkout -b candidate
    git push origin production
    git push origin candidate
    
  4. Conceda o papel de IAM de Gravador do repositório de origem à conta de serviço do Cloud Build para o repositório hello-cloudbuild-env.

    PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} \
        --format='get(projectNumber)')"
    cat >/tmp/hello-cloudbuild-env-policy.yaml <<EOF
    bindings:
    - members:
      - serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com
      role: roles/source.writer
    EOF
    gcloud source repos set-iam-policy \
        hello-cloudbuild-env /tmp/hello-cloudbuild-env-policy.yaml
    

Como criar o gatilho do pipeline de entrega contínua

Nesta seção, você configurará o Cloud Build para que seja acionado por um envio no branch candidato do repositório hello-cloudbuild-env.

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

    Acessar acionadores

  2. Clique em Criar gatilho.

  3. Preencha as opções a seguir:

    • No campo Nome, digite hello-cloudbuild-deploy.
    • Em Evento, selecione Mover para uma ramificação.
    • Em Origem, selecione hello-cloudbuild-env como Repositório e ^candidate$ como Ramificação.
    • Em Configuração, selecione arquivo de configuração do Cloud Build (yaml ou json).
    • No campo local do arquivo de configuração do Cloud Build, digite cloudbuild.yaml depois do /.
  4. Clique em Criar.

Como modificar o pipeline de integração contínua para acionar o pipeline de entrega contínua

Nesta seção, você adicionará algumas etapas ao pipeline de integração contínua que gerará uma nova versão do manifesto do Kubernetes e, depois, enviará esse manifesto para o repositório hello-cloudbuild-env para acionar o pipeline de entrega contínua.

  1. Substitua o arquivo cloudbuild.yaml pelo exemplo estendido no arquivo cloudbuild-trigger-cd.yaml.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    cp cloudbuild-trigger-cd.yaml cloudbuild.yaml
    

    O cloudbuild-trigger-cd.yaml é uma versão estendida do arquivo cloudbuild.yaml. Ele adiciona etapas para gerar o novo manifesto do Kubernetes e acionar o pipeline de entrega contínua.

    # This step clones the hello-cloudbuild-env repository
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Clone env repository
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        gcloud source repos clone hello-cloudbuild-env && \
        cd hello-cloudbuild-env && \
        git checkout candidate && \
        git config user.email $(gcloud auth list --filter=status:ACTIVE --format='value(account)')
    
    # This step generates the new manifest
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Generate manifest
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
         sed "s/GOOGLE_CLOUD_PROJECT/${PROJECT_ID}/g" kubernetes.yaml.tpl | \
         sed "s/COMMIT_SHA/${SHORT_SHA}/g" > hello-cloudbuild-env/kubernetes.yaml
    
    # This step pushes the manifest back to hello-cloudbuild-env
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Push manifest
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        set -x && \
        cd hello-cloudbuild-env && \
        git add kubernetes.yaml && \
        git commit -m "Deploying image us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:${SHORT_SHA}
        Built from commit ${COMMIT_SHA} of repository hello-cloudbuild-app
        Author: $(git log --format='%an <%ae>' -n 1 HEAD)" && \
        git push origin candidate
    
  2. Confirme as modificações e envie-as para o Cloud Source Repositories.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git add cloudbuild.yaml
    git commit -m "Trigger CD pipeline"
    git push google master
    

    Isso aciona o pipeline de integração contínua no Cloud Build.

  3. Examine a versão de integração contínua.

    Acessar o Cloud Build

    Suas versões executadas e concluídas recentemente para o repositório hello-cloudbuild-app são exibidas. Se quiser, clique na versão para acompanhar a execução e examinar os registros. A última etapa desse pipeline envia o manifesto novo para o repositório hello-cloudbuild-env, que aciona o pipeline de entrega contínua.

  4. Examine a versão de entrega contínua.

    Acessar o Cloud Build

    Suas versões executadas e concluídas recentemente para o repositório hello-cloudbuild-env são exibidas. Se quiser, clique na versão para acompanhar a execução e examinar os registros.

Como testar o pipeline completo

O pipeline de CI/CD completo agora está configurado. Nesta seção, você testará o pipeline inteiro.

  1. Acesse a página “Serviços” do GKE.

    ACESSAR A PÁGINA “SERVIÇOS” DO GOOGLE KUBERNETES ENGINE

    A lista contém um único serviço denominado hello-cloudbuild, criado pela versão de entrega contínua concluída recentemente.

  2. Clique no endpoint do serviço hello-cloudbuild. "Hello World!" será exibido. Se não houver um endpoint ou ocorrer um erro no balanceador de carga, talvez seja necessário aguardar alguns minutos até que o balanceador de carga seja totalmente inicializado. Clique em Atualizar para atualizar a página, se necessário.

  3. No Cloud Shell, substitua “Hello World” por “Hello Cloud Build” no aplicativo e no teste de unidade.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    sed -i 's/Hello World/Hello Cloud Build/g' app.py
    sed -i 's/Hello World/Hello Cloud Build/g' test_app.py
    
  4. Confirme e envie a alteração para o Cloud Source Repositories.

    git add app.py test_app.py
    git commit -m "Hello Cloud Build"
    git push google master
    

    Isso aciona o pipeline de CI/CD inteiro.

  5. Após alguns minutos, atualize o aplicativo no seu navegador. "Hello Cloud Build!" será exibido.

Como testar a reversão

Nesta seção, você voltará para a versão do aplicativo que dizia "Hello World!".

  1. Abra o console do Cloud Build no repositório hello-cloudbuild-env.

    Acessar o Cloud Build

  2. Clique na segunda versão mais recente disponível.

  3. Clique em Recriar.

  4. Quando a versão estiver concluída, atualize o aplicativo no seu navegador. "Hello World!" aparecerá novamente.

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.

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Como excluir os recursos

Se você quiser manter o projeto do Google Cloud usado neste tutorial, exclua os recursos individuais:

  1. Exclua os repositórios Git locais.

    cd ~
    rm -rf ~/hello-cloudbuild-app
    rm -rf ~/hello-cloudbuild-env
    
  2. Exclua os repositórios Git no Cloud Source Repositories.

    gcloud source repos delete hello-cloudbuild-app --quiet
    gcloud source repos delete hello-cloudbuild-env --quiet
    
  3. Exclua os acionadores do Cloud Build.

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

      Acessar acionadores

    2. Para cada gatilho, clique em Mais e, depois, em Excluir.

  4. Exclua o repositório do Docker no Artifact Registry.

    gcloud artifacts repositories delete my-repository \
        --location=us-central1
    
  5. Remova a permissão concedida ao Cloud Build para se conectar ao GKE.

    PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} \
        --format='get(projectNumber)')"
    gcloud projects remove-iam-policy-binding ${PROJECT_NUMBER} \
        --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
        --role=roles/container.developer
    
  6. Exclua o cluster do GKE.

    gcloud container clusters delete hello-cloudbuild \
       --region us-central1
    

A seguir