← Voltar para todos os artigos

Conceitos básicos de Docker para Desenvolvedores

Publicado por Thiago Ramos em 07 de Julho, 2025
Banner do artigo sobre Docker

Todo desenvolvedor já viveu o pesadelo do "mas na minha máquina funciona!". Você passa dias criando uma aplicação, tudo roda perfeitamente no seu ambiente local, mas ao enviar para um colega ou para o servidor de produção, nada funciona. Versões de linguagens incompatíveis, dependências ausentes, configurações de sistema operacional... a lista de problemas é infinita. É exatamente aqui que o Docker entra, não como uma ferramenta, mas como uma revolução na forma como desenvolvemos e distribuímos software.

Este guia aprofundado irá te levar do zero ao entendimento prático de como usar Docker para criar ambientes consistentes, portáteis e prontos para produção.

A Analogia Definitiva: Contêineres de Navio

Pense nos contêineres de metal que vemos em navios. Antes deles, cada mercadoria (caixas, barris, sacos) era carregada uma a uma, um processo lento e propenso a danos. Com os contêineres, tudo é padronizado. Não importa o que há dentro; o guindaste, o navio e o caminhão só precisam saber como manusear o contêiner. O Docker faz o mesmo para o software: ele empacota sua aplicação, suas bibliotecas e todas as suas dependências em um "contêiner" de software padronizado, que pode ser executado em qualquer máquina que tenha o Docker instalado.

A Trindade do Docker: Imagem, Contêiner e Dockerfile

Para dominar o Docker, você precisa entender profundamente três conceitos:

1. Imagem (Image): A Planta da Casa

Uma imagem é um pacote imutável (não pode ser alterado) que contém tudo o que é necessário para executar sua aplicação: o código, um sistema operacional base (como Alpine Linux ou Ubuntu), bibliotecas, variáveis de ambiente e arquivos de configuração. Ela é como a planta de uma casa: a especificação completa de como a casa deve ser.

Um detalhe crucial é que as imagens são construídas em camadas (layers). Cada instrução em um `Dockerfile` cria uma nova camada sobre a anterior. O Docker é inteligente e armazena essas camadas em cache. Se você alterar apenas a última linha do seu `Dockerfile`, ele reutilizará todas as camadas anteriores, tornando o processo de build muito mais rápido.

2. Contêiner (Container): A Casa Construída

Um contêiner é uma instância viva e executável de uma imagem. Usando nossa analogia, se a imagem é a planta da casa, o contêiner é a casa real que você construiu. Você pode criar múltiplos contêineres (casas) a partir da mesma imagem (planta). Eles são leves e isolados uns dos outros e do sistema hospedeiro, garantindo que a aplicação dentro de um contêiner sempre se comporte da mesma forma, não importa onde seja executada.

3. Dockerfile: A Receita do Bolo

É o arquivo de texto onde você, como arquiteto, escreve as instruções passo a passo para construir sua imagem. É uma receita que o Docker segue à risca.

Guia Prático: Conteinerizando uma API Python com Boas Práticas

Vamos criar um `Dockerfile` otimizado para uma API em FastAPI.

Passo 1: Estrutura do Projeto

/minha-api-docker
|-- main.py
|-- requirements.txt
|-- Dockerfile
|-- .dockerignore

Os arquivos `main.py` e `requirements.txt` são os mesmos do guia anterior.

Crie o arquivo `.dockerignore`. Ele funciona como um `.gitignore`, impedindo que arquivos e pastas desnecessários sejam copiados para a imagem, tornando-a menor e o build mais rápido.

# Arquivo .dockerignore
__pycache__/
*.pyc
*.pyo
.pytest_cache/
venv/
.env

Passo 2: O Dockerfile com Multi-stage Build

Esta é uma técnica profissional. Usamos um "estágio de construção" para instalar dependências e um "estágio final" que só copia o necessário, resultando em uma imagem final muito menor e mais segura.

# --- Estágio 1: Builder ---
# Usamos uma imagem completa para instalar as dependências
FROM python:3.9 as builder

WORKDIR /app

# Instala o poetry para um gerenciamento de dependências mais robusto
RUN pip install poetry

# Copia apenas os arquivos de dependência
COPY poetry.lock pyproject.toml ./

# Instala as dependências em um ambiente virtual separado
RUN poetry install --no-dev --no-root

# --- Estágio 2: Final ---
# Usamos uma imagem "slim", muito menor, para a aplicação final
FROM python:3.9-slim

WORKDIR /app

# Copia o ambiente virtual com as dependências do estágio builder
COPY --from=builder /app/.venv ./.venv

# Adiciona o venv ao PATH do sistema
ENV PATH="/app/.venv/bin:$PATH"

# Copia o código da aplicação
COPY . .

# Expõe a porta que a aplicação vai usar
EXPOSE 80

# Comando para rodar a aplicação
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

Passo 3: Comandos Essenciais no Dia a Dia

Construir e rodar é só o começo. Aqui está uma "cola" dos comandos que você mais usará:

  • Construir a imagem: docker build -t minha-api .
  • Rodar o contêiner: docker run -d -p 8080:80 --name api-container minha-api
  • Ver contêineres em execução: docker ps
  • Ver todos os contêineres (incluindo parados): docker ps -a
  • Ver logs de um contêiner: docker logs -f api-container
  • Parar um contêiner: docker stop api-container
  • Remover um contêiner parado: docker rm api-container

O Próximo Nível: Orquestração com Docker Compose

Excelente, nossa API está em um contêiner! Mas e se ela precisar conversar com um banco de dados? Gerenciar múltiplos contêineres e suas redes na mão é inviável. Para isso, usamos o Docker Compose.

Crie um arquivo chamado docker-compose.yml na raiz do projeto:

version: '3.8'

services:
  # Serviço da nossa API Python
  api:
    build: . # Constrói a imagem a partir do Dockerfile no diretório atual
    container_name: minha_api_service
    ports:
      - "8000:80" # Mapeia a porta 80 do contêiner para a porta 8000 da sua máquina
    volumes:
      - .:/app # Monta o código local dentro do contêiner para live reload
    depends_on:
      - db

  # Serviço do Banco de Dados PostgreSQL
  db:
    image: postgres:13-alpine # Puxa a imagem oficial do PostgreSQL
    container_name: postgres_db
    volumes:
      - postgres_data:/var/lib/postgresql/data/ # Persiste os dados do banco
    environment:
      - POSTGRES_USER=thiago
      - POSTGRES_PASSWORD=senhaforte
      - POSTGRES_DB=minhaapi
    ports:
      - "5432:5432"

volumes:
  postgres_data: # Define o volume para ser gerenciado pelo Docker

Com este arquivo, para subir toda a sua aplicação (API + Banco de Dados), você roda um único comando:

docker-compose up -d

E para derrubar tudo:

docker-compose down

Conclusão: Você Agora Pensa em Contêineres

Parabéns! Você passou de um conceito abstrato para a conteinerização prática de uma aplicação com boas práticas e orquestração. Docker não é apenas uma ferramenta, é uma mudança de mentalidade. Ele te força a pensar em sua aplicação como um conjunto de serviços independentes, portáteis e escaláveis. Este é o alicerce do desenvolvimento de software moderno e da cultura DevOps.