Reduza o Tamanho das Suas Imagens Docker: Um Guia Completo para Iniciantes

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 BaseDescriçãoTamanho AproximadoUso Recomendado
ubuntu ou debianImagens completas de sistemas operacionais.100 MB a 200 MBAmbientes de desenvolvimento ou testes complexos.
slim (ex: node:20-slim)Versões mais leves de imagens oficiais.50 MB a 100 MBAplicações que precisam de ferramentas específicas não presentes no Alpine.
alpineUma distribuição Linux mínima, baseada no BusyBox.5 MB a 8 MBOtimização máxima, ideal para a maioria das aplicações.
scratchImagem completamente vazia.0 MBUsada 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:

  1. Estágio de Construção (builder): Usa uma imagem completa para instalar dependências e compilar o código.
  2. 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).

PassoDockerfileTamanho SimuladoRedução
0. Imagem “Gorda” (Single-Stage)FROM golang:1.21950 MB
1. Multi-Stage BuildFROM golang:1.21 AS builder25 MB97%
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.

PassoDescriçãoDockerfile (Trecho)Tamanho SimuladoRedução
1. Ponto de Partida (Imagem “Gorda”)Usa a imagem completa do Python.FROM python:3.111.1 GB
2. Imagem Base LeveTroca para a versão slim.FROM python:3.11-slim120 MB89%
3. Multi-Stage BuildUsa python:3.11 para build e python:3.11-slim para runtime.FROM python:3.11 AS builder
FROM python:3.11-slim AS runtime
90 MB25%
4. Alpine e LimpezaTroca para python:3.11-alpine e usa --no-cache no pip.FROM python:3.11-alpine55 MB39%

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:

  1. Escolha Imagens Base Mínimas (alpine, -slim, scratch).
  2. Implemente Multi-Stage Builds para separar o ambiente de build do ambiente de runtime.
  3. Use .dockerignore e Combine Comandos RUN limpando o cache na mesma linha.

Com essas dicas, suas imagens Docker estarão prontas para rodar de forma mais leve e rápida!

Anterior PostgreSQL e o Controle de Concorrência por Multiversão (MVCC)

About author

Você pode gostar também