GitLab Runner: Construir imagens Docker
Construa e envie imagens Docker de forma eficiente a partir dos seus pipelines GitLab CI/CD utilizando runners Stackhero e Docker-in-Docker
👋 Bem-vindo à documentação da Stackhero!
A Stackhero disponibiliza uma solução GitLab Runner cloud simples que torna a execução dos seus jobs GitLab CI/CD eficiente e sem complicações. Eis o que pode esperar:
- Minutos CI/CD ilimitados: execute os seus pipelines sempre que precisar, sem cobrança por minuto nem custos inesperados.
- Vários jobs em simultâneo: acelere o desenvolvimento ao executar múltiplos jobs em paralelo.
- O executor Docker com suporte para Docker-in-Docker: construa e faça push de imagens de containers facilmente como parte do seu processo CI/CD.
- Compatível tanto com GitLab.com como com instâncias GitLab self-managed.
- Uma infraestrutura privada e dedicada com armazenamento NVMe/SSD rápido garante um desempenho de build estável e previsível.
- Disponível nas regiões 🇪🇺 Europa e 🇺🇸 USA para responder às necessidades da sua equipa.
Poupe tempo: pode ligar o seu primeiro GitLab Runner e começar a executar pipelines em apenas alguns minutos!
Introdução
Com um GitLab Runner da Stackhero, cada job é executado dentro de um novo contentor utilizando o executor Docker. Pode construir as suas próprias imagens Docker diretamente no pipeline ao ativar o Docker-in-Docker (DinD). Esta configuração inicia um daemon Docker ao lado do seu job, permitindo-lhe executar comandos docker build e docker push como parte do seu processo CI/CD.
Cada execução beneficia de minutos CI/CD ilimitados: pode construir quantas vezes forem necessárias, sem preocupações com limites de utilização. O cache de build é armazenado no disco dedicado do runner, o que significa que builds repetidos podem reutilizar camadas anteriores. Isto reduz significativamente o tempo de build e acelera a conclusão dos seus pipelines.
Construir uma imagem Docker com Docker-in-Docker
Pode adicionar o seguinte exemplo de .gitlab-ci.yml ao seu repositório. Esta configuração constrói o Dockerfile na raiz do seu projeto:
build-image:
stage: build
image: docker:29
services:
- name: docker:29-dind
alias: docker
variables:
DOCKER_HOST: "tcp://docker:2375"
DOCKER_TLS_CERTDIR: ""
before_script:
- docker info
script:
# Substitua "my-image" pelo nome pretendido:
- docker build -t my-image .
# Opcionalmente, execute um teste rápido na imagem construída:
# - docker run --rm my-image /path/to/tests
Estamos a utilizar a versão 29 da imagem Docker neste exemplo. Pode optar por uma versão mais recente assim que estiver disponível. Pode consultar as tags mais recentes na página oficial da imagem Docker.
Nesta configuração, o serviço docker:29-dind inicia um daemon Docker ao lado do seu job, e DOCKER_HOST: "tcp://docker:2375" indica ao CLI do docker para o utilizar. Defina sempre o DOCKER_HOST quando declarar um serviço docker:dind: sem esta variável, o CLI liga-se silenciosamente a outro daemon, o que pode fazer com que o build passe mesmo quando o serviço está mal configurado, ocultando problemas reais (e quebrando ferramentas como o Testcontainers que dependem deste serviço). DOCKER_TLS_CERTDIR: "" liga-se através da porta interna não segura (sem TLS) 2375 na rede interna do job.
Como alternativa mais simples, pode omitir o bloco services e ambas as variáveis: o runner da Stackhero também expõe um daemon Docker pronto a usar através de um socket montado, pelo que um simples docker build funciona sem configuração adicional.
Executar os seus testes com Testcontainers
O Testcontainers é uma biblioteca de testes, disponível para Java, Go, Node.js, Python, .NET e outras linguagens, que inicia serviços reais como contentores Docker descartáveis durante a execução dos seus testes. Em vez de simular uma base de dados ou um message broker, os seus testes de integração comunicam com uma instância real de PostgreSQL, MySQL, Redis ou Kafka, criada antes dos testes e removida logo após. É especialmente popular em projetos Java e Spring.
O Testcontainers necessita de um daemon Docker, pelo que funciona exatamente com a mesma configuração Docker-in-Docker apresentada acima. Não é necessária nenhuma variável adicional:
test:
stage: test
# Utilize a imagem necessária para os seus testes (um JDK neste caso), não necessariamente a imagem docker:
image: gradle:jdk21
services:
- name: docker:29-dind
alias: docker
variables:
DOCKER_HOST: "tcp://docker:2375"
DOCKER_TLS_CERTDIR: ""
script:
- gradle test
O Testcontainers lê a variável DOCKER_HOST para encontrar o daemon e reutiliza o mesmo hostname docker para aceder às portas publicadas pelos contentores que inicia. Ambos funcionam de forma autónoma, pois o alias docker é resolvido na rede do seu job.
Mantenha o
DOCKER_HOSTnos seus jobs com Testcontainers. Sem esta variável, o Testcontainers recorre ao socket Docker montado pelo runner e tenta aceder aos seus contentores de teste através do endereço IP do host, ao qual o seu job não consegue ligar-se. O sintoma mais comum é o contentor auxiliar Ryuk falhar comWait strategy failed. Container is removed, ouTimed out waiting for log output matching '.*Started.*'.
Enviar para o registo de contentores do GitLab
O GitLab disponibiliza variáveis predefinidas (CI_REGISTRY, CI_REGISTRY_USER, CI_REGISTRY_PASSWORD, CI_REGISTRY_IMAGE) para que o seu pipeline possa autenticar-se e enviar imagens para o registo de contentores do seu projeto de forma segura. Não são necessários segredos adicionais.
Segue um exemplo de job que constrói e envia a sua imagem:
build-and-push:
stage: build
image: docker:29
services:
- name: docker:29-dind
alias: docker
variables:
DOCKER_HOST: "tcp://docker:2375"
DOCKER_TLS_CERTDIR: ""
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script:
- docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" .
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
# Se estiver na branch principal, pode também marcar e enviar "latest":
- |
if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then
docker tag "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" "$CI_REGISTRY_IMAGE:latest"
docker push "$CI_REGISTRY_IMAGE:latest"
fi
Para enviar as suas imagens para outro registo (como o Docker Hub ou um registo privado), pode guardar as credenciais como variáveis CI/CD e utilizá-las com o docker login da mesma forma.
Acelerar builds repetidos
O disco do seu runner é persistente entre pipelines, permitindo reutilizar camadas de imagem como cache de build. Isto pode tornar builds repetidos muito mais rápidos. Eis um exemplo de configuração:
build-cached:
stage: build
image: docker:29
services:
- name: docker:29-dind
alias: docker
variables:
DOCKER_HOST: "tcp://docker:2375"
DOCKER_TLS_CERTDIR: ""
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
script:
# Faça pull da imagem mais recente para alimentar o cache (se existir):
- docker pull "$CI_REGISTRY_IMAGE:latest" || true
- docker build --cache-from "$CI_REGISTRY_IMAGE:latest" -t "$CI_REGISTRY_IMAGE:latest" .
- docker push "$CI_REGISTRY_IMAGE:latest"
Este método permite que os seus builds tirem partido do cache de camadas do Docker, reconstruindo apenas as camadas novas ou alteradas.
Executar jobs em paralelo
O seu plano determina quantos jobs podem ser executados em simultâneo. Jobs na mesma etapa iniciam ao mesmo tempo, até ao limite de concorrência definido. Isto significa que vários jobs independentes podem correr em paralelo, terminando assim que o mais lento terminar, em vez de esperar que cada um termine sequencialmente.
Exemplo:
stages:
- test
unit:
stage: test
image: node:22
script: npm run test:unit
integration:
stage: test
image: node:22
script: npm run test:integration
e2e:
stage: test
image: node:22
script: npm run test:e2e
Se definir a sua concorrência para 1 ou superior, os jobs unit, integration e e2e serão executados em simultâneo.
Para mais detalhes sobre como construir imagens Docker em pipelines GitLab CI/CD, consulte a documentação oficial do GitLab sobre builds Docker.