Docker e JavaScript: conteinerização de aplicações Node.js — guia completo
No desenvolvimento web moderno, a conteinerização tornou-se não apenas uma tendência, mas uma necessidade. O Docker permite empacotar uma aplicação JavaScript junto com todas as suas dependências, configurações e ambiente de execução em um contêiner isolado, que funcionará de forma idêntica em qualquer sistema — do laptop do desenvolvedor ao servidor de produção. Neste artigo, vamos analisar o processo de conteinerização de aplicações Node.js do zero, abordando as melhores práticas e cenários de uso típicos.
Fundamentos do Docker para desenvolvedores JavaScript
Antes de mergulharmos nos detalhes, vamos entender os conceitos-chave do Docker aplicados ao ecossistema JavaScript.
- Imagem (Image) — um modelo imutável contendo código, ambiente de execução Node.js, dependências (node_modules) e configurações. As imagens são armazenadas em um registro (por exemplo, Docker Hub).
- Contêiner (Container) — uma instância em execução de uma imagem. Cada contêiner é isolado dos outros e do sistema host.
- Dockerfile — instruções para construir a imagem. Para aplicações Node.js, este é o arquivo principal que criaremos.
- docker-compose — ferramenta para definir e executar aplicações com vários contêineres (por exemplo, Node.js + MongoDB + Redis).
Criando um Dockerfile para uma aplicação Node.js
Vamos começar com uma aplicação Express simples. Crie um arquivo Dockerfile na raiz do projeto:
# Imagem base Node.js 18 (Alpine — versão leve)FROM node:18-alpine
# Cria o diretório de trabalho dentro do contêinerWORKDIR /usr/src/app
# Copia package.json e package-lock.json (se existir)COPY package*.json ./
# Instala dependências de produçãoRUN npm ci --only=production
# Copia o código-fonte da aplicaçãoCOPY . .
# Expõe a porta que a aplicação irá escutarEXPOSE 3000
# Comando para iniciar a aplicaçãoCMD ["node", "src/index.js"]Pontos importantes para projetos JavaScript:
- A ordem das instruções é importante. Copiar package.json antes do código permite que o Docker armazene em cache a camada de instalação das dependências. Se você alterar apenas o código, as dependências não serão reinstaladas — isso acelera a construção.
- Use
npm ciem vez denpm install— é mais rápido e garante que as versões das dependências correspondam ao arquivo de lock. - A versão Alpine da imagem Node.js (~120 MB em vez de ~900 MB) reduz significativamente o tamanho da imagem final.
Trabalhando com docker-compose: conectando Node.js ao banco de dados
Aplicações reais raramente usam apenas um contêiner. O docker-compose.yml permite executar Node.js, MongoDB e, por exemplo, Redis com um único comando:
version: '3.8'services: app: build: . ports: - "3000:3000" environment: - NODE_ENV=production - MONGODB_URI=mongodb://mongo:27017/myapp - REDIS_URL=redis://redis:6379 depends_on: - mongo - redis volumes: - ./logs:/usr/src/app/logs
mongo: image: mongo:6 volumes: - mongo_data:/data/db ports: - "27017:27017"
redis: image: redis:7-alpine ports: - "6379:6379"
volumes: mongo_data:Como funciona:
- O serviço
appé construído a partir do nosso Dockerfile. - Os serviços
mongoeredisusam imagens prontas do Docker Hub. - As variáveis de ambiente
MONGODB_URIeREDIS_URLsão passadas para a aplicação — observe que usamos os nomes dos serviços como hostname (o DNS do Docker os resolve automaticamente para os endereços IP dos contêineres). - O volume
mongo_d