Docker para Desenvolvedores: Imagens, Contêineres e Compose
Você criou um aplicativo que funciona na sua máquina, mas quando o implanta, as coisas quebram. Dependências estão faltando, as versões diferem e a configuração é uma bagunça. O Docker resolve isso empacotando sua aplicação e seu ambiente em uma única unidade portátil. Este artigo explica imagens Docker, contêineres e Docker Compose com exemplos práticos para que você possa otimizar seu fluxo de trabalho de desenvolvimento.
O que é uma Imagem Docker?
Uma imagem Docker é um modelo somente leitura que contém o código da sua aplicação, runtime, bibliotecas, variáveis de ambiente e arquivos de configuração. Pense nela como um snapshot do ambiente da sua aplicação. As imagens são construídas a partir de um Dockerfile, que é um arquivo de texto com instruções.
Aqui está um Dockerfile simples para uma aplicação Node.js:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Cada instrução cria uma camada. As camadas são armazenadas em cache, então reconstruir após uma alteração no código apenas reexecuta as etapas a partir da camada alterada. Isso torna as builds rápidas.
O que é um Contêiner Docker?
Um contêiner é uma instância em execução de uma imagem. É um processo isolado com seu próprio sistema de arquivos, rede e espaço de processos, mas compartilha o kernel do sistema operacional host. Isso torna os contêineres leves e rápidos de iniciar em comparação com máquinas virtuais.
Para executar um contêiner a partir de uma imagem:
docker run -d -p 3000:3000 --name myapp my-node-app
Isso inicia um contêiner em modo detached (-d), mapeia a porta 3000 no host para a porta 3000 no contêiner (-p 3000:3000) e o nomeia myapp.
Os contêineres são efêmeros por padrão. Qualquer dado gravado dentro é perdido quando o contêiner para. Para persistir dados, use volumes:
docker run -v /host/data:/app/data my-node-app
Por que Usar Docker Compose?
Aplicações reais frequentemente precisam de múltiplos serviços: um servidor web, um banco de dados, um cache, etc. Executar cada um com docker run e vinculá-los manualmente é tedioso. O Docker Compose permite definir e executar aplicações multi-contêiner com um único arquivo YAML.
Aqui está um docker-compose.yml para uma aplicação Node.js com PostgreSQL e Redis:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
cache:
image: redis:7
volumes:
pgdata:
Execute tudo com:
docker compose up -d
O Compose cria uma rede para que os serviços possam se alcançar pelo nome (por exemplo, db, cache). Ele também lida com a criação de volumes e a ordem de dependência.
Comandos Docker Essenciais para Desenvolvedores
docker build -t myapp .– Constrói uma imagem a partir de um Dockerfile no diretório atual.docker images– Lista as imagens locais.docker ps– Lista os contêineres em execução (adicione-apara todos).docker exec -it myapp sh– Abre um shell dentro de um contêiner em execução.docker logs myapp– Visualiza os logs do contêiner.docker compose down– Para e remove contêineres, redes e volumes definidos no Compose.
Melhores Práticas para Docker em Desenvolvimento
- Use .dockerignore para excluir node_modules, .git e outros arquivos desnecessários do contexto de build.
- Aproveite builds multi-stage para manter as imagens de produção pequenas. Por exemplo, construa sua aplicação em um estágio e copie apenas os artefatos para uma imagem de runtime mínima.
- Fixe as versões no seu Dockerfile (por exemplo,
node:18-alpineem vez denode:latest) para builds reproduzíveis. - Use volumes para o código durante o desenvolvimento para hot reloading:
volumes: - .:/appno Compose. - Não execute como root em produção. Crie um usuário não-root no seu Dockerfile.
Armadilhas Comuns e Como Evitá-las
Armadilha 1: Imagens inchadas. Usar uma imagem base de sistema operacional completa como ubuntu pode resultar em imagens de 1GB+. Use variantes slim ou alpine.
Armadilha 2: Builds lentas. Ordene as instruções do Dockerfile da menos para a mais frequentemente alterada. Copie os arquivos de pacote e instale as dependências antes de copiar o código-fonte.
Armadilha 3: Perda de dados. Sempre use volumes para bancos de dados. Sem eles, os dados desaparecem quando o contêiner é removido.
Armadilha 4: Confusão de rede. No Compose, os serviços se comunicam por uma rede padrão usando os nomes dos serviços como hostnames. Não use localhost para alcançar outro serviço.
FAQ
Qual é a diferença entre uma imagem e um contêiner?
Uma imagem é um modelo somente leitura; um contêiner é uma instância em execução dessa imagem. Você pode executar vários contêineres a partir da mesma imagem.
Posso usar Docker Compose em produção?
O Compose é principalmente para desenvolvimento e testes. Para produção, considere orquestradores como Kubernetes ou Docker Swarm, embora o Compose possa funcionar para implantações simples.
Como atualizo um contêiner com novo código?
Reconstrua a imagem com docker build e então recrie o contêiner: docker compose up -d --build.
Pronto para otimizar seu fluxo de trabalho de desenvolvimento? Experimente nosso Analisador de Logs Nginx para depurar os logs do seu servidor web em contêineres rapidamente.