Pular para o conteúdo

Publicar imagem para amd64 e arm64

Intermediário12 min de leituracontainers

Times com máquinas Apple Silicon e clusters com instâncias ARM (Graviton, Ampere) precisam da mesma imagem em duas arquiteturas. A solução é uma manifest list: uma única referência que aponta para uma imagem por plataforma, e o runtime escolhe a certa sozinho.

O driver padrão do Docker não faz build multiplataforma. Crie um builder com o driver docker-container:

Janela do terminal
docker buildx create --name multi --driver docker-container --use
docker buildx inspect --bootstrap # confirme as plataformas suportadas

Para emular a arquitetura ausente no seu host:

Janela do terminal
docker run --privileged --rm tonistiigi/binfmt --install all

Emulação funciona e é lenta — de 5 a 20 vezes mais devagar em builds que compilam. O passo 5 mostra a alternativa.

Janela do terminal
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t registry.exemplo.com/loja:1.8.3 \
--push . # multiplataforma exige push direto

Não existe --load para várias plataformas: o armazenamento local do Docker guarda uma imagem por vez. Ou você envia para o registry, ou faz build de uma plataforma só.

Se o seu Dockerfile compila código, use os argumentos que o buildx injeta para compilar sem emulação:

FROM --platform=$BUILDPLATFORM golang:1.23 AS build
ARG TARGETOS TARGETARCH
WORKDIR /src
COPY . .
# compila NA arquitetura do host, PARA a arquitetura de destino: rápido
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /bin/loja ./cmd/loja
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /bin/loja /bin/loja
ENTRYPOINT ["/bin/loja"]

Esse padrão — --platform=$BUILDPLATFORM no estágio de build, destino no estágio final — é o que torna o build multiplataforma viável em Go, Rust e qualquer linguagem com compilação cruzada.

Janela do terminal
docker buildx imagetools inspect registry.exemplo.com/loja:1.8.3

A saída deve listar linux/amd64 e linux/arm64 sob o mesmo digest de manifest list. Teste puxando de cada arquitetura:

Janela do terminal
docker run --rm --platform linux/arm64 registry.exemplo.com/loja:1.8.3 --version
docker run --rm --platform linux/amd64 registry.exemplo.com/loja:1.8.3 --version
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
tags: registry.exemplo.com/loja:${{ github.sha }}
cache-from: type=registry,ref=registry.exemplo.com/loja:buildcache
cache-to: type=registry,ref=registry.exemplo.com/loja:buildcache,mode=max
provenance: true
sbom: true

Duas plataformas custam mais que uma — a questão é quanto:

  • Compilação cruzada (passo 2): custo próximo de zero. Prefira sempre que a linguagem permitir.
  • Runners nativos por arquitetura, com a manifest list montada no fim: rápido, e a opção certa quando há compilação de dependências nativas (Python, Node com módulos binários).
    Janela do terminal
    # cada runner publica sua própria tag por arquitetura...
    docker buildx build --platform linux/arm64 -t loja:1.8.3-arm64 --push .
    # ...e um job final junta tudo numa referência só
    docker buildx imagetools create -t registry.exemplo.com/loja:1.8.3 \
    registry.exemplo.com/loja:1.8.3-amd64 registry.exemplo.com/loja:1.8.3-arm64
  • Emulação por QEMU: simples de configurar e caro em tempo. Aceitável para imagens que só copiam artefatos prontos.

Antes de publicar as duas, confirme que você precisa das duas: se o cluster é só amd64 e o ARM serve apenas para o notebook do time, talvez baste uma imagem de desenvolvimento.

Sintoma Causa provável
exec format error A plataforma pedida não está na manifest list
multiple platforms feature is currently not supported Builder com driver padrão; crie o docker-container
Build ARM eterno Emulação compilando; use compilação cruzada ou runner nativo
--load falha Esperado com várias plataformas: use --push
Dependência nativa quebra só em ARM Wheel/binário indisponível; compile no runner nativo
  • Builder docker-container ativo.
  • Build usando compilação cruzada quando possível.
  • imagetools inspect listando as duas plataformas.
  • Execução testada em cada arquitetura.
  • Cache de build configurado no CI.
  • Tempo de build medido e aceitável.