Entrega contínua ao estilo GitOps com o Cloud Build

Esta página explica como criar um pipeline de integração e entrega contínuas (CI/CD) no Google Cloud usando apenas produtos alojados e a popular metodologia GitOps.

Os engenheiros da Google armazenam ficheiros de configuração e implementação no nosso repositório de código-fonte principal há muito tempo. Esta metodologia é descrita no livro Site Reliability Engineering, capítulo 8 (Beyer et al., 2016) e foi demonstrado por Kelsey Hightower durante o seu discurso de abertura do Google Cloud Next '17.

Uma parte fundamental do GitOps é a ideia de "ambientes como código": descrever as suas implementações de forma declarativa através de ficheiros (por exemplo, manifestos do Kubernetes) armazenados num repositório Git.

Neste tutorial, vai criar um pipeline de CI/CD que cria automaticamente uma imagem de contentor a partir de código comprometido, armazena a imagem no Artifact Registry, atualiza um manifesto do Kubernetes num repositório Git e implementa a aplicação no Google Kubernetes Engine (GKE) através desse manifesto.

Arquitetura do pipeline de CI/CD

Este tutorial usa dois repositórios Git:

  • repositório de apps: contém o código-fonte da própria aplicação
  • repositório env: contém os manifestos para a implementação do Kubernetes

Quando envia uma alteração para o repositório da app, o pipeline do Cloud Build executa testes, cria uma imagem de contentor e envia-a para o Artifact Registry. Depois de enviar a imagem, o Cloud Build atualiza o manifesto de implementação e envia-o para o repositório env. Isto aciona outro pipeline do Cloud Build que aplica o manifesto ao cluster do GKE e, se for bem-sucedido, armazena o manifesto noutro ramo do repositório env.

Mantemos os repositórios app e env separados porque têm ciclos de vida e utilizações diferentes. Os principais utilizadores do repositório da app são pessoas reais e este repositório é dedicado a uma aplicação específica. Os principais utilizadores do repositório env são sistemas automáticos (como o Cloud Build) e este repositório pode ser partilhado por várias aplicações. O repositório env pode ter várias ramificações que mapeiam cada uma para um ambiente específico (neste tutorial, só usa a produção) e referenciam uma imagem de contentor específica, ao passo que o repositório app não o faz.

Quando terminar este tutorial, terá um sistema no qual pode facilmente:

  • Distinguir entre implementações com falhas e bem-sucedidas consultando o histórico do Cloud Build
  • Aceda ao manifesto atualmente usado consultando o ramo production do repositório env,
  • Reverta para qualquer versão anterior reexecutando a compilação do Cloud Build correspondente.

Fluxo do pipeline de CI/CD

Acerca deste tutorial

Este tutorial usa os Cloud Source Repositories para alojar repositórios Git, mas pode alcançar os mesmos resultados com outros produtos de terceiros, como o GitHub, o Bitbucket ou o GitLab.

Este pipeline não implementa um mecanismo de validação antes da implementação. Se usar o GitHub, o Bitbucket ou o GitLab, pode modificar o pipeline para usar um pedido de envio para este fim.

Embora recomendemos o Spinnaker às equipas que querem implementar padrões de implementação avançados (azul/verde, análise de canários, multinuvem, etc.), o respetivo conjunto de funcionalidades pode não ser necessário para uma estratégia de CI/CD bem-sucedida para organizações e projetos mais pequenos. Neste tutorial, vai aprender a criar um pipeline de CI/CD adequado para aplicações alojadas no GKE com ferramentas.

Para simplificar, este tutorial usa um único ambiente, o de produção, no repositório env, mas pode expandi-lo para implementar em vários ambientes, se necessário.

Objetivos

  • Crie repositórios Git nos Cloud Source Repositories.
  • Crie uma imagem de contentor com o Cloud Build e armazene-a no Artifact Registry.
  • Crie um pipeline de CI.
  • Crie um pipeline de CD.
  • Teste o pipeline de CI/CD.

Custos

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

Para gerar uma estimativa de custos com base na sua utilização projetada, 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. Selecione ou crie um Google Cloud projeto.

    Aceda a Gerir recursos

  2. Ative a faturação para o seu projeto.

    Ative a faturação

  3. Abra o Cloud Shell para executar os comandos indicados neste tutorial. O Cloud Shell é um ambiente de shell interativo para Google Cloud que lhe permite gerir os seus projetos e recursos a partir do navegador de Internet.

    Aceda ao Cloud Shell

  4. Se o comando gcloud config get-value project não devolver o ID do projeto selecionado, configure o Cloud Shell para usar o 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 Docker do Artifact Registry denominado my-repository na região us-central1 para armazenar as suas imagens de contentores.

    gcloud artifacts repositories create my-repository \
      --repository-format=docker \
      --location=us-central1
    
  7. No Cloud Shell, crie um cluster do GKE que vai usar para implementar a aplicação de exemplo deste tutorial.

    Piloto automático

    Crie um cluster do Autopilot com o nome hello-cloudbuild:

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

    Standard

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

    gcloud container clusters create hello-cloudbuild \
        --num-nodes 1 --location us-central1
    
  8. Se nunca usou o Git no Cloud Shell, configure-o com o seu nome e endereço de email. O Git vai usá-las para se identificar como o autor das confirmações que vai criar na Cloud Shell.

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

Quando terminar este tutorial, pode evitar a faturação contínua eliminando os recursos que criou. Consulte o artigo Limpar para ver mais detalhes.

Criar os repositórios Git nos Cloud Source Repositories

Nesta secção, cria os dois repositórios Git (app e env) usados neste tutorial e inicializa o app com algum código de exemplo.

  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 exemplo de código do GitHub.

    cd ~
    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    
  3. Configure os Cloud Source Repositories como um 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 que clonou contém uma aplicação "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)

Criar uma imagem de contentor com o Cloud Build

O código que clonou contém o seguinte Dockerfile.

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

Com este Dockerfile, pode criar uma imagem de contentor com o Cloud Build e armazená-la no Artifact Registry.

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

    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 transmite os registos gerados pela criação da imagem do contentor para o seu terminal quando executa este comando.

  2. Após a conclusão da compilação, verifique se a nova imagem do contentor está disponível no Artifact Registry.

    Aceda ao Artifact Registry

    Imagem hello-cloudbuild no Artifact Registry

Criar o pipeline de integração contínua

Nesta secção, configura o Cloud Build para executar automaticamente um pequeno teste de unidade, compilar a imagem do contentor e, em seguida, enviá-la para o Artifact Registry. O envio de uma nova consolidação para os Cloud Source Repositories aciona automaticamente este pipeline. O ficheiro 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.13-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 Acionadores do Cloud Build.

    Aceda a Acionadores

  2. Clique em Criar acionador.

  3. Preencha as seguintes opções:

    • No campo Nome, escreva hello-cloudbuild.
    • Em Evento, selecione Enviar para uma ramificação.
    • Em Origem, selecione hello-cloudbuild-app como repositório e ^master$ como ramo.
    • Em Configuração de compilação, selecione Ficheiro de configuração do Cloud Build.
    • No campo Localização do ficheiro de configuração do Cloud Build, escreva cloudbuild.yaml após /.
  4. Clique em Criar para guardar o acionador de compilação.

    Sugestão: se precisar de criar acionadores de versão para muitos projetos, pode usar a API Build Triggers.

  5. No Cloud Shell, envie o código da aplicação para os 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 a consola do Cloud Build.

    Aceda ao Cloud Build

    São apresentadas as compilações executadas e concluídas recentemente. Pode clicar numa compilação para acompanhar a respetiva execução e examinar os respetivos registos.

Criar o pipeline de entrega contínua

O Cloud Build também é usado para o pipeline de entrega contínua. O pipeline é executado sempre que um commit é enviado para o ramo candidate do repositório hello-cloudbuild-env. O pipeline aplica a nova versão do manifesto ao cluster do Kubernetes e, se for bem-sucedido, copia o manifesto para o ramo de produção. Este processo tem as seguintes propriedades:

  • O ramo candidato é um histórico das tentativas de implementação.
  • O ramo production é um histórico das implementações bem-sucedidas.
  • Tem uma vista das implementações bem-sucedidas e falhadas no Cloud Build.
  • Pode reverter para qualquer implementação anterior executando novamente a compilação correspondente no Cloud Build. Uma reversão também atualiza o ramo de produção para refletir fielmente o histórico de implementações.

Vai modificar o pipeline de integração contínua para atualizar a ramificação candidate do repositório hello-cloudbuild-env, acionando o pipeline de entrega contínua.

Conceder acesso do Cloud Build ao GKE

Para implementar a aplicação no seu cluster do Kubernetes, o Cloud Build precisa da função de gestão de identidades e acessos do Kubernetes Engine Developer.

Shell

No Cloud Shell, execute o seguinte comando:

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

Consola

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

    Aceda a Autorizações

  2. Defina o estado da função Kubernetes Engine Developer como Ativar.

A inicializar o repositório hello-cloudbuild-env

Tem de inicializar o repositório hello-cloudbuild-env com dois ramos (production e candidate) e um ficheiro de configuração do Cloud Build que descreva o processo de implementação.

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

    cd ~
    gcloud source repos clone hello-cloudbuild-env
    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git checkout -b production
    
  2. Copie o ficheiro 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 ficheiro cloudbuild-delivery.yaml descreve o processo de implementação a ser executado no Cloud Build. Tem dois passos:

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

    2. Se tiver êxito, o Cloud Build copia o manifesto para o ramo production.

    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 ramo candidato e envie ambos os ramos para que fiquem disponíveis nos Cloud Source Repositories.

    git checkout -b candidate
    git push origin production
    git push origin candidate
    
  4. Conceda a função do IAM Source Repository Writer à 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
    

Criar o acionador para o pipeline de entrega contínua

Nesta secção, configura o Cloud Build para ser acionado por um push para o ramo candidate do repositório hello-cloudbuild-env.

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

    Aceda a Acionadores

  2. Clique em Criar acionador.

  3. Preencha as seguintes opções:

    • No campo Nome, escreva hello-cloudbuild-deploy.
    • Em Evento, selecione Enviar para uma ramificação.
    • Em Origem, selecione hello-cloudbuild-env como repositório e ^candidate$ como ramo.
    • Em Configuração, selecione Ficheiro de configuração do Cloud Build (YAML ou JSON).
    • No campo Localização do ficheiro de configuração do Cloud Build, escreva cloudbuild.yaml após /.
  4. Clique em Criar.

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

Nesta secção, adiciona alguns passos ao pipeline de integração contínua que gera uma nova versão do manifesto do Kubernetes e a envia para o repositório hello-cloudbuild-env para acionar o pipeline de entrega contínua.

  1. Substitua o ficheiro cloudbuild.yaml pelo exemplo expandido no ficheiro cloudbuild-trigger-cd.yaml.

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

    O ficheiro cloudbuild-trigger-cd.yaml é uma versão expandida do ficheiro cloudbuild.yaml. Adiciona passos 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 os 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
    

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

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

    Aceda ao Cloud Build

    São apresentadas as compilações executadas e concluídas recentemente para o repositório hello-cloudbuild-app. Pode clicar numa compilação para acompanhar a respetiva execução e examinar os respetivos registos. O último passo desta conduta envia o novo manifesto para o repositório hello-cloudbuild-env, o que aciona a conduta de entrega contínua.

  4. Examine a compilação de entrega contínua.

    Aceda ao Cloud Build

    São apresentadas as compilações executadas e concluídas recentemente para o repositório hello-cloudbuild-env. Pode clicar numa compilação para acompanhar a respetiva execução e examinar os respetivos registos.

Testar a pipeline completa

O pipeline de CI/CD completo está agora configurado. Nesta secção, testa-o de forma integral.

  1. Aceda à página Serviços do GKE.

    Aceda aos serviços do Google Kubernetes Engine

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

  2. Clique no ponto final do serviço hello-cloudbuild. "Hello World!" é apresentado. Se não existir nenhum ponto final ou se vir um erro do equilibrador de carga, pode ter de aguardar alguns minutos para que o equilibrador de carga seja completamente inicializado. Clique em Atualizar para atualizar a página, se necessário.

  3. No Cloud Shell, substitua "Hello World" por "Hello Cloud Build", tanto na aplicação como 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 os Cloud Source Repositories.

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

    Isto aciona a pipeline de CI/CD completa.

  5. Após alguns minutos, atualize a aplicação no navegador. É apresentada a mensagem "Hello Cloud Build!".

Testar a reversão

Nesta secção, reverte para a versão da aplicação que dizia "Hello World!".

  1. Abra a consola do Cloud Build para o repositório hello-cloudbuild-env.

    Aceda ao Cloud Build

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

  3. Clique em Reconstruir.

  4. Quando a compilação estiver concluída, recarregue a aplicação no navegador. "Hello World!" volta a aparecer.

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.

  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.

Eliminar os recursos

Se quiser manter o Google Cloud projeto que usou neste tutorial, elimine os recursos individuais:

  1. Elimine os repositórios Git locais.

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

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

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

      Aceda a Acionadores

    2. Para cada acionador, clique em Mais , e, de seguida, em Eliminar.

  4. Elimine o repositório Docker no Artifact Registry.

    gcloud artifacts repositories delete my-repository \
        --location=us-central1
    
  5. Remova a autorização concedida ao Cloud Build para se ligar 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. Elimine o cluster do GKE.

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

O que se segue?