Docker para Desenvolvedores: Imagens, Contêineres e Compose
Você escreveu um código que funciona na sua máquina, mas quando um colega de equipe tenta executá-lo, ele se depara com dependências ausentes, conflitos de versão ou erros obscuros. O Docker resolve isso empacotando sua aplicação e seu ambiente em uma única unidade portátil. Neste guia, você aprenderá os fundamentos do Docker: imagens, contêineres e Compose, com passos práticos para conteinerizar uma aplicação web simples.
O que é Docker?
Docker é uma plataforma para criar, executar e compartilhar aplicações em contêineres. Um contêiner é um pacote executável leve e autônomo que inclui tudo o que é necessário para executar um software: código, runtime, ferramentas de sistema, bibliotecas e configurações. Os contêineres isolam as aplicações umas das outras e do sistema host, garantindo consistência entre desenvolvimento, testes e produção.
Imagens vs Contêineres: O Projeto e a Instância em Execução
Uma imagem é um modelo somente leitura que define um contêiner. É como uma classe na programação orientada a objetos. Um contêiner é uma instância executável de uma imagem, como um objeto. Você pode criar muitos contêineres a partir da mesma imagem.
As imagens são criadas a partir de um Dockerfile, um arquivo de texto com instruções. Os contêineres são iniciados com docker run.
Principais diferenças
| Imagem | Contêiner |
|---|---|
| Modelo somente leitura | Instância executável |
Criada com docker build | Iniciada com docker run |
| Armazenada em registro (ex.: Docker Hub) | Executa no host |
| Sistema de arquivos em camadas | Camada gravável por cima |
Criando Sua Primeira Imagem
Vamos conteinerizar uma aplicação Node.js simples. Crie um arquivo chamado Dockerfile:
# Use um runtime Node.js oficial como imagem pai
FROM node:18-alpine
# Defina o diretório de trabalho
WORKDIR /app
# Copie o package.json e instale as dependências
COPY package*.json ./
RUN npm install
# Copie o restante da aplicação
COPY . .
# Exponha a porta em que a aplicação é executada
EXPOSE 3000
# Defina o comando para executar a aplicação
CMD ["node", "server.js"]
Crie a imagem com uma tag:
docker build -t my-node-app .
A flag -t nomeia e marca a imagem. O . no final especifica o contexto de build (diretório atual).
Executando Contêineres
Inicie um contêiner a partir da sua imagem:
docker run -p 3000:3000 -d --name my-app my-node-app
-p 3000:3000: Mapeia a porta 3000 no host para a porta 3000 no contêiner.-d: Executa o contêiner em modo detached (em segundo plano).--name my-app: Atribui um nome ao contêiner para facilitar a referência.
Verifique os contêineres em execução com docker ps. Pare o contêiner com docker stop my-app.
Gerenciando Dados com Volumes
Os contêineres são efêmeros; os dados dentro deles são perdidos quando o contêiner é removido. Para persistir dados, use volumes. Por exemplo, para persistir dados de banco de dados:
docker run -v my-db-data:/var/lib/mysql -d mysql:8
Isso cria um volume nomeado my-db-data que sobrevive a reinicializações e remoções do contêiner.
Simplificando Aplicações Multi-Contêiner com Docker Compose
Aplicações reais frequentemente precisam de múltiplos serviços: um servidor web, um banco de dados, um cache, etc. O Docker Compose permite definir e executar aplicações multi-contêiner com um único arquivo YAML.
Crie um docker-compose.yml:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
- db
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
db:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
volumes:
postgres_data:
Execute tudo com:
docker compose up -d
O Compose cria a imagem web, inicia o banco de dados e os conecta em uma rede compartilhada. O depends_on garante que o banco de dados inicie antes do serviço web.
Comandos Essenciais do Docker
docker ps -a: Lista todos os contêineres (incluindo os parados).docker images: Lista as imagens.docker logs my-app: Visualiza os logs do contêiner.docker exec -it my-app sh: Abre um shell dentro de um contêiner em execução.docker compose down: Para e remove contêineres, redes e volumes definidos no arquivo Compose.
Boas Práticas para Desenvolvimento
- Use .dockerignore para excluir arquivos (ex.: node_modules) do contexto de build, acelerando as builds.
- Aproveite builds multi-stage para manter as imagens de produção pequenas. Por exemplo, crie sua aplicação em um estágio e copie apenas os artefatos necessários para uma imagem de runtime enxuta.
- Fixe versões no seu Dockerfile (ex.:
node:18-alpineem vez denode:latest) para reprodutibilidade. - Execute como usuário não-root por segurança. Adicione
USER nodeapós copiar os arquivos.
FAQ
Qual é a diferença entre uma imagem Docker e um contêiner?
Uma imagem é um modelo somente leitura que define a aplicação e seu ambiente. Um contêiner é uma instância em execução dessa imagem. Você pode criar múltiplos contêineres a partir da mesma imagem.
Como faço para persistir dados no Docker?
Use volumes. Volumes nomeados (ex.: docker run -v my-vol:/data) são armazenados fora do sistema de arquivos do contêiner e persistem entre reinicializações e remoções do contêiner.
Quando devo usar o Docker Compose?
Use o Compose quando sua aplicação consiste em múltiplos serviços (ex.: web + banco de dados) que precisam ser executados juntos. Ele simplifica a orquestração para desenvolvimento e testes.
Pronto para conteinerizar seu próximo projeto? Comece escrevendo um Dockerfile simples e executando seu primeiro contêiner. Para mais ferramentas de desenvolvimento, confira o TryQuickToolBox's JSON Formatter para validar respostas de API enquanto testa seus serviços conteinerizados.