Implantação contínua no Google Kubernetes Engine usando o Jenkins

Last reviewed 2019-02-27 UTC

Neste tutorial, mostraremos como configurar um pipeline de entrega contínua usando Jenkins e Google Kubernetes Engine (GKE), conforme descrito no diagrama a seguir.

Arquitetura de entrega contínua do Jenkins.

Objetivos

  • Compreender um aplicativo simples.
  • Implantar um aplicativo no GKE.
  • Fazer upload de código para o Cloud Source Repositories.
  • Criar pipelines de implantação no Jenkins.
  • Implantar ambientes de desenvolvimento.
  • Implantar uma versão canário.
  • Implantar ambientes de produção.

Custos

Neste tutorial, há componentes faturáveis do Google Cloud, entre eles:

  • Compute Engine
  • Google Kubernetes Engine
  • Cloud Source Repositories
  • Cloud Build

Use a calculadora de preços para gerar uma estimativa de custo baseada na projeção de uso deste tutorial. Usuários novos do GCP podem estar qualificados para uma avaliação gratuita.

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. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

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

  4. Enable the Compute Engine, GKE, and Cloud Build APIs.

    Enable the APIs

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

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

  7. Enable the Compute Engine, GKE, and Cloud Build APIs.

    Enable the APIs

Como preparar o ambiente

  1. Conclua o tutorial Como configurar o Jenkins no GKE. Certifique-se de que você tem uma instalação do Jenkins em execução no GKE.

  2. No Cloud Shell, clone o código de amostra:

    cd ~/
    git clone https://github.com/GoogleCloudPlatform/continuous-deployment-on-kubernetes
    
  3. Acesse o diretório do app de exemplo:

    cd ~/continuous-deployment-on-kubernetes/sample-app
    
  4. Aplique o papel cluster-admin à conta de serviço do Jenkins:

    kubectl create clusterrolebinding jenkins-deploy \
        --clusterrole=cluster-admin --serviceaccount=default:cd-jenkins
    

    Neste tutorial, a conta de serviço do Jenkins precisa das permissões de cluster-admin para criar namespaces do Kubernetes e outros recursos necessários ao aplicativo. Para uso em produção, catalogue as permissões individuais necessárias e aplique-as à conta de serviço individualmente.

Entender o aplicativo

Você implementará o aplicativo de amostra gceme no pipeline de implantação contínua. O aplicativo, escrito na linguagem Go, está no diretório sample-app do repositório. Quando você executa o binário gceme em uma instância do Compute Engine, o aplicativo exibe os metadados da instância em um card de informações.

Card de informações do gceme

O aplicativo simula um microsserviço aceitando dois modos de operação:

  • No modo de back-end, o gceme detecta a atividade na porta 8080 e retorna metadados da instância do Compute Engine no formato JSON.

  • No modo de front-end, o gceme consulta o serviço gceme de back-end e renderiza o JSON resultante na interface do usuário.

    Arquitetura do gceme

Os modos de front-end e back-end aceitam dois URLs adicionais:

  • /version imprime a versão em execução.
  • /healthz informa a integridade do aplicativo. No modo de front-end, a integridade será exibida como OK se o back-end for alcançável.

Como criar o app de exemplo

  1. Execute os seguintes comandos para criar o app de amostra:

    export PROJECT_ID=$(gcloud info --format='value(config.project)')
    
    find . -type f -name "*" -exec sed -i 's|gcr.io/cloud-solutions-images/gceme:1.0.0|gcr.io/'"${PROJECT_ID}"'/gceme:1.0.0|g' {} +
    
    gcloud builds submit --tag gcr.io/${PROJECT_ID}/gceme:1.0.0
    

Como implantar o aplicativo de amostra no Kubernetes

Implante o front-end e o back-end do gceme no Kubernetes usando arquivos de manifesto que descrevem o ambiente de implantação. Os arquivos usam uma imagem padrão que é atualizada posteriormente neste tutorial.

Implante os aplicativos em dois ambientes.

  • Produção. O site ativo que os usuários acessam.

  • Canário. Um site de capacidade menor que recebe uma porcentagem do tráfego do usuário. Use esse ambiente para verificar a integridade do software com tráfego em tempo real antes de lançá-lo no ambiente de produção.

Primeiro, implante o aplicativo no ambiente de produção para enviar o código de trabalho ao pipeline.

  1. Crie o namespace do Kubernetes para isolar logicamente a implantação da produção:

    kubectl create ns production
    
  2. Crie as implantações e serviços canário e de produção:

    kubectl --namespace=production apply -f k8s/production
    kubectl --namespace=production apply -f k8s/canary
    kubectl --namespace=production apply -f k8s/services
    
  3. Escalone os front-ends do ambiente de produção:

    kubectl --namespace=production scale deployment gceme-frontend-production --replicas=4
    
  4. Consulte o endereço IP externo dos serviços de produção: O endereço IP do balanceador de carga pode demorar alguns minutos para aparecer.

    kubectl --namespace=production get service gceme-frontend
    

    Quando o processo for concluído, o endereço IP externo será exibido na coluna EXTERNAL-IP.

    NAME             TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
    gceme-frontend   LoadBalancer   10.35.254.91   35.196.48.78   80:31088/TCP   1m
    

    Se o endereço IP externo não aparecer, aguarde alguns minutos e repita a etapa anterior até que o endereço IP externo seja exibido.

  5. Depois que o endereço IP externo for exibido, armazene o endereço IP do balanceador de carga do serviço de front-end em uma variável de ambiente:

    export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}"  --namespace=production services gceme-frontend)
    
  6. Para confirmar que os dois serviços estão funcionando, abra o endereço IP externo do front-end no navegador.

  7. Pesquise o URL /version do endpoint de produção para observar as atualizações graduais na próxima seção:

    while true; do curl http://$FRONTEND_SERVICE_IP/version; sleep 1;  done
    
  8. Pressione Ctrl+C para sair da repetição.

Crie um repositório para hospedar o código-fonte do aplicativo de amostra

Em seguida, crie uma cópia do aplicativo de amostra gceme e envie-o por push para o Cloud Source Repositories.

  1. Crie o repositório no Cloud Source Repositories:

    gcloud services enable sourcerepo.googleapis.com
    gcloud source repos create gceme
    
  2. Inicialize o repositório Git local:

    rm -rf ../.git
    git config --global init.defaultBranch main
    git init
    git config credential.helper gcloud.sh
    export PROJECT_ID=$(gcloud config get-value project)
    git remote add origin https://source.developers.google.com/p/$PROJECT_ID/r/gceme
    
  3. Defina o nome de usuário e o endereço de e-mail das confirmações do Git neste repositório para os valores da sua conta conectada:

    git config --global user.email $(gcloud config list account --format "value(core.account)")
    git config --global user.name $(gcloud config list account --format "value(core.account)")
    
  4. Adicione, confirme e envie os arquivos por push:

    git add .
    git commit -m "Initial commit"
    git push origin main
    

Criar uma conta de serviço

Nesta seção, você cria uma conta de serviço que o Jenkins usará para acessar seu repositório Git e executar implantações no GKE.

  1. Criar a conta de serviço e conceder papéis de origem e do GKE a ela.

    export SA=jenkins-sa
    export SA_EMAIL=${SA}@${PROJECT_ID}.iam.gserviceaccount.com
    
    gcloud iam service-accounts create $SA
    
    gcloud projects add-iam-policy-binding $PROJECT_ID \
      --member serviceAccount:$SA_EMAIL \
      --role roles/source.writer
    
    gcloud projects add-iam-policy-binding $PROJECT_ID \
      --member serviceAccount:$SA_EMAIL \
      --role roles/container.developer
    
  2. Crie e faça o download de uma chave de conta de serviço JSON para sua conta de serviço recém-criada:

    gcloud iam service-accounts keys create ~/jenkins-gke-key.json --iam-account $SA_EMAIL
    

    Anote onde o arquivo foi criado, porque você fará upload dele para o Jenkins em uma etapa posterior.

    Para mais informações sobre como fazer o download de uma chave de conta de serviço, consulte Criar uma chave de conta de serviço.

Abrir a interface do usuário da Web do Jenkins

  1. Configure o encaminhamento de portas se ainda não tiver feito isso para permitir o acesso à IU da Web do Jenkins:

    export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/component=jenkins-main" -l "app.kubernetes.io/instance=cd-jenkins" -o jsonpath="{.items[0].metadata.name}")
    kubectl port-forward $POD_NAME 8080:8080 >> /dev/null 2>&1 &
    
  2. Abra a interface do usuário do Jenkins, no Cloud Shell, clique em Visualização da Web e em Visualizar na porta 8080.

Configurar o Jenkins Cloud para o Kubernetes

  1. Na interface do usuário do Jenkins, selecione Gerenciar Jenkins > Gerenciar nós e nuvens.
  2. Clique em Configure Clouds (Configurar nuvens) no painel de navegação à esquerda.
  3. Clique em Add a new cloud (Adicionar uma nova nuvem) e selecione Kubernetes.
  4. Clique em Kubernetes Cloud Details.
  5. No campo URL do Jenkins, digite o seguinte valor: http://cd-jenkins:8080
  6. No campo Jenkins tunnel, insira o seguinte valor: cd-jenkins-agent:50000
  7. Clique em Save.

Como criar um pipeline

Use o Jenkins para definir e executar um pipeline para testar, criar e implantar a cópia do gceme no cluster do Kubernetes.

Adicionar as credenciais da sua conta de serviço

Configure as credenciais para permitir que o Jenkins acesse o repositório do código.

  1. Na interface do usuário do Jenkins, selecione Manage Jenkins > Manage Credentials no painel de navegação à esquerda.
  2. Clique no link (global).

    Grupos de credenciais do Jenkins

  3. Clique em Add Credentials, no menu de navegação à esquerda.

  4. No menu Tipo, selecione Conta de serviço do Google da chave privada.

  5. Insira o nome do projeto e selecione a chave JSON que foi criada em uma seção anterior.

  6. Clique em Criar.

Anote o nome da credencial para uso posterior neste tutorial.

Credenciais do Jenkins

Criar um job Jenkins

Em seguida, use o recurso Jenkins Pipeline para configurar o pipeline de criação. Os arquivos do Jenkins Pipeline são escritos com uma sintaxe semelhante à do Groovy.

Navegue até a interface do usuário do Jenkins e siga estas etapas para configurar um job de pipeline.

  1. Clique no link Jenkins na parte superior esquerda da interface.
  2. Clique no link New Item na navegação à esquerda.
  3. Nomeie o projeto como sample-app, escolha a opção Multibranch Pipeline e clique em OK.
  4. Na seção Branch Sources, clique em Add Source e selecione git.
  5. Cole o URL do clone HTTPS do repositório sample-app no Cloud Source Repositories no campo Repositório do projeto. Substitua [PROJECT_ID] pelo ID do projeto.

    https://source.developers.google.com/p/[PROJECT_ID]/r/gceme
    
  6. Na lista suspensa Credentials, selecione o nome das credenciais que você criou ao adicionar sua conta de serviço.

  7. Na seção Scan Multibranch Pipeline, selecione a caixa Periodically if not otherwise run. Defina o valor de Interval para "1 minute".

  8. Clique em Save.

    Crie configurações de job do Jenkins.

Depois que você concluir essas etapas, um job denominado "Branch indexing" será executado. Esse meta-job identifica os branches do repositório e garante que não ocorreram alterações nos branches existentes. Se você atualizar o Jenkins, o branch main exibirá esse job.

A primeira execução do job falha até você fazer algumas alterações de código na próxima etapa.

Modificar a definição de pipeline

  1. Crie um branch para o ambiente canário, denominado canary.

    git checkout -b canary
    
  2. Atualize o Jenkinsfile para substituir REPLACE_WITH_YOUR_PROJECT_ID na linha 2 pelo ID do projeto.

    sed -i 's|REPLACE_WITH_YOUR_PROJECT_ID|'"$PROJECT_ID"'|' Jenkinsfile
    

O contêiner Jenkinsfile que define esse pipeline é escrito com a sintaxe do Groovy para pipeline do Jenkins. O uso de um Jenkinsfile permite expressar todo um pipeline de criação em um único arquivo que subsiste com o código-fonte. Os canais aceitam recursos poderosos, como carregamento em paralelo e obrigatoriedade de aprovação manual do usuário.

Implantar uma versão canário

Agora que seu pipeline está configurado corretamente, é possível fazer uma alteração no aplicativo gceme e deixar o pipeline testá-lo, empacotá-lo e implantá-lo.

O ambiente canário é configurado como uma versão canário. Assim sendo, a alteração é lançada para uma pequena porcentagem desses pods que sustentam o balanceador de carga de produção. Isso é feito no Kubernetes com a manutenção de várias implantações que compartilham os mesmos rótulos. Para este aplicativo, os serviços gceme-frontend fazem o balanceamento de carga em todos os pods com os rótulos app: gceme e role: frontend. O arquivo de manifesto canário k8s/frontend-canary.yaml define as réplicas como 1 e inclui os rótulos necessários para o serviço gceme-frontend.

Atualmente, um dos cinco pods de front-end executa o código canário, enquanto os outros quatro executam o código de produção. Isso ajuda a garantir que o código canário não prejudique os usuários antes de reverter para a frota completa de pods.

  1. Abra html.go e substitua as duas instâncias de blue por orange.
  2. Abra main.go e altere o número da versão de 1.0.0 para 2.0.0:

    const version string = "2.0.0"
    
  3. Em seguida, adicione e confirme esses arquivos no repositório local:

    git add Jenkinsfile html.go main.go
    git commit -m "Version 2"
    
  4. Por fim, envie as alterações por push ao servidor Git remoto:

    git push origin canary
    
  5. Após o push da alteração ao repositório Git, navegue até a interface do usuário do Jenkins, onde é possível ver que a compilação foi iniciada.

    Tela de primeira criação do Jenkins.

  6. Depois que a versão estiver em execução, clique na seta para baixo ao lado dela na navegação à esquerda e selecione Console Output:

    Navegação no console do Jenkins.

  7. Durante a execução, você poderá notar o build esperando por um tempo com a seguinte mensagem:

    Still waiting to schedule task
    ‘Jenkins’ doesn’t have label ‘sample-app’
    

    Essa mensagem significa que o Jenkins está aguardando o GKE criar os pods necessários para as etapas do build. É possível acompanhar o progresso na página Cargas de trabalho do GKE.

  8. Acompanhe a saída do build. Quando o build terminar, pesquise o endpoint em /version:

    while true; do curl http://$FRONTEND_SERVICE_IP/version; sleep 1;  done
    

    A versão começa a mudar em algumas das solicitações. Agora você implantou essa alteração em um subconjunto de usuários.

    Pressione Ctrl+C para sair da repetição.

  9. Depois que a alteração é implantada no ambiente canário, continue implantando no restante dos usuários, mesclando o código com o branch main e enviando-os por push ao servidor Git.

    git checkout main
    git merge canary
    git push origin main
    
  10. Em aproximadamente 1 minuto, o job main na pasta sample-app é iniciado.

    Job principal do Jenkins.

  11. Clique no link main para mostrar as etapas do pipeline, bem como informações de aprovação/reprovação e características de tempo.

    Produção de pipeline do Jenkins.

  12. Pesquise o URL de produção para verificar se a nova versão 2.0.0 foi implantada e atende às solicitações de todos os usuários.

    export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" --namespace=production services gceme-frontend)
    while true; do curl http://$FRONTEND_SERVICE_IP/version; sleep 1;  done
    

É possível observar o arquivo Jenkins no projeto para ver o fluxo de trabalho. O Jenkinsfile está localizado em https://source.cloud.google.com/[PROJECT_ID]/gceme/+/main:Jenkinsfile.

Implantar um branch de desenvolvimento

Às vezes é preciso trabalhar com alterações não triviais que não podem ser enviadas por push diretamente ao ambiente canário. Um branch de desenvolvimento é um conjunto de ambientes que os desenvolvedores usam para testar as alterações de código antes de enviá-las para integração ao ambiente de produção. Esses ambientes são uma versão reduzida do aplicativo, mas são implantados usando os mesmos mecanismos que o ambiente de produção.

Se você quiser criar um ambiente para desenvolvedores em uma ramificação separada, precisará enviá-la por push ao servidor Git e deixar o Jenkins implantar o ambiente. Veja as etapas abaixo. Em um cenário de desenvolvimento, você não usaria um balanceador de carga voltado para o público. Para proteger o aplicativo, use o proxy kubectl. O proxy autentica-se na API Kubernetes e retransmite as solicitações da máquina local para o serviço no cluster sem expor o serviço à Internet.

  1. Na primeira janela do terminal, crie outra ramificação e a envie para o servidor Git:

    git checkout -b new-feature
    git push origin new-feature
    

    Abra a IU da Web do Jenkins e revise o resultado do console no job new-feature. Um novo job é criado, e o ambiente de desenvolvimento é o processo de criação. Na parte inferior da saída do console do job, há instruções para acessar o ambiente.

  2. Após a conclusão do build, inicie o proxy em segundo plano:

    kubectl proxy &
    
  3. Verifique se o aplicativo pode ser acessado usando localhost.

    curl http://localhost:8001/api/v1/namespaces/new-feature/services/gceme-frontend:80/proxy/
    
  4. Agora é possível enviar o código por push a esse branch para atualizar o ambiente de desenvolvimento. Quando terminar, mescle o branch de volta ao canary para implantar esse código no ambiente canário.

    git checkout canary
    git merge new-feature
    git push origin canary
    
  5. Quando você estiver seguro de que o código não causará problemas ao ambiente de produção, faça a mesclagem do branch canary com o branch main para iniciar a implantação:

    git checkout main
    git merge canary
    git push origin main
    
  6. Quando terminar com o branch de desenvolvimento, exclua-o do servidor e exclua o ambiente do cluster do Kubernetes:

    git push origin :new-feature
    kubectl delete ns new-feature
    

Limpar

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados no tutorial, exclua o projeto que os contém ou mantenha o projeto e exclua os recursos individuais.

Excluir o projeto

O jeito mais fácil de evitar cobranças é excluindo o projeto que você criou para o tutorial.

Para excluir o projeto:

  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.

A seguir