Uma imagem que existe apenas na máquina do desenvolvedor não serve a nenhum pipeline de CI/CD, a nenhum servidor de produção, a nenhum colega de equipe. Para que uma imagem seja utilizável em qualquer ambiente, ela precisa ser publicada em um registro de container — um serviço que armazena, versiona e distribui imagens.
O fluxo padrão em qualquer pipeline de entrega é construir a imagem, enviá-la para um registro e, em seguida, instruir o servidor de destino a baixá-la e executá-la. O registro é o elo entre a construção e a execução.
Docker Hub: O Registro Público Padrão
O Docker Hub é o registro padrão do Docker — quando se executa docker pull nginx, é do Docker Hub que a imagem vem. Qualquer pessoa pode criar uma conta gratuita e publicar imagens públicas sem limite.
Criando uma conta e fazendo login:
# Login interativo
docker login
# Login com credenciais diretas (útil em scripts)
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
Convenção de nomenclatura no Docker Hub:
usuario/nome-da-imagem:tag
# Exemplos
ricardomatosdocker/minha-api:1.0.0
ricardomatosdocker/minha-api:latest
Publicando uma imagem:
# Constrói com a tag no formato correto
docker build -t ricardomatosdocker/minha-api:1.0.0 .
# Adiciona a tag latest apontando para a mesma imagem
docker tag ricardomatosdocker/minha-api:1.0.0 ricardomatosdocker/minha-api:latest
# Publica ambas as tags
docker push ricardomatosdocker/minha-api:1.0.0
docker push ricardomatosdocker/minha-api:latest
GitHub Container Registry: Imagens Integradas ao Repositório
O GitHub Container Registry (GHCR) armazena imagens diretamente associadas a um repositório GitHub. A integração com GitHub Actions é nativa e as permissões seguem as mesmas regras do repositório — quem tem acesso ao repositório tem acesso às imagens.
A URL do GHCR segue o formato:
ghcr.io/usuario-ou-org/nome-da-imagem:tag
# Exemplos
ghcr.io/ricardomatosdocker/minha-api:1.0.0
ghcr.io/minha-empresa/minha-api:main
Login no GHCR:
# Usando um Personal Access Token com permissão write:packages
echo "$GITHUB_TOKEN" | docker login ghcr.io -u "$GITHUB_USERNAME" --password-stdin
Automatizando a Publicação com GitHub Actions
O fluxo mais comum em projetos modernos: a cada push na branch main, o pipeline constrói a imagem e a publica automaticamente. A cada tag de release, publica com o número de versão.
# .github/workflows/docker-publish.yml
name: Build e Publicação de Imagem
on:
push:
branches: [ main ]
tags: [ 'v*.*.*' ]
pull_request:
branches: [ main ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Clona o repositório
uses: actions/checkout@v4
- name: Configura o Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login no GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extrai metadados da imagem
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=sha-
- name: Constrói e publica a imagem
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
O que esse workflow produz em cada cenário:
Um push na main gera a tag main e uma tag com o SHA do commit como sha-abc1234. Um Pull Request gera uma imagem de teste mas não a publica. A criação da tag v1.2.0 gera as tags 1.2.0, 1.2 e v1.2.0 — seguindo as convenções do SemVer.
Multi-platform Builds: Imagens para ARM e AMD64
Com a popularização de máquinas Apple Silicon e instâncias ARM na AWS (Graviton), imagens que rodam apenas em AMD64 se tornam um problema. O Docker Buildx permite construir imagens para múltiplas arquiteturas em um único comando:
- name: Configura QEMU para emulação de ARM
uses: docker/setup-qemu-action@v3
- name: Configura Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Constrói para múltiplas plataformas
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
O registro armazena um manifesto multi-plataforma — quando docker pull é executado, o Docker automaticamente baixa a variante correta para a arquitetura do host.
Gerenciando Tags de Forma Consistente
Uma estratégia de tagging bem definida evita confusão sobre qual versão está rodando em cada ambiente:
# SHA do commit — imutável, rastreável, ideal para rollbacks
ghcr.io/empresa/api:sha-abc1234
# Branch — sempre aponta para o último build daquela branch
ghcr.io/empresa/api:main
ghcr.io/empresa/api:develop
# Versão semântica — para releases estáveis
ghcr.io/empresa/api:1.2.0
ghcr.io/empresa/api:1.2 # última patch da minor 1.2
ghcr.io/empresa/api:1 # última minor do major 1
# Ambiente — prático mas menos rastreável
ghcr.io/empresa/api:staging
ghcr.io/empresa/api:production
# latest — conveniente mas perigoso em produção
ghcr.io/empresa/api:latest
A recomendação para ambientes de produção é usar sempre o SHA do commit ou a versão semântica exata — nunca latest ou tags de ambiente mutáveis. Isso garante que o deploy é determinístico e que um rollback pode ser feito apontando para um SHA específico.
Inspecionando Imagens Publicadas
# Inspeciona uma imagem remota sem baixá-la
docker manifest inspect ghcr.io/empresa/api:1.2.0
# Verifica as plataformas suportadas
docker manifest inspect ghcr.io/empresa/api:1.2.0 | \
grep -A2 '"platform"'
# Histórico de camadas de uma imagem publicada
docker history ghcr.io/empresa/api:1.2.0
Encerrando o Módulo 3
Com este artigo conclui-se o Módulo 3. Foram cobertos os conceitos fundamentais de containers, a instalação e uso do Docker, a escrita de Dockerfiles eficientes, volumes e redes, orquestração local com Docker Compose, boas práticas de produção e publicação em registros.
O Módulo 4 entra no tema que une tudo isso em um fluxo contínuo: CI/CD na Prática. Os containers construídos neste módulo serão testados, publicados e implantados automaticamente por pipelines que rodam sem intervenção humana.
Referências para Aprofundamento
Documentação oficial
- Docker Hub Documentation — docs.docker.com — Guia completo do Docker Hub, incluindo repositórios privados, webhooks e automação de builds.
- GitHub Container Registry — docs.github.com — Documentação oficial do GHCR com exemplos de autenticação e publicação via Actions.
GitHub Actions
- docker/build-push-action — GitHub — Repositório oficial da action usada para construir e publicar imagens. Documentação detalhada de todos os parâmetros disponíveis.
- docker/metadata-action — GitHub — Repositório da action de geração automática de tags e labels. Inclui exemplos de todas as estratégias de tagging.
Leitura complementar
- Multi-platform Images — docs.docker.com — Guia oficial sobre construção de imagens multi-plataforma com Buildx e QEMU.
Exercícios
Exercício 1
Por que o step de publicação usa push: ${{ github.event_name != 'pull_request' }}? O que se perderia ao publicar também a partir de PRs?
Ver resposta
✓ Resposta: Porque em um Pull Request o objetivo é validar que a imagem constrói, não distribuí-la. A expressão mantém o build acontecendo — que é justamente o valor do check — e desliga apenas a publicação. Publicar a partir de PRs traria dois problemas. O primeiro é poluição: o registro acumularia tags de branches que talvez nunca sejam mergeadas. O segundo é de segurança: qualquer pessoa capaz de abrir um PR passaria a gravar no registro da organização, o que abre caminho para publicar conteúdo arbitrário sob um nome confiável — e imagens são baixadas e executadas justamente por confiança na origem.
Exercício 2
Por que o workflow declara permissions: contents: read e packages: write? O que acontece se packages: write for esquecido?
Ver resposta
✓ Resposta: Porque o GITHUB_TOKEN é gerado automaticamente a cada execução e suas permissões são escopadas. Declará-las explicitamente aplica o mínimo privilégio ao workflow: ele lê o repositório e escreve pacotes, e nada além disso — se o token vazar em um log, o alcance é limitado a essas duas capacidades. Esquecer o packages: write produz uma falha particularmente confusa: o docker login no GHCR conclui com sucesso, dando a impressão de que a autenticação está correta, e só o push falha por permissão. É um dos erros mais comuns na primeira configuração, justamente porque o login não denuncia o problema.
Exercício 3
O que o docker/metadata-action gera em cada cenário — push na main, Pull Request e criação da tag v1.2.0? Por que produzir 1.2 além de 1.2.0?
Ver resposta
✓ Resposta: Um push na main gera a tag main e uma tag com o SHA do commit, como sha-abc1234. Um Pull Request gera a tag correspondente ao PR, mas sem publicação. A tag v1.2.0 gera 1.2.0, 1.2 e v1.2.0. A tag 1.2 existe para quem deseja receber correções de patch sem alterar a referência: quem aponta para ela passa a rodar a 1.2.1 e a 1.2.2 assim que saírem, mas nunca salta sozinho para a 1.3. É a mesma lógica das faixas de versão dos gerenciadores de pacote, aplicada a imagens — segurança de atualização automática apenas onde a compatibilidade está garantida.
Exercício 4
Por que a tag sha-abc1234 é a referência ideal para rollback, e por que apontar produção para latest é problemático?
Ver resposta
✓ Resposta: Porque o SHA é imutável e rastreável: aquela tag corresponde a exatamente um commit e a exatamente uma imagem, para sempre. O rollback se resume a apontar para o SHA anterior, com certeza absoluta do que está subindo e de qual código o produziu. O latest é o oposto — um ponteiro móvel. Duas máquinas que fizeram pull em momentos diferentes podem estar executando imagens distintas sob o mesmo nome, sem nenhum sinal disso. E o custo se manifesta no pior momento: durante um incidente, a pergunta "qual versão está no ar?" deixa de ter resposta confiável, e o rollback não tem alvo definido.
Exercício 5
Por que builds multi-plataforma se tornaram necessários? O que o registro efetivamente armazena quando se usa platforms: linux/amd64,linux/arm64?
Ver resposta
✓ Resposta: Porque a arquitetura deixou de ser homogênea: máquinas Apple Silicon no desenvolvimento e instâncias ARM em produção — como as Graviton da AWS — passaram a conviver com o AMD64 tradicional. Uma imagem construída apenas para AMD64 falha ao subir nessas máquinas ou roda sob emulação, com perda expressiva de desempenho. Com o build multi-plataforma, o registro armazena um manifesto multi-plataforma: uma lista que aponta para a imagem específica de cada arquitetura. O docker pull consulta esse manifesto e baixa automaticamente a variante correta para o host, sem que quem executa precise saber que existem duas.
Exercício 6
Que problema cache-from: type=gha e cache-to: type=gha,mode=max resolvem? E o que o mode=max acrescenta?
Ver resposta
✓ Resposta: Resolvem o fato de o runner ser descartado ao fim de cada execução, levando junto todo o cache de camadas do Docker. Sem um cache externo, cada build recomeça do zero — reinstala dependências, recompila tudo — mesmo quando apenas uma linha mudou, o que anula na prática toda a estratégia de ordenação de camadas do Dockerfile. O type=gha grava essas camadas no cache do GitHub Actions e as recupera na execução seguinte. O mode=max acrescenta a exportação das camadas intermediárias, incluindo as dos estágios de build de um multi-stage — que, por não fazerem parte da imagem final, não seriam preservadas de outro modo, apesar de serem justamente as mais caras de reconstruir.