Hoje em dia construir containers já faz parte do Dia a Dia de qualquer Sysadmin.
Após executar algumas vezes ninguém aguenta rodar mais docker build -t ., principalmente quando sua build tem varias dependências e fica atualizando a lista de pacotes o tempo todo, mesmo com o cache nos layers do docker as vezes fica muito demorado.
Para resolver esse problema normalmente utilizamos alguma ferramenta de Continuous Integration, ela será responsável por criar as imagens e enviar para algum repositório. Quando utilizamos o gitlab-ci nós podemos abstrair algumas partes do CI/CD, principalmente se utilizamos o registry do gitlab:
- Configurar um web-hook para buildar quando enviamos o código
- Criar credenciais para o push da imagem
- Criar um webhook para nós avisar caso de falha
- Configurar algum bot para criar um merge request quando a build for finalizada com sucesso
- Criar webhooks para deploy no kubernetes
Para utilizar o Gitlab-CI, só precisamos de um Gitlab Runner configurado.
A versão gratuita do Gitlab já fornece a função de CI/CD sem precisar realizar nenhum configuração, é claro que iremos utilizar um recurso compartilhado, que pode ser utilizado por qualquer pessoa, o bom senso nos recomenda a não rodar nada confidencial lá.
Caso você não possua uma conta no Gitlab, é só acessar ele link e criar uma https://gitlab.com/users/sign_in. Com a versão gratuita do Gitlab nós temos acesso a repositórios públicos e privados com a função de CI/CD.
Nesse post vamos usar o repositório https://gitlab.com/odilonjunior/post-ci-cd como exemplo para o CI/CD, esse repo tem um codigo fonte java, que será construído via MVN depois seu artefato será copiado para um container que será responsável por executar o binário.
Nosso projeto possui essa estrutura.
├── Dockerfile
├── pom.xml
├── Readme.md
└── src
└── main
└── java
└── Bootcamp.java
Nosso Dockerfile é bem simples, ele usa a estrutura de multistage, no primeiro estagio ele gera o binário, no segundo ele apenas adiciona para um container java e criar o nosso CMD.
FROM maven:alpine
WORKDIR /srv/
COPY src src
COPY pom.xml .
RUN mvn package
FROM openjdk:8-jdk-alpine
WORKDIR /srv/
COPY --from=0 /srv/target/cicd-0.0.1.jar .
ENV JAVA_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar cicd-0.0.1.jar" ]
Para iniciar o CI/CD precisamos de um arquivo .gitlab-ci.yml na raiz do repositório, o nosso ficou construído da seguinte forma:
# This file is a template, and might need editing before it works on your project.
# Official docker image.
# Version 1.1
image: docker
services:
- docker:dind
stages:
- build
build_java:
stage: build
services:
- docker:dind
script:
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY"
- docker build -t ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA} .
- docker tag ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA} ${CI_REGISTRY_IMAGE}:latest
- docker push ${CI_REGISTRY_IMAGE}:latest
tags:
- docker
Nosso pipeline possui apenas um stage, que chamamos de build, e o build possui apenas um step, que é o build_java.
Ele vai executar em ordem, o login no registry do gitlab, utilizando as variáveis $CI_BUILD_TOKEN e $CI_REGISTRY como parâmetro, essas variáveis são globais no projeto, ela existem por padrão, toda vez que um pipeline é criado. Podemos verificar a lista de variáveis aqui https://docs.gitlab.com/ee/ci/variables/
Após executar o login nós vamos construir a imagem, utilizando as variáveis $CI_REGISTRY_IMAGE e $CI_COMMIT_SHA, a primeira variáveis vai ser o nome da imagem, como estamos usando o Gitlab publico ela ficara assim registry.gitlab.com/odilonjunior/post-ci-cd, a segunda variável faz referencia ao hash do commit, ele é aleatório, o hash vai servir como tag da nossa imagem.
O terceiro passo é um tag na imagem criada, vamos colocar a tag latest na imagem.
O ultimo passo é um docker push utilizando a variável CI_REGISTRY_IMAGE e a tag latest.
Você pode acompanhar o pipeline pela URL https://gitlab.com/odilonjunior/post-ci-cd/pipelines
Caso ele finalize com sucesso, o seguinte status vai aparecer:
E o log de saida do pipeline será parecido com esse: