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