Thiago Lopes Silva
4 min readDec 28, 2021

Multi stage build docker: uma abordagem para otimizar o processo de criação da imagem docker

Boa Viagem, Recife/PE

Recentemente eu precisei buildar algumas imagens dockers e puder perceber que a imagem final ficava muito grande, o processo de build copiava código desnecessário para a imagem e a necessidade ter vários dockerfiles por Env.

Então….

“Como eu faço para otimizar este processo?

Usar a feature chamada Multi Stage Build nativa do Docker

Imagem 1

Esta feature foi introduzida no Docker Engine 17.05 e tem como objetivo facilitar o gerencimento de criação de imagens dockers com as seguintes funcionalidades:

  • Debugar um Build Stage Específico;
  • Usando um debug stagecom todas as ferramentas de depuração habilitados e um production stage;
  • Usando um testing stage que irá popular os dados da aplicação;
  • Diminuir a complexidade do processo de criação da imagem;

Fonte: Docker Multi Stage Build

Para exemplificar, eu irei usar um use case simples que irá buildar um projeto typescript e em seguida copiar o código compilado para a imagem, mas antes, em seguida, eu iriei mostrar um dockerfile com e sem o uso do multi stage.

  1. Dockerfile sem o Multi-Stage Build

Sem o uso do multi-stage o dockerfile irá ter bastantes etapas que podem levar o desenvolvedor a colocar informações desnecessárias no container final. Sendo este um dos fatores que devemos levar em consideração quando quisermos otimizar uma imagem docker.

Dockerfile 1

Eu criei o repositório https://github.com/thiagoolsilva/multi-stage-build-docker-example no qual contém a implementação do uso desta feature. Recomendo você clona-lo e testar a feature localmente.

Após buildar o dockerfile 1 eu notei que a imagem final ficou com um total de 260 MB. Contendo o código de desenvolvimento, build e libraries desnecessárias. Sendo necessário fazermos um processo de otimização da imagem final, conforme será descrito na próxima seção através do uso do Multi-Stage Build.

2. Dockerfile com Multi-Stage Build

Com o uso do Multi-Stage build vamos dividir o nosso dockerfile em duas etapas. Build e Run.

Imagem 2

A primeira etapa chamada de build, será responsável por instalar as dependências necessárias para buildar o código, buildar o código e por fim instalar as dependências que vão ser usadas em produção.

A segunda etapa chamada Run, será responsável por copiar apenas o código compilado da etapa anterior e configurar o código a ser executado através do uso do comando CMD.

Dito isso, vamos dividir o nosso dockerfile em duas etapas, consoante o arquivo dockerfile 2 abaixo.

Dockerfile 2

Atenção: Este post tem a premissa que você já possua alguns conceitos de docker.

Na primeira etapa, Build, descrita a partir da linha 15 à 30 no arquivo dockerfile 2, tem como objetivo executar todos os passos necessários para buildar o código e deixar disponível na imagem. Código este que será usado na próxima etapa chamada Run.

Um ponto importante que devemos observar após executar o comando abaixo é que esta etapa gerou uma imagem untaged docker de 254 MB.

 docker images

Na segunda etapa, Run, descrita a partir da linha 33 a 47 no arquivo dockerfile 2, tem como objetivo acessar os arquivos compilados na primeira etapa e em seguida copiar para a imagem final apenas o código necessário para executar a imagem, deixando desta forma os arquivos e dependências de compilação fora deste etapa para conseguirmos uma melhor otimização.

A possibilidade de copiar os arquivos da imagem/etapa anterior é feito a partir do uso do seguinte comando no dockerfile.

COPY --from=buildStage /app/dist/ /app/
COPY --from=buildStage /app/node_modules/ /app/node_modules

O comando COPY ,nativo do docker, tem a sintaxe de receber o source e o destino a ser enviado para a imagem docker nos argumentos. Neste caso, precisamos acrescentar mais um argumento chamado from no inicio para acessar os dados da etapa anterior através do uso do alias do stage que definimos na linha 16.

Prontamente, configuramos a imagem docker através do uso do comando CMD , conforme descrito na linha 47 do arquivo dockerfile 2, que tem como objetivo ser executado toda vez que um container for iniciado através da imagem que acabamos de criar.

Em seguida, após executar novamente o comando docker images pude notar que o tamanho final da imagem docker que usamos, ficou com um total de 121 MB. Tendo uma redução de 52,36% no tamanho total da imagem final.

Um ganho muito relevante que irá ajudar será na redução do tempo de baixar as imagens do dockerhub ou ECR ou até na questão da economia do dinheiro quando hospedado uma imagem mais otimizada em hubs dockers pagos.

Por fim, conseguimos otimizar o nosso processo de build do docker usando o Multi-Stage Build do docker e conseguimos otimizar o tamanho final da imagem contendo apenas o código relevante para executar o container.

Como sugestão de leitura, eu recomendo vocês irem até a documentação oficial desta feature e aprofundem mais sobre este assunto que tem bastante ganhos no dia a dia.

Conclusão

Em síntese espero que este post tenha ajudado vocês a entender um pouco do poder que a feature Multi-Stage Build do docker possui. Lembrem de sempre otimizar a sua imagem final e principalmente focar na segurança da imagem para evitar problemas.

Se tiverem alguma dúvida sobre o assunto, é so deixar o seu comentário.