Docker Compose - orquestrando múltiplos containers
1. Retomando: do docker run repetido a um arquivo único No artigo anterior, subir uma API e um Postgres conectados exigiu dois comandos docker run longos, com flags de rede, volume e variáveis de ambiente para lembrar (e digitar) toda vez. Em um projeto real, com mais serviços — cache, fila, worker em background — isso rapidamente vira inviável de manter na cabeça ou em um script solto. O Docker Compose resolve isso descrevendo toda a aplicação multi-container em um único arquivo declarativo, versionado junto com o código. 2. O arquivo compose.yaml Compose lê um arquivo YAML (por convenção compose.yaml, ou o nome legado docker-compose.yml, ainda amplamente usado) descrevendo serviços (cada um vira um ou mais containers), redes e volumes: # compose.yaml services: api: build: . ports: - "8000:8000" environment: DATABASE_URL: postgresql://postgres:segredo@banco:5432/postgres depends_on: - banco banco: image: postgres:16 environment: POSTGRES_PASSWORD: segredo volumes: - pg-dados:/var/lib/postgresql/data volumes: pg-dados: Isso substitui inteiramente os dois docker run do artigo anterior. Uma diferença importante já aparece aqui: por padrão, Compose cria uma rede própria para o projeto e conecta todos os serviços a ela automaticamente — não é preciso um docker network create manual, nem declarar --network em cada serviço. Cada serviço já é acessível pelos demais pelo nome declarado em services: (aqui, banco resolve para o container do Postgres), exatamente como as redes definidas pelo usuário do artigo anterior. 3. Comandos essenciais do Compose docker compose up -d # sobe todos os serviços em segundo plano docker compose ps # lista os containers do projeto e seu status docker compose logs -f api # segue os logs de um serviço específico docker compose logs -f # segue os logs de todos os serviços, intercalados docker compose exec api bash # abre um shell dentro do container de um serviço docker compose stop # para os containers sem removê-los docker compose down # para e remove containers e rede (volumes nomeados sobrevivem) docker compose down -v # remove também os volumes — cuidado, apaga dados O padrão docker compose se repete: a maioria dos comandos aceita opcionalmente o nome de um serviço específico (como em logs -f api) ou afeta o projeto inteiro se omitido. 4. depends_on: ordem de início, não prontidão depends_on controla a ordem em que os containers são criados e iniciados — no exemplo acima, banco inicia antes de api. O que ele não garante é que o Postgres já esteja pronto para aceitar conexões nesse momento: o processo do Postgres pode levar alguns segundos para inicializar depois que o container começa a rodar, e uma API que tenta conectar imediatamente pode falhar nessa janela. A forma correta de esperar prontidão de verdade é um healthcheck: services: api: build: . depends_on: banco: condition: service_healthy banco: image: postgres:16 environment: POSTGRES_PASSWORD: segredo healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 3s retries: 5 Com condition: service_healthy, o Compose só considera banco "pronto" (e só então inicia api) depois que o healthcheck passar — muito mais confiável do que assumir um tempo fixo de espera ou implementar retry manual na aplicação (embora ter retry na aplicação continue sendo uma boa prática defensiva, mesmo com healthcheck). 5. Variáveis de ambiente e o arquivo .env Segredos e configuração que variam por ambiente (senha de banco, chaves de API) não deveriam ficar hardcoded no compose.yaml. Compose lê automaticamente um arquivo .env no mesmo diretório e substitui variáveis referenciadas com ${NOME}: # .env POSTGRES_PASSWORD=segredo-local API_PORT=8000 services: api: build: . ports: - "${API_PORT}:8000" banco: image: postgres:16 environment: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} Isso permite versionar o compose.yaml normalmente enquanto o .env (que contém segredos reais) fica de fora do controle de versão via .gitignore — o mesmo princípio de nunca commitar credenciais, aplicado à configuração do Compose. 6. Um ambiente completo: app + banco + cache Estendendo o exemplo para um cenário mais realista, com Redis como cache além da API e do Postgres: services: api: build: . ports: - "8000:8000" environment: DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@banco:5432/postgres REDIS_URL: redis://cache:6379 depends_on: banco: condition: service_healthy cache: condition: service_started banco: image: postgres:16 environment: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - pg-dados:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s retries: 5 cache: image: redis:7-alpine volumes: - redis-dados:/data volumes: pg-dados: redis-dados: Um docker compose up -d sobe os três serviços, na ordem certa, já conectados entre si e com dados persistentes — o equivalente a uma infraestrutura local completa de desenvolvimento, reproduzível em qualquer máquina que tenha Docker instalado, sem instalar Postgres ou Redis diretamente no sistema. 7. Escalando um serviço Para rodar múltiplas instâncias do mesmo serviço (por exemplo, vários workers processando uma fila em paralelo): docker compose up -d --scale worker=3 Isso só funciona sem conflito para serviços que não publicam uma porta fixa do host (ports:) — três containers não podem, todos, mapear a porta 8000 do host simultaneamente. Serviços pensados para escalar dessa forma tipicamente não expõem porta ao host diretamente, sendo acessados por outro serviço (como um proxy reverso) através da rede interna do Compose. 8. Conclusão e próximos passos Docker Compose transforma uma coleção de comandos docker run frágeis e difíceis de lembrar em um arquivo único, declarativo e versionável, que descreve toda a aplicação — serviços, rede, volumes, ordem de inicialização e variáveis de ambiente. No último artigo desta série, o assunto vai para práticas de nível de produção: multi-stage builds para reduzir drasticamente o tamanho final da imagem, segurança (rodar sem root, scan de vulnerabilidades) e como tudo isso se encaixa em um pipeline de CI/CD. Imagem de capa: Logo oficial do Docker — Wikimedia Commons, fonte: docker.com/company/newsroom/media-resources Referências: Docker Documentation — Compose Compose File Reference
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to