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). O termo “GitOps” foi cunhado pela Weaveworks. 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 ambiente. 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 ambiente.

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 ambiente são sistemas automatizados, como o Cloud Build, e ele pode ser compartilhado por vários aplicativos. O repositório do ambiente 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 aplicativo.

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 ambiente, 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 Limpar.

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/gke-gitops-tutorial-cloudbuild \
        hello-cloudbuild-app
    
  3. Configure o Cloud Source Repositories como remoto.

    cd ~/hello-cloudbuild-app
    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 ~/hello-cloudbuild-app
    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.7-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 ~/hello-cloudbuild-app
    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 produção e candidato, 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 produção.

    cd ~
    gcloud source repos clone hello-cloudbuild-env
    cd ~/hello-cloudbuild-env
    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 ~/hello-cloudbuild-env
    cp ~/hello-cloudbuild-app/cloudbuild-delivery.yaml ~/hello-cloudbuild-env/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 ~/hello-cloudbuild-app
    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 ~/hello-cloudbuild-app
    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 ~/hello-cloudbuild-app
    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.

Limpeza

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. No Console do Google Cloud, acesse a página Gerenciar recursos.

    Acessar "Gerenciar recursos"

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

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