Como construir imagens Docker multi‑arquitetura (amd64/arm64) com GitHub Actions
Docker & Containers · Por Kevin Marcondes · Publicado em · Atualizado em · 9 min de leitura
Este tutorial mostra, passo a passo, como construir imagens Docker multi‑arquitetura (amd64 e arm64) e publicar manifestos com GitHub Actions. Você vai entender como funcionam os manifestos OCI, estruturar um Dockerfile multiplataforma, configurar Buildx e QEMU no CI, aplicar cache do BuildKit e validar que cada arquitetura puxa a imagem correta.
Multi‑arquitetura e manifestos OCI: o que você vai publicar
Uma imagem multi‑arquitetura é um conjunto de imagens específicas por plataforma reunidas sob uma mesma tag. O registro armazena um índice OCI, também chamado de manifest list.
Quando você faz o pull, o cliente escolhe a variante correta com base na plataforma. Assim, a tag única, como latest, aponta para amd64 e arm64 ao mesmo tempo, sem esforço no consumo.
No Buildx, publicar para várias plataformas em um único push já cria o manifesto automaticamente. Também é possível criar ou atualizar manifestos explicitamente quando necessário.
Pré‑requisitos de ambiente e acesso ao registro
Antes de iniciar, garanta acesso ao registro onde vai publicar e um repositório com um Dockerfile pronto para build. O runner do GitHub Actions pode ser o padrão em Linux.
O workflow instalará QEMU e Buildx para permitir builds cruzados. Separe as credenciais do registro como segredos do repositório para autenticação no pipeline.
- Conta no Docker Hub e/ou GHCR com permissão de escrita.
- Segredos configurados no repositório (por exemplo: DOCKERHUB_USERNAME/DOCKERHUB_TOKEN, ou uso do GITHUB_TOKEN no GHCR).
- Dockerfile preparado para multiplataforma; evite dependências específicas de CPU sem alternativa.
- Pipeline no GitHub Actions criado; veja como configurar pipeline de deploy automático.
- Para imagens enxutas, aplique multistage; referência: como criar imagens Docker leves: multistage.
Estruture o Dockerfile para multiplataforma (TARGETARCH/TARGETOS)
BuildKit injeta variáveis de contexto como TARGETOS, TARGETARCH e BUILDPLATFORM. Use essas variáveis para compilar binários na arquitetura alvo e escolher bases corretas em estágios de build.
O exemplo abaixo compila um binário Go estático por arquitetura e publica uma imagem final mínima. Adapte os comandos ao seu projeto e linguagem. Se seu build usa bibliotecas nativas, verifique a compatibilidade com cross‑compile.
# Exemplo Dockerfile multiplataforma (Go)
# Estágio de build roda na plataforma do builder
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS build
ARG TARGETOS
ARG TARGETARCH
WORKDIR /src
# Dependências de módulos
COPY go.mod go.sum ./
RUN go mod download
# Código da aplicação
COPY . .
# Compila binário para a arquitetura alvo
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
go build -o /out/app ./cmd/app
# Imagem final mínima
FROM alpine:3.20
WORKDIR /app
COPY --from=build /out/app /usr/local/bin/app
# Labels OCI úteis para rastreabilidade
LABEL org.opencontainers.image.source="https://example.com/repo" \
org.opencontainers.image.description="App multi-arch amd64/arm64"
USER 10001
ENTRYPOINT ["app"]
Workflow GitHub Actions: Buildx, QEMU, login e push multi‑arch
O workflow instala QEMU e Buildx, autentica no registro e usa o build‑push‑action para construir e publicar, em uma única etapa, as variantes amd64 e arm64. O cache do BuildKit reduz tempo de build entre execuções.
Ajuste as tags conforme sua estratégia. O metadata‑action ajuda a gerar tags por branch, tag de release e SHA automaticamente. Para GHCR, o GITHUB_TOKEN geralmente funciona para publicar pacotes do repositório.
name: build-and-push-multiarch
on:
push:
branches: ["main"]
tags: ["v*", "v*.*.*"]
pull_request:
jobs:
docker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta (tags e labels)
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
labels: |
org.opencontainers.image.source=${{ github.repositoryUrl }}
org.opencontainers.image.revision=${{ github.sha }}
- name: Login GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Exemplo opcional: login também no Docker Hub
# - name: Login Docker Hub
# uses: docker/login-action@v3
# with:
# username: ${{ secrets.DOCKERHUB_USERNAME }}
# password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build & Push multi-arch
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Estratégia de tags e labels que evitam confusão
Defina convenções claras para cada evento do repositório. Use latest apenas para a linha estável principal. Em releases, publique tags imutáveis (semver) e, se fizer sentido, tags abreviadas para faixas.
Inclua labels OCI para rastreabilidade de origem e commit. Evite sobrescrever tags de versões antigas. Preserve a mesma lista de plataformas em todas as tags estáveis.
- Branches principais: latest e SHA do commit.
- Releases: vX.Y.Z, vX.Y, vX e latest (se representar a versão estável atual).
- Pull requests: tags efêmeras (por exemplo, pr-<número>) sem promover a latest.
- Inclua labels org.opencontainers.image.source e revision para auditoria.
| Evento | Tags recomendadas |
|---|---|
| Push na main | latest, sha-<CURTO> |
| Tag v1.2.3 | v1.2.3, v1.2, v1, latest (opcional) |
| Pull request | pr-<número>, sha-<CURTO> (sem latest) |
Publicar e inspecionar o manifesto gerado
Ao construir com múltiplas plataformas no Buildx e fazer push, o manifesto é criado automaticamente. Inspecione o resultado para conferir que amd64 e arm64 constam na mesma tag.
Use a ferramenta de inspeção para verificar plataformas, digests e camadas. Isso ajuda a diagnosticar pulls que falham por ausência de variante específica.
# Inspecione a manifest list publicada
# Ajuste a referência para seu registro e tag
docker buildx imagetools inspect ghcr.io/<owner>/<repo>:<tag>
# Opcional: inspecione um digest específico
# docker buildx imagetools inspect ghcr.io/<owner>/<repo>@sha256:<digest>
Como confirmar em cada arquitetura (amd64/arm64)
Valide a execução forçando a plataforma no cliente. Cada comando deve imprimir a arquitetura do kernel dentro do container. Assim, você garante que a imagem correta foi baixada a partir do mesmo tag.
Se o binário depender de bibliotecas nativas, execute um teste rápido de funcionalidade em cada plataforma para antecipar problemas de runtime.
# Força execução como amd64
docker run --rm --platform linux/amd64 ghcr.io/<owner>/<repo>:<tag> uname -m
# Força execução como arm64
docker run --rm --platform linux/arm64 ghcr.io/<owner>/<repo>:<tag> uname -m
Erros comuns, diagnósticos e otimizações de cache
Alguns problemas aparecem em builds multiplataforma. Abaixo, um guia rápido de diagnóstico e solução. Para reduzir o tempo de build, use cache remoto do BuildKit e baseie camadas em arquivos estáveis.
Para técnicas de cache avançadas no CI, confira o guia dedicado: como usar BuildKit e cache remoto no CI Docker.
- no matching manifest: a tag não inclui a plataforma desejada.
- exec format error: binário para a arquitetura errada foi copiado na imagem final.
- Falha ao registrar QEMU: o runner não carregou binfmt; garanta setup‑qemu no workflow.
- Cross‑compile com bibliotecas nativas: ajuste toolchains e flags para cada TARGETARCH.
- Cache ineficiente: reorganize camadas e ative cache‑to/cache‑from do BuildKit.
| Sintoma | Causa provável | Como resolver |
|---|---|---|
| no matching manifest para arm64 | Tag publicada só com amd64 | Garanta platforms: linux/amd64,linux/arm64 no build e republique |
| exec format error ao iniciar | Binário amd64 copiado em imagem arm64 | Use TARGETOS/TARGETARCH no build e COPY do artefato correto |
| Build lento em PRs | Cache não compartilhado ou inválido | Ative cache gha e fixe passos COPY para maximizar reaproveitamento |
| Erro ao rodar toolchain nativo no cross | Dependências específicas da CPU | Use compilações estáticas ou toolchains multiplataforma apropriadas |
Perguntas frequentes
Preciso de runners arm64 para construir imagens arm64?
Na maioria dos casos, não. O Docker Buildx com QEMU permite construir variantes arm64 a partir de um runner amd64. Builds nativos podem ser mais rápidos e necessários para projetos com dependências de sistema específicas, mas para muitas aplicações o cross‑build funciona bem.
Buildx já publica o manifesto ou preciso criar manualmente?
Se você usa o build‑push‑action com múltiplas plataformas e push habilitado, o manifesto é criado automaticamente na tag informada. A criação manual só é necessária para compor manifestos usando imagens já existentes ou para retagging específico.
Como escolher entre Docker Hub e GHCR para publicar?
Ambos funcionam. Avalie permissões, integração com repositórios e controle de acesso. Você pode publicar em ambos, autenticando nos dois registros e usando duas referências de tag no mesmo build. Ajuste segredos e permissões conforme a política do seu time.
Como reduzir o tamanho da imagem final multi‑arch?
Use multistage para manter só o artefato necessário, escolha bases mínimas e elimine ferramentas de build no estágio final. Prefira binários estáticos quando possível e ordene COPY/RUN para maximizar cache. O mesmo Dockerfile atende amd64 e arm64 sem duplicação.
Posso ter tags diferentes por arquitetura?
Pode, mas é raro e aumenta a complexidade. O padrão é uma tag única apontando para um manifesto que referencia as variantes por plataforma. Use tags por arquitetura apenas quando há diferenças funcionais entre builds que exijam separação explícita.
QEMU é suficiente para projetos com dependências nativas (por exemplo, C/C++)?
Depende do projeto. QEMU cobre muitos casos, mas toolchains e bibliotecas nativas podem exigir configuração extra ou builds nativos. Avalie compilar estaticamente quando possível e mantenha testes por arquitetura para validar binários resultantes.
Pronto para publicar imagens multi‑arch com confiança?
Se você quer revisar seu Dockerfile e seu workflow de release multiplataforma no GitHub Actions, envie uma mensagem com o repositório e o objetivo das suas tags.
Quero revisar meu workflowVeja também
- tutorial: implementar Canary releases com Traefik em Docker Compose usando headers e weight routing para deploys graduais de aplicações PHP
- Tutorial: configurar HAProxy com Let's Encrypt e healthchecks para balanceamento de tráfego entre containers Docker em VPS (passo a passo)
- Tutorial prático: implementar mTLS entre Nginx e microserviços usando smallstep CA em Docker Compose
- Tutorial: configurar cgroup v2 e systemd ResourceControl para limitar CPU, memória e I/O de containers Docker no Debian