Como diminuir o tamanho do container usando multi-stage builds.

De vez em quando esbarro com alguma imagem docker enorme em que 99% do tamanho é lixo deixado quando a imagem estava sendo construída. Isso é especialmente ruim quando estamos de executáveis Go que são autocontidos e geralmente não precisam de nenhum recurso externo, nem mesmo a libc.

multi-stage builds

A solução é usar multi-stage builds, dessa forma você pode ter duas etapas no seu Dockerfile, uma de compilação que baixa o que for necessário, compila o código, etc. e outra que apenas copia os executáveis e os recursos para os locais esperados e nem precisa ser baseada em alguma distribuição, pode ser uma imagem scretch que reduz ainda mais o tamanho final.

Exemplo de Dockerfile

Este é um pequeno exemplo de Dockerfile que usa multi-stage builds para primeiro criar um executável e em seguida criar uma imagem que contém apenas o próprio executável e mais nada.

FROM golang:latest as build
WORKDIR /build
ADD . .
RUN CGO_ENABLED=0 GOOS=linux \
    go build -ldflags '-extldflags "-static"' -o app

FROM scratch
COPY --from=build /build/app /app
ENTRYPOINT ["/app"]

Pontos positivos

  • Menor superfície de ataque, não tem nenhum software extra parado esperando ser expoitado é só o seu executável e mais nada.
  • Mais fácil de escalar, simplesmente pela velocidade que um cluster vai conseguir baixar e subir novas instâncias.

Pontos negativos

  • Se você está acostumado a acessar seus containers abrindo um shell, você não vai poder fazer isso com imagens scratch para isso precisa subir uma distribuição nem que seja uma bem leve.
  • Debug pode ser um pouco mais complicado se dependendo de como seu sistema está projetado, se você cometer um erro e seu executável retornar um panic você precisa garantir que está observando a saída de erro.

Boa prática

Usar multi-stage build é sem dúvida uma boa prática não importando a linguagem, é simples de implementar e os pontos negativos são facilmente contornados.

Cesar Gimenes

Última modificação