Otimize o build das suas imagens docker com multi-stage
Neste post vamos falar um pouco sobre como criar imagens docker otimizadas para nossas aplicações através do multi-stage build.
A partir da versão 17.05 do Docker, lançada a 1 ano atrás, o time de desenvolvimento disponibilizou suporte a multi-stage build, basicamente, são extensões dos comandos FROM e COPY que nos permite criar um Dockerfile com múltiplas fases de build, ou steps.
Para este post irei utilizar como exemplo uma aplicação ASP.NET Core 2, onde o processo de build da imagem irá conter múltiplas fases, como restore, testes e etc. Nossa imagem final irá conter apenas o ambiente mínimo necessário para rodar a aplicação, sem dependências não produtivas.
Você pode baixar todos os arquivos do projeto no github e pular para a parte do Dockerfile, mas se desejar, vamos criar juntos o projeto web e os testes.
Aplicação
Iremos utilizar uma aplicação web convencional, criando através do cli.
Estou utilizando Ubuntu 16 para criar este post, mas você pode utilizar windows sem problemas.
No seu terminal, crie uma aplicação web e certifique-se de que ela esteja abrindo no navegador.
1 | mkdir aspnetproject && cd aspnetproject |
Com isso já temos nossa aplicação web disponível em http://localhost:5000/.
Testes
Iremos criar um projeto de teste para que possamos usar como exemplo.
1 | cd .. && mkdir tests && dotnet new mstest -o tests |
Agora que temos nossa aplicação e nossos testes, vamos colocar tudo isso dentro de uma imagem.
Dockerfile e as mudanças no FROM e no COPY
Antes de criamos nosso Dockerfile vamos entender quais melhorias ocorreram no FROM e no COPY.
Anteriormente, tinhamos apenas um comando FROM que era carregado inicialmente e estabelecia qual imagem seria utilizada, afetando todos os comandos subsequentes.
Com multi-stages podemos utilizar quantos comandos FROM desejarmos. Cada FROM é um novo estágio que substitui o anterior, é de fato uma nova imagem, totalmente independente e isolada. Desta forma, se no estágio inicial estivermos utilizando uma imagem com .NET Core e no último estágio estamos utilizando uma imagem sem suporte a .net core a imagem final gerada ficará sem este suporte.
Outra mudança no FROM ocorreu em sua assinatura, agora existe a possibilidade de nomear os estágios através da instrução as.
1 | FROM microsoft/aspnetcore-build:1.1 as build |
Neste primeiro momento podemos notar que cada etapa do build pode conter a imagem mais eficiente possível, seja uma etapa para executar um teste ou rodar a aplicação final.
Porém, nada disso faz muito sentido sem a possibilidade de utilizarmos informações das etapas anteriores, por isso, o comando COPY possui o argumento --from=<nome do estágio>, permitindo assim a copia de arquivos existentes em etapas anteriores
Exemplo:
1 | FROM microsoft/aspnetcore-build:1.1 as build |
Criando nosso Dockerfile
Nossa aplicação possui uma pasta para o projeto web e outra para o projeto de testes, precisamos garantir que somente se os testes estiverem passando a imagem será gerada.
1 | # Restaura e copia os arquivos do projeto |
Nas primeiras 3 etapas estamos utilizando a imagem oficial para o processo de build de aplicações ASP.NET Core 2.
FROM microsoft/aspnetcore-build:2.0
Esta imagem já possui alguns recursos como:
- .NET Core SDK
- NuGet com cache para pacotes comumente utilizados em projetos asp.net core
- Node.js
- Bower
- Gulp
Na primeira etapa eu copio os arquivos do projeto e faço o restore.
1 | COPY . . |
Na segunda etapa, o qual chamei de test-env, estou setando uma pasta local como pasta padrão /tests, copiando apenas a pasta com o projeto de testes a partir do base-env e rodando os testes.
1 | WORKDIR /tests |
Note que, nesse momento o contexto da imagem anterior já não é utilizado, logo, a pasta tests precisa ser copiada para a minha nova etapa através do comando COPY com o argumento --from=base.
Crio então uma nova etapa para a realização do publish do projeto web.
1 | FROM microsoft/aspnetcore-build:2.0 AS build-env |
Na etapa final estou utilizando a imagem microsoft/aspnetcore:2.0, o qual é mais otimizada para executar a aplicação, não possuindo as dependências que são geralmente utilizadas para os processos de build e testes.
FROM microsoft/aspnetcore:2.0
Novamente, posso copiar a pasta com o projeto web que foi gerada em etapas anteriores e então definir o entrypoint para rodar a aplicação, mesmo que ela tenha utilizado uma imagem completamente diferente.
1 | WORKDIR /app |
Espero que tenham gostado e que aproveitem ao máximo o multi-stage para criar imagens finais mais eficientes, seja em .NET ou quaisquer outras plataformas.


Comments