Reduzindo o tamanho de suas imagens Docker (exemplo c/ Ruby)

Carlos Ribeiro
Feb 15 · 6 min read

Um grande problema de fazer deploy em produção utilizando docker é tamanho que as imagens geradas podem ocupar. Imagens muito grandes demoram para ser baixadas, consomem parte da sua cota de tráfego de rede no seu provedor de nuvem, custam para ser guardadas no seu repositório e não trazer nenhuma valor adicional.

Na maioria das situações, quando criamos uma imagem docker, colocamos alguns passos e dependências que as vezes não precisamos na imagem final que iremos rodar em produção

Vou usar como exemplo a seguinte aplicação: https://github.com/opensanca/opensanca_jobs

e esse é o Dockerfile que nos gera a imagem.

E o comando utilizado para realizar o build:

Esse build nos gerou uma imagem de quase 1GB!!! 😱.

Essa imagem tem algumas coisas desnecessárias, como o node e o yarn (só precisamos deles para precompilar os assets e não para a execução da aplicação).


Multi-Stage build

Na versão 17.05 o docker introduziu o conceito de Multi-stage Builds. Essa técnica de build nos permite dividir o nosso Dockerfile em várias declarações FROM. Cada declaração pode usar imagens base diferentes e você pode copiar artefatos de um estágio para outro, sem trazer as coisas que você não quer na imagem final. Nossa imagem final só contará com o build escrito no ultimo estágio.

Nosso Dockerfile separado em dois estágios: Pré Build e Final Build

No estágio de build, instalamos o node e o yarn, instalamos as dependências e precompilamos os assets. No estágio final, utilizamos uma imagem alpine (que é bem leve) com ruby, instalamos apenas dependências necessárias para rodar a aplicação e copiamos as bibliotecas e assets gerados no estágio anterior com o seguinte comando:

Fazendo o build desse Dockerfile, agora temos uma imagem de 562mb.

Já diminuímos quase pela metade, mas será que ainda podemos reduzir ainda mais o tamanho da imagem?? 🤔

Sim. Podemos fazer algumas outras ações para reduzir ainda mais essa imagem.


Removendo arquivos desnecessários

Podemos apagar da imagem arquivos que não são necessários, como cache e os arquivos utilizados temporariamente das bibliotecas instaladas. E também podemos adicionar um arquivo .dockerignore, dizendo para o build o que não enviar para a imagem.

Nesse novo Dockerfile, adicionamos esse trecho que remove caches e arquivos C temporários utilizados para buildar as bibliotecas.:

Além de incluir tambem o nosso .dockerignore para informar ao processo de build os arquivos que não precisam ser enviados para a imagem:

Com esses dois passos, agora nossa imagem tem 272MB.

Podemos dar um passo a mais. Para produção, não precisamos das pastas de teste, dependências do npm (pois elas já foram incluídas no asset pipeline), assets não precompilados, e caches.

Pra remover esses arquivos, podemos incluir uma estratégia de passar um argumento para o build, que iremos chamar de to_remove .

Nesse argumento, passaremos os arquivos que não desejamos para produção:

Perceba o --build-arg to_remove="spec node_modules app/assets vendor/assets lib/assets tmp/cache" Essas são as pastas que iremos remover do nosso processo de build, pois não necessitamos delas para rodar em produção.

Removendo esses arquivos, ficamos com uma imagem de 164MB, quase 6 vezes menor que a original.

Se você ainda não acreditou em mim e quer ver com seus próprios olhos, deixo aqui o pull request pra alteração que gerou essa redução em um projeto que estou trabalhando: https://github.com/opensanca/opensanca_jobs/pull/164

Cheers 🍻

Opensanca

Aqui falamos sobre opensource, inovação, tecnologia e happy hour, faça parte!

Carlos Ribeiro

Written by

Software Developer

Opensanca

Opensanca

Aqui falamos sobre opensource, inovação, tecnologia e happy hour, faça parte!