Reduzindo imagens docker com MultiStage

Se você usa linguagens compiladas (Golang, Java) e está buscando uma forma eficiente de reduzir o tamanho das suas imagens docker, está no lugar certo. Mas antes de decidir utilizar imagens pequenas, precisamos entender o real impacto do uso das imagens grandes. Esse impacto pode ser maior que o imaginado em ambientes de alta volumetria.

Cenário:

  • Uma aplicação utiliza uma imagem docker de 1GB.
  • Essa aplicação está em um cluster de 100 nós.
No cenário descrito acima, imagine que a aplicação recebe uma volumetria inesperada e vai escalar automaticamente de 1 para 100 containers. Isso significa que o cluster todo vai trafegar até 99GB de informação, fazendo pull (download) da imagem docker em cada nó que ainda não a tem disponível. Pra piorar a situação, resolvemos fazer deploy de uma nova versão com a aplicação escalada, ou seja, todas as imagens serão substituídas nesse processo (~100GB).

Resultado:

  • Altas taxas de transferência
  • Concorrência de banda entre pull de imagens e usuários
  • Escalabilidade afetada
Para tentar resolver essa questão, vamos falar de Docker multistage, que apesar de ser uma funcionalidade bastante conhecida na comunidade, ainda gera dúvidas em alguns dos nossos clientes. Antes da prática, vamos entender a teoria do uso de Multi Stage para o nosso caso: imagine que o processo de build de uma aplicação precise atender uma série de dependências. Essas dependências criam camadas que fazem a imagem docker ficar cada vez maior. Com o uso de Multi Stage, temos a possibilidade de utilizar imagens diferentes entre o processo de build e o processo de execução do binário. O resultado disso é uma imagem significativamente menor, já que as camadas anteriores são ‘descartadas’, restando somente o binário para execução em uma imagem enxuta. Dockerfile:
FROM golang:1.7.3 AS builder WORKDIR /go/src/github.com/alexellis/href-counter/ RUN go get -d -v golang.org/x/net/html COPY app.go . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY -–from=builder /go/src/github.com/alexellis/href-counter/app . CMD ["./app"]
O exemplo acima, ilustra o uso da imagem golang:1.7.3 com a tag builder, para o processo de build da aplicação. Em seguida, a imagem alpine:latest, é utilizada como imagem de execução, onde copiamos somente o binário da imagem anterior: COPY –from=builder /go/src/github.com/alexellis/href-counter/app . Isso traz resultados relevantes, já que a imagem final precisa somente do binário compilado.

Mão na Massa

Para o nosso laboratório prático, vamos utilizar um projeto opensource mantido pelo Luizalabs chamado Teresa, onde tive a chance de colaborar para uma redução significativa da imagem docker. A ideia do passo a passo é fazer o build da imagem antes do commit com Multi Stage, e depois do commit pra fazermos a comparação.

Antes do Multi Stage

1 – Vamos começar clonando o projeto
$git clone https://github.com/luizalabs/teresa.git
2 – Em seguda acesse a pasta teresa
$cd teresa
3 – Voltando para o commit antes da alteração
$git checkout 70da95bbe2197ce1db3b46e8945ab990b28ebd8a
4 – Visualizando dockerfile
$cat Dockerfile FROM golang:1.8 RUN mkdir -p /go/src/github.com/luizalabs/teresa WORKDIR /go/src/github.com/luizalabs/teresa COPY . /go/src/github.com/luizalabs/teresa RUN make build-server ENTRYPOINT ["./teresa-server"] CMD ["run"] EXPOSE 50051
5 – Fazendo build da imagem antiga
$docker build -t teresa-old .
6 – Listando imagens existentes
$docker images
Reduzindo imagens docker com MultiStage 1 Observe que a imagem tinha o tamanho de 823MB antes da alteração.

Depois do Multi Stage

1 – Indo para o commit depois da alteração
$git checkout 15de6e124ac1d081479320259c106f34628b989f
2 – Visualizando dockerfile novo
$cat Dockerfile FROM golang:1.8 AS builder WORKDIR /go/src/github.com/luizalabs/teresa COPY . /go/src/github.com/luizalabs/teresa RUN make build-server FROM debian:9-slim RUN apt-get update && \ apt-get install ca-certificates -y &&\ rm -rf /var/lib/apt/lists/* &&\ rm -rf /var/cache/apt/archives/* WORKDIR /app COPY –from=builder /go/src/github.com/luizalabs/teresa . ENTRYPOINT [“./teresa-server”] CMD [“run”] EXPOSE 50051
3 – Fazendo build da imagem nova
$docker build -t teresa-new .
4 – Listando imagens existentes
$docker images
Reduzindo imagens docker com MultiStage 2 Pós alteração, a imagem chegou a 172MB, com possibilidade de melhoras. Se você ainda não conhecia Multi Stage, agora já conhece e pode utilizar na busca por imagens menores. Se você já utiliza, compartilhe conosco o seu case também. 🙂
Bruno Rodrigues Leite Correia
Bruno Rodrigues Leite Correia
Sysadmin, evangelista devops, agile expert e desenvolvedor nas horas vagas.

Comentários

Deixar Um Comentário