Reduza o Tamanho das Suas Imagens Docker: Um Guia Completo para Iniciantes
Se você já começou a criar suas próprias imagens, deve ter notado que elas podem ficar enormes rapidamente. Imagens grandes consomem mais espaço em disco, demoram mais para serem baixadas e aumentam o tempo de deploy.
Mas não se preocupe! Otimizar imagens Docker é uma habilidade essencial e, com as técnicas certas, você pode reduzir o tamanho final em até 90%. Neste guia completo, vamos explorar as técnicas mais eficazes na hora de reduzir o tamanho de uma imagem!
Por Que Imagens Grandes São um Problema?
O Docker utiliza um sistema de arquivos em camadas (layers). Cada instrução no seu Dockerfile (como RUN, COPY, ADD) cria uma nova camada. O problema é que, mesmo que você exclua um arquivo em uma camada posterior, a camada anterior com o arquivo original ainda existe na imagem final, contribuindo para o seu tamanho.
A meta da otimização é garantir que a imagem final contenha apenas o essencial para a aplicação rodar, eliminando ferramentas de build, caches e arquivos temporários.
1. A Regra de Ouro: Escolha a Imagem Base Certa
A fundação da sua imagem é a primeira e mais fácil otimização.
| Imagem Base | Descrição | Tamanho Aproximado | Uso Recomendado |
|---|---|---|---|
ubuntu ou debian | Imagens completas de sistemas operacionais. | 100 MB a 200 MB | Ambientes de desenvolvimento ou testes complexos. |
slim (ex: node:20-slim) | Versões mais leves de imagens oficiais. | 50 MB a 100 MB | Aplicações que precisam de ferramentas específicas não presentes no Alpine. |
alpine | Uma distribuição Linux mínima, baseada no BusyBox. | 5 MB a 8 MB | Otimização máxima, ideal para a maioria das aplicações. |
scratch | Imagem completamente vazia. | 0 MB | Usada em Multi-Stage Builds para binários estáticos (ex: Go). |
Dica para Iniciantes: Sempre comece com a versão mais leve possível, como alpine ou a versão -slim da sua linguagem.
2. O Poder do Multi-Stage Build (Construção em Múltiplos Estágios)
Esta é a técnica mais eficaz para a maioria das aplicações que precisam de um processo de build (compilação). Ela resolve o problema das camadas, pois permite que você use uma imagem grande para construir e, em seguida, descarte tudo, copiando apenas o resultado final para uma imagem mínima.
A estrutura é simples:
- Estágio de Construção (
builder): Usa uma imagem completa para instalar dependências e compilar o código. - Estágio Final (
production): Usa uma imagem base mínima e copia apenas os artefatos necessários do estágio de construção.
Exemplo Prático (Aplicação Go)
O Go é um excelente exemplo, pois gera um binário estático que pode rodar em qualquer lugar, até mesmo na imagem scratch (0MB).
| Passo | Dockerfile | Tamanho Simulado | Redução |
|---|---|---|---|
| 0. Imagem “Gorda” (Single-Stage) | FROM golang:1.21 | 950 MB | – |
| 1. Multi-Stage Build | FROM golang:1.21 AS builder | 25 MB | 97% |
FROM scratch |
# ----------------------------------------------------------------
# ESTÁGIO 1: BUILDER (Construção)
# Usamos a imagem completa do Go para compilar o código
FROM golang:1.21 AS builder
WORKDIR /app
# Copia o código fonte
COPY . .
# Compila a aplicação, garantindo que o binário seja estático
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-s -w' -o /app/main .
# ----------------------------------------------------------------
# ----------------------------------------------------------------
# ESTÁGIO 2: PRODUCTION (Produção - Imagem Final)
# Usamos a imagem mais leve possível: scratch (vazia)
FROM scratch
# Define o diretório de trabalho
WORKDIR /app
# Copia APENAS o binário compilado do estágio 'builder'
COPY --from=builder /app/main /app/main
# Comando para iniciar a aplicação
CMD ["/app/main"]
O Resultado: A imagem final tem apenas o tamanho do binário compilado (alguns MB), pois o scratch não adiciona nada. Isso pode reduzir o tamanho de uma imagem em dezenas de vezes.
3. Otimizações Adicionais Essenciais
Além do Multi-Stage Build, estas práticas garantem que você não desperdice espaço:
A. Use .dockerignore
O arquivo .dockerignore impede que arquivos e pastas desnecessárias (como .git, node_modules local, logs, etc.) sejam copiados para o contexto de build.
Por que é importante? Se você copia 1GB de arquivos desnecessários para o contexto de build, o Docker cria uma camada de 1GB, mesmo que você os exclua depois. O .dockerignore impede que essa camada seja criada.
Exemplo de .dockerignore:
# Ignora pastas de controle de versão e logs
.git
.svn
logs
tmp
# Ignora dependências locais (serão instaladas dentro do container)
node_modules
# Ignora arquivos de build e cache
dist
build
.cache
B. Combine Comandos RUN e Limpe o Cache
Cada comando RUN cria uma nova camada. Para evitar camadas intermediárias grandes e desnecessárias, combine comandos usando && e limpe o cache na mesma linha.
Exemplo (Debian/Ubuntu):
# Ruim: 3 camadas, a camada 2 contém o cache do apt
RUN apt-get update
RUN apt-get install -y some-package
RUN rm -rf /var/lib/apt/lists/*
# Bom: 1 camada, o cache é limpo imediatamente
RUN apt-get update && \
apt-get install -y some-package && \
rm -rf /var/lib/apt/lists/*
Exemplo (Alpine):
# O apk também precisa de limpeza
RUN apk add --no-cache some-package
O parâmetro --no-cache no apk (Alpine Package Keeper) evita a criação de arquivos de cache do gerenciador de pacotes, economizando espaço.
C. Use a Instrução COPY de Forma Inteligente (Cache de Build)
A ordem das instruções no Dockerfile é crucial para o cache de build. O Docker só refaz as camadas a partir do ponto em que algo mudou.
Sempre coloque as instruções que mudam com menos frequência (como a instalação de dependências) antes das instruções que mudam com frequência (como a cópia do seu código fonte).
Exemplo (Node.js):
WORKDIR /app
# 1. Copia apenas o package.json (muda pouco)
COPY package*.json ./
# 2. Instala dependências (muda apenas se o package.json mudar)
RUN npm install --production
# 3. Copia o código fonte (muda a cada alteração)
COPY . .
Se você mudar apenas um arquivo de código (passo 3), o Docker reutiliza o cache dos passos 1 e 2, economizando tempo de build.
Vamos simular um caso real:
Vamos simular a otimização de uma aplicação Python simples, como um servidor Flask, e ver o impacto de cada técnica.
| Passo | Descrição | Dockerfile (Trecho) | Tamanho Simulado | Redução |
|---|---|---|---|---|
| 1. Ponto de Partida (Imagem “Gorda”) | Usa a imagem completa do Python. | FROM python:3.11 | 1.1 GB | – |
| 2. Imagem Base Leve | Troca para a versão slim. | FROM python:3.11-slim | 120 MB | 89% |
| 3. Multi-Stage Build | Usa python:3.11 para build e python:3.11-slim para runtime. | FROM python:3.11 AS builderFROM python:3.11-slim AS runtime | 90 MB | 25% |
| 4. Alpine e Limpeza | Troca para python:3.11-alpine e usa --no-cache no pip. | FROM python:3.11-alpine | 55 MB | 39% |
Dockerfile Exemplo otimizado:
# ----------------------------------------------------------------
# ESTÁGIO 1: BUILDER (Instalação de Dependências)
# Usamos uma imagem Alpine para instalar as dependências
FROM python:3.11-alpine AS builder
# Define o diretório de trabalho
WORKDIR /app
# Copia o arquivo de dependências
COPY requirements.txt .
# Instala as dependências e usa --no-cache-dir para limpar o cache do pip
RUN pip install --no-cache-dir -r requirements.txt
# ----------------------------------------------------------------
# ESTÁGIO 2: PRODUCTION (Imagem Final)
# Usamos a mesma imagem base mínima
FROM python:3.11-alpine
WORKDIR /app
# Copia as dependências instaladas do estágio 'builder'
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
# Copia o código da aplicação
COPY . .
# Comando para iniciar a aplicação
CMD ["python", "app.py"]
Conclusão
Reduzir o tamanho das imagens Docker não é apenas uma questão de estética; é uma prática essencial para eficiência, segurança e velocidade dos seus deploys.
Comece hoje mesmo aplicando estas técnicas:
- Escolha Imagens Base Mínimas (
alpine,-slim,scratch). - Implemente Multi-Stage Builds para separar o ambiente de build do ambiente de runtime.
- Use
.dockerignoree Combine ComandosRUNlimpando o cache na mesma linha.
Com essas dicas, suas imagens Docker estarão prontas para rodar de forma mais leve e rápida!
About author
Você pode gostar também
Domine o Jenkins: Crie Pipelines eficientes com Jenkinsfile e Groovy
O Jenkinsfile é a maneira mais recomendada para criar Pipelines no Jenkins. Utilizando as melhores práticas, podemos colocar o arquivo na raiz de um repositório Git. Essa técnica nos permite
Entenda o ciclo de vida dos arquivos no Git e facilite seu trabalho
Git é um versionador de código fonte fácil de usar, isso quase todos sabem, entretanto sua experiência de uso pode ser bem confusa em alguns casos. Convido-os a uma breve
Rodando Agentes de IA no Kubernetes com o Agent Sandbox
O cenário da inteligência artificial está passando por uma enorme mudança de paradigma. Num passado não tão distante da IA Generativa, a interação com um agente de IA era comumente






