Com a adoção de microsserviços e containers o processo de deployment de uma aplicação se torna mais trabalhoso. Fazer o build das imagens e deployment para produção manualmente é tedioso e leva tempo. É muito importante ter um pipeline de build e deployment automatizado (Continuous Delivery).

Esse post descreve uma das abordagens utilizadas na Mobicare para Continous Delivery usando a ferramenta AWS CodeBuild para microsserviços em Java Boot, Maven, Docker e ECS integrada ao BitBucket.

GitFlow & WebHooks

Além do pipeline de build e deployment, também vamos descrever brevemente nosso processo de controle de versão de código e como usamos os WebHooks para iniciar o progresso de build.

Utilizamos o BitBucket como ferramenta de SCM e temos em cada projeto branches de desenvolvimento (develop) e de produção (master), além de branches de feature, hotfix e release. Esses branches especiais (develop e master) possuem restrições que negam qualquer push, fazendo com que apenas Pull Requests (PR) possam modificar esses branches.

Essa configuração é definida no menu Settings > Branch permissions > Add a branch permission do repositório do BitBucket. Veja:

O BitBucket oferece uma série de checks que podem ser habilitados. O objetivo de usar apenas PRs para trazer código para esses branches é evitar que códigos não revisados e testados no ambiente de staging entrem em produção.

Assim que algum PR é executado nesses branches, um projeto no AWS CodeBuild é acionado, via WebHooks do Bitbucket, para iniciar o build do projeto através de triggers WebHook entre o BitBucket e o CodeBuild.


Ambiente de Staging e Produção

Aqui na Mobicare convencionamos que o ambiente de staging / homologação fica em uma região da AWS (Oregon) e o ambiente de produção fica em outro (N. Virginia). Com isso conseguimos mais facilmente controlar os acessos dos desenvolvedores e operadores aos recursos da AWS.

Na próxima seção vamos mostrar como iniciar o build no ambiente correto de acordo com a branch onde o PR ocorreu.


CodeBuild Source Triggers

O primeiro passo é criar um projeto no CodeBuild e configurar o acesso ao BitBucket através do protocolo OAuth.

Uma vez com acesso ao BitBucket, o próximo passo é escolher o repositório:

Em seguida, definimos que o CodeBuild será informado pelo BitBucket cada vez que um código é enviado (Push) para este repositório. Essa parte é simples, o CodeBuild e o BitBucket se entendem, basta selecionar o checkbox.

O que precisamos fazer é filtrar apenas Pushes para a branch que temos interesse de acordo com o ambiente. O ambiente de staging ouvirá refs/heads/develop e o ambiente de produção ouvirá refs/heads/master.


Build Environment

O CodeBuild executa o build em containers Docker baseados em imagens próprias (padrão) ou imagens customizadas. No nosso caso, vamos utilizar uma imagem padrão, porém, mais para frente, vamos baixar alguns arquivos para customizar em tempo de build essas imagens.

Na imagem acima vemos uma opção Privileged que detalharemos em seguida. Ainda nesta tela do CodeBuild, mais abaixo, temos a opção de configurar variáveis de ambiente. Com elas, podemos ter scripts genéricos de build que funcionam para múltiplos projetos e ambientes.


Script de Build (buildspec.yml)

Agora que escolhemos a imagem que será utilizada, temos que definir de onde virá o script de build, que por padrão é um arquivo chamado buildspec.yml e fica na raiz do projeto. O arquivo buildspec.yml é um arquivo YAML seguindo uma estrutura específica para o CodeBuild.

O arquivo buildspec.yml tem em seu primeiro nível as fases de build. No exemplo deste post, utilizamos as fases: install, pre_build, build e post_build.

Na fase de install, definimos o ambiente/tecnología, neste caso openjdk8. O codebuild atualmente suporta os runtime-versions abaixo:

Além de definir o ambiente, vamos executar alguns comandos para customizar o ambiente do container.

Como estamos utilizamos Maven e um Artifactory privado para controlar as dependências Java, precisamos carregar os arquivos de configuração do Maven que estão customizados com endereço do repositório e credenciais de acesso.

Fazemos isso usando o utilitário aws-cli instalado na imagem de build. Para isso o IAM Service Role precisa estar autorizado a acessar o Bucket S3 escolhido (esse passo está detalhado mais à frente).

O artefato que estamos gerando é uma imagem Docker que será enviada para o ECR (Elastic Container Registry). Para que essa imagem seja gerada dentro de um container docker, precisamos conectar no Docker do hospedeiro. Para esse passo ser bem sucedido, a opção Privileged da configuração de ambiente precisa estar selecionada.

Já na fase de pre_build, utilizamos o aws-cli para logar no ECR, que é um repositório Docker gerenciado pela AWS. Novamente o IAM Service Role precisa estar preparado para permitir essa operação.

Na fase de build, temos o passo para gerar o JAR do Spring Boot, em seguida o comando Docker para gerar a imagem, a tag latest e finalmente fazer o push para o ECR. Esse último passo também deve ser permitido pelo IAM Service Role.

Na fase de post_build, fazemos o deploy da aplicação no ECS através do comando update-service.

Todas as etapas possuem variáveis que foram definidas na parte Environment.


Definição de Cache

O CodeBuild permite a configuração de cache para acelerar builds subsequentes. O Cache pode ser configurado como local na máquina ou remoto armazenado em um bucket S3.

Cada abordagem possui vantagens e desvantagens. No nosso caso, optamos por fazer o cache do repositório local do Maven em um Bucket no S3 para evitar que todos as dependências sejam baixas a cada build. Aqui novamente o IAM Service Role selecionado deve permitir a escrita no Bucket S3.


IAM Service Role

Em vários passos ao longo deste post falamos sobre permissões que devem ser configuradas no IAM Service Role selecionado. Abaixo um exemplo de Service Role que usamos para esse projeto.

Os dois primeiros itens são políticas gerenciadas pela AWS, e os três últimos são políticas customizadas:

  • CloudWatchFullLogs: permissões de escrita e leitura de logs no Cloud
  • AmazonEC2ContainerRegistryPowerUser: permissão para interagir com o ECR para download e upload de imagens e layers.
  • Acesso de leitura de escrita ao Bucket com os arquivos do Maven e onde o cache será armazenado.
  • Acesso de atualização dos serviços no ECS.

Conclusão

Utilizar o AWS para o processo de Continuous Deployment abstrai toda a complexidade de infra-estrutura que ferramentas como Jenkins e GO CD exigem, como providenciar um servidor, fazer deploy da ferramenta e manter essa estrutura, reduzindo esse processo a apenas alguns passos de configuração simples.

O valor que essa solução agrega ao processo de desenvolvimento para a equipe é absurdo, tornando o processo entre o commit e a demonstração (e posteriormente o lançamento para produção) extremamente ágil.

Um próxima evolução será usar o AWS CodeDeploy que consegue trazer mais controle e validações para o processo de deploy.


A Mobicare combina os Melhores Talentos, Tecnologias de Ponta, Práticas Agile e DevOps com Capacidades Operacionais avançadas para ajudar Operadoras Telecom a gerar novas receitas e a melhorar a experiência dos seus próprios clientes.

Se você gosta de inovar, trabalhar com tecnologia de ponta e está sempre buscando conhecimento, somos um match perfeito!

Vem trabalhar com a gente 😉 bit.ly/mobicarreiras