Node.js: Implantação e Otimização
Última atualização: 2026-08-26
Depois que Charlie implantou a API no servidor de produção usando o node app.js, o processo travava com frequência devido a exceções não capturadas, causando interrupções prolongadas no serviço durante a operação autônoma. Ele implementou o PM2 para habilitar o reinício automático e o modo de cluster, combinado com a conteinerização do Docker e um proxy reverso do Nginx, o que aumentou a disponibilidade do serviço de 95% para 99,9% — e ele finalmente conseguiu dormir tranquilo.
Você aprenderá:
- Gerenciamento de processos PM2 (inicialização / reinicialização / logs / monit)
- Noções básicas do Docker (Dockerfile / docker-compose)
- Gerenciamento de variáveis de ambiente (.env / Configuração de produção)
- Módulo de cluster
- Análise de desempenho (console.time / hooks de desempenho / clinic.js)
- Definição dos parâmetros de avaliação dos exames de saúde
- Configuração do proxy reverso do Nginx
1. Gestão de Processos PM2
(1) Por que precisamos de um gerente de processos?
Em um ambiente de produção, os processos do Node.js podem travar devido a exceções não capturadas, vazamentos de memória ou recursos de sistema insuficientes. Se iniciado diretamente com node app.js, o processo não será reiniciado após uma falha, resultando em interrupção do serviço. O PM2 é o gerenciador de processos mais popular para o Node.js, oferecendo recursos de reinício automático, gerenciamento de logs, balanceamento de carga e monitoramento.
▶ Exemplo:(2) Instalação e comandos básicos
npm install -g pm2
pm2 start app.js --name "my-api"
pm2 restart my-api
pm2 stop my-api
pm2 remove my-api
pm2 logs my-api
pm2 monit
(3) Referência rápida aos comandos comuns do PM2
| Comando | Função | Casos de uso comuns |
|---|---|---|
pm2 start app.js |
Abrir o aplicativo | Primeira implantação |
pm2 restart <name> |
Reinicie o aplicativo | Após atualizar o código |
pm2 reload <name> |
Reinício sem tempo de inatividade | Atualização do ambiente de produção |
pm2 stop <name> |
Serviço suspenso | Em manutenção |
pm2 delete <name> |
Excluir processo | Remover completamente |
pm2 logs [name] |
Ver registro | Solucionar problemas |
pm2 monit |
Painel de monitoramento em tempo real | Monitorar o uso de recursos |
pm2 list |
Lista de processos | Visualizar status de execução |
pm2 describe <name> |
Detalhes do processo | Diagnósticos aprofundados |
pm2 save |
Salvar lista de processos | Configuração de inicialização |
pm2 startup |
Gerar um script de inicialização | Restaurar automaticamente após a reinicialização do servidor |
▶ Exemplo: Inicialização e monitoramento do PM2
pm2 start app.js --name "charlie-api" -i max
pm2 monit
pm2 save
pm2 startup
┌─────┬──────────────┬─────────────┬─────────┬─────────┬──────────┐
│ id │ name │ mode │ ↺ │ status │ cpu │
├─────┼──────────────┼─────────────┼─────────┼─────────┼──────────┤
│ 0 │ charlie-api │ cluster │ 15 │ online │ 12% │
│ 1 │ charlie-api │ cluster │ 2 │ online │ 8% │
│ 2 │ charlie-api │ cluster │ 0 │ online │ 5% │
│ 3 │ charlie-api │ cluster │ 1 │ online │ 3% │
└─────┴──────────────┴─────────────┴─────────┴─────────┴──────────┘
2. Noções básicas sobre o Docker
(1) Por que usar o Docker
O Docker empacota aplicativos e suas dependências em imagens de contêiner, garantindo a consistência entre os ambientes de desenvolvimento, teste e produção. O código do Charlie, que antes havia sido executado com sucesso em sua máquina local, apresentou erros após ser implantado no servidor devido a diferenças nas versões do Node.js, mas o Docker resolveu completamente esse problema.
▶ Exemplo:(2) Como escrever um Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js
USER node
CMD ["node", "app.js"]
▶ Exemplo:(3) Orquestração de múltiplos serviços com o Docker Compose
version: "3.8"
services:
api:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- MONGO_URI=mongodb://mongo:27017/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- mongo
- redis
restart: always
healthcheck:
test: ["CMD", "node", "healthcheck.js"]
interval: 30s
timeout: 3s
retries: 3
mongo:
image: mongo:7
volumes:
- mongo-data:/data/db
restart: always
redis:
image: redis:7-alpine
restart: always
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- api
restart: always
volumes:
mongo-data:
(4) Comparação dos métodos de implantação
| Dimensão | Implantação em hardware físico | Implantação no Docker | Implantação em serviço em nuvem |
|---|---|---|---|
| Consistência ambiental | Insuficiente | Excelente | Excelente |
| Velocidade de implantação | Lenta | Rápida | Mais rápida |
| Complexidade operacional | Alta | Média | Baixa |
| Utilização de recursos | Alta | Média | Sob demanda |
| Flexibilidade de expansão | Ruim | Boa | Excelente |
| Custo | Baixo | Médio | Alto |
| Casos de uso | Pequenos projetos | Projetos de médio e grande porte | Projetos de nível empresarial |
| Curva de aprendizagem | Baixa | Média | Alta |
▶ Exemplo: Compilando e executando uma imagem do Docker
docker build -t charlie-api:1.0 .
docker run -d -p 3000:3000 --env-file .env charlie-api:1.0
docker-compose up -d
docker-compose logs -f api
3. Gerenciamento de variáveis de ambiente
(1) Por que as variáveis de ambiente são necessárias?
As configurações, como endereços de bancos de dados, números de porta e senhas, variam de acordo com os diferentes ambientes (desenvolvimento, teste e produção). Codificar essas configurações diretamente no código cria riscos de segurança e dificulta a alternância entre os ambientes. As variáveis de ambiente separam a configuração do código, em conformidade com a metodologia Twelve-Factor App.
▶ Exemplo:(2) Arquivos dotenv e .env
const dotenv = require('dotenv');
dotenv.config({ path: `.env.${process.env.NODE_ENV || 'development'}` });
const config = {
port: parseInt(process.env.PORT, 10) || 3000,
mongoUri: process.env.MONGO_URI,
jwtSecret: process.env.JWT_SECRET,
redisUrl: process.env.REDIS_URL,
logLevel: process.env.LOG_LEVEL || 'info',
};
module.exports = config;
▶ Exemplo:(3) Estratégia de configuração do ambiente de produção
.env # Default Configuration(Do not submit Git)
.env.development # Development Environment
.env.test # Test Environment
.env.production # Production Environment(Through CI/CD Inject,Do not コミット to the code リポジトリ)
(4) Comparação entre métodos de gerenciamento de variáveis de ambiente
| Método | Segurança | Flexibilidade | Colaboração em equipe | Casos de uso |
|---|---|---|---|---|
.env Arquivo |
Médio | Alto | Bom | Projetos gerais |
| Variáveis de ambiente do sistema | Alto | Baixo | Insuficiente | Fácil implantação |
| Segredos do Docker | Alto | Médio | Bom | Ambiente do Docker |
| K8s Secrets | Alto | Alto | Excelente | Kubernetes |
| Centro de Configuração da Plataforma em Nuvem | Alta | Alta | Excelente | Projetos nativos da nuvem |
▶ Exemplo: Carregamento de configurações para vários ambientes
const path = require('path');
const dotenv = require('dotenv');
const env = process.env.NODE_ENV || 'development';
const envFile = path.resolve(process.cwd(), `.env.${env}`);
dotenv.config({ path: envFile });
if (env === 'development') {
dotenv.config({ path: path.resolve(process.cwd(), '.env') });
}
const required = ['MONGO_URI', 'JWT_SECRET'];
const missing = required.filter(key => !process.env[key]);
if (missing.length) {
throw new Error(`Missing required env vars: ${missing.join(', ')}`);
}
module.exports = {
env,
port: parseInt(process.env.PORT, 10) || 3000,
mongoUri: process.env.MONGO_URI,
jwtSecret: process.env.JWT_SECRET,
redisUrl: process.env.REDIS_URL,
logLevel: process.env.LOG_LEVEL || 'info',
};
4. Modo de cluster
(1) Por que precisamos de clusters?
O Node.js é monothread, o que significa que apenas uma instância pode ser executada em um único núcleo da CPU. O modo cluster utiliza o módulo cluster para criar vários processos de trabalho, aproveitando ao máximo as CPUs multicore para melhorar significativamente a taxa de transferência e a disponibilidade.
(2) Como funciona o módulo de cluster
O processo mestre é responsável por escutar na porta e distribuir as solicitações, enquanto os processos de trabalho lidam com a lógica de negócios propriamente dita. Vários processos de trabalho compartilham a mesma porta para garantir o balanceamento de carga.
▶ Exemplo:(3) Configuração manual de um cluster
const cluster = require('cluster');
const os = require('os');
const http = require('http');
if (cluster.isPrimary) {
const numCPUs = os.cpus().length;
console.log(`Master ${process.pid} is running`);
console.log(`Forking ${numCPUs} workers...`);
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died. Restarting...`);
cluster.fork();
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Handled by worker ${process.pid}\n`);
}).listen(3000);
console.log(`Worker ${process.pid} started`);
}
(4) Modo de cluster do PM2
O PM2 possui suporte integrado a clusters que pode ser ativado sem a necessidade de modificar o código:
pm2 start app.js -i max
pm2 start app.js -i 4
▶ Exemplo: Comparação de desempenho dos modos de cluster
node single.js &
ab -n 10000 -c 100 http://localhost:3000/
Single process:
Requests per second: 3254.21 [#/sec]
Cluster (4 workers):
Requests per second: 11280.67 [#/sec]
5. Análise de desempenho
(1) Sincronização básica com console.time
A maneira mais simples de medir o desempenho, ideal para identificar rapidamente operações lentas:
app.get('/api/users', async (req, res) => {
console.time('フェッチ-users');
const users = await User.find().lean();
console.timeEnd('フェッチ-users');
res.json(users);
});
(2) Indicadores de desempenho: medição precisa
O módulo integrado perf_hooks no Node.js oferece medição de tempo de alta precisão:
const { performance, PerformanceObserver } = require('perf_hooks');
const obs = new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach((entry) => {
console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`);
});
});
obs.observe({ type: 'measure', buffered: true });
function measureAsync(label, fn) {
return async (...args) => {
performance.mark(`${label}-start`);
const result = await fn(...args);
performance.mark(`${label}-end`);
performance.measure(label, `${label}-start`, `${label}-end`);
return result;
};
}
const fastQuery = measureAsync('db-query', async () => {
return await User.find().lean();
});
(3) Diagnóstico profissional do Clinic.js
O Clinic.js é o conjunto oficial de ferramentas de diagnóstico de desempenho recomendado pelo Node.js:
npm install -g clinic
clinic doctor -- node app.js
clinic flame -- node app.js
clinic bubbleprof -- node app.js
(4) Comparação de estratégias de otimização de desempenho
| Estratégia | Ferramentas/Métodos | Cenários aplicáveis | Dificuldade | Eficácia |
|---|---|---|---|---|
| Análise de tempo | console.time | Localização rápida | Baixo | Médio |
| Medição precisa | perf_hooks | Caminho crítico | Médio | Alto |
| Análise da CPU | Clinic Flame | Uso intensivo da CPU | Médio | Alto |
| Event Loop | Clinic Doctor | I/O Blocking | Medium | High |
| Vazamento de memória | heapdump / memwatch | Problemas de memória | Alto | Alto |
| Teste de carga | autocannon / ab | Planejamento de capacidade | Baixa | Média |
| Monitoramento APM | New Relic / Datadog | Monitoramento contínuo | Médio | Alto |
▶ Exemplo: Uso do autocannon para testes de carga
npm install -g autocannon
autocannon -c 100 -d 10 http://localhost:3000/api/health
┌─────────┬──────┬──────┬───────┬──────┬───────┬───────┬───────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼──────┼──────┼───────┼──────┼───────┼───────┼───────┤
│ Latency │ 2 ms │ 4 ms │ 12 ms │ 18ms │ 5 ms │ 3 ms │ 45 ms │
└─────────┴──────┴──────┴───────┴──────┴───────┴───────┴───────┘
Requests/sec: 18523.6
6. Pontos-finais de verificação de integridade
(1) Por que os exames de saúde são necessários?
Os sistemas de orquestração de contêineres (Docker, K8s) e os balanceadores de carga precisam saber se um serviço está funcionando normalmente. Os endpoints de verificação de integridade oferecem uma interface padronizada para detectar o status do serviço, permitindo a detecção automática de falhas e o redirecionamento do tráfego.
▶ Exemplo:(2) Exame básico de saúde
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
▶ Exemplo:(3) Avaliação detalhada do estado de saúde
app.get('/health', async (req, res) => {
const checks = {
server: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
};
try {
await mongoose.connection.db.admin().ping();
checks.dataベース = 'ok';
} catch (err) {
checks.dataベース = 'error';
}
try {
await redisClient.ping();
checks.redis = 'ok';
} catch (err) {
checks.redis = 'error';
}
const isHealthy = checks.dataベース === 'ok' && checks.redis === 'ok';
res.status(isHealthy ? 200 : 503).json(checks);
});
(4) Separação entre prontidão e atividade
Em ambientes de produção, as verificações de integridade são normalmente divididas em dois tipos: verificação de atividade (se o processo está ativo) e verificação de prontidão (se ele está pronto para receber tráfego):
app.get('/healthz', (req, res) => {
res.json({ status: 'alive' });
});
app.get('/readyz', async (req, res) => {
try {
await mongoose.connection.db.admin().ping();
await redisClient.ping();
res.json({ status: 'ready' });
} catch {
res.status(503).json({ status: 'not ready' });
}
});
▶ Exemplo: Script de verificação de integridade do Docker
const http = require('http');
const options = {
hostname: 'localhost',
port: process.env.PORT || 3000,
path: '/healthz',
timeout: 2000,
};
const req = http.request(options, (res) => {
if (res.statusCode === 200) {
process.exit(0);
} else {
process.exit(1);
}
});
req.on('error', () => process.exit(1));
req.on('timeout', () => { req.destroy(); process.exit(1); });
req.end();
7. Proxy reverso do Nginx
(1) Por que precisamos do Nginx?
O Nginx atua como um proxy reverso, oferecendo recursos como terminação de SSL, balanceamento de carga, fornecimento de recursos estáticos e limitação de solicitações. O Node.js concentra-se na lógica de negócios, enquanto o Nginx lida com a otimização da camada de rede; cada um desempenha sua função específica.
▶ Exemplo:(2) Arquitetura de implantação do Node.js em produção
graph LR
Client[Client] --> Nginx[Nginx Reverse Proxy<br/>:80/:443]
Nginx --> PM2[PM2 Cluster Management]
PM2 --> W1[Worker 1<br/>:3000]
PM2 --> W2[Worker 2<br/>:3000]
PM2 --> W3[Worker 3<br/>:3000]
PM2 --> W4[Worker 4<br/>:3000]
W1 --> DB[(MongoDB)]
W2 --> DB
W3 --> Redis[(Redis)]
W4 --> Redis
(3) Configuração do Nginx
upstream nodejs_backend {
least_conn;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
keepalive 64;
}
server {
listen 80;
server_name api.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/ssl/certs/api.example.com.crt;
ssl_certificate_key /etc/ssl/certs/api.example.com.key;
location / {
proxy_pass http://nodejs_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
location /health {
proxy_pass http://nodejs_backend/health;
access_log off;
}
location /static/ {
alias /app/public/;
expires 30d;
add_header Cache-Control "public, immutable";
}
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
limit_req zone=api burst=50 nodelay;
}
▶ Exemplo: Configuração do proxy reverso do Nginx no Docker
# docker-compose.yml Excerpt
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/ssl/certs:ro
depends_on:
api:
condition: service_healthy
restart: always
8. Exemplo abrangente: configuração completa de implantação
Charlie consolidou todas as configurações de implantação em um único sistema, automatizando todo o processo, desde o código até a produção.
▶ Exemplo: Estrutura de diretórios do projeto
charlie-api/
├── app.js
├── healthcheck.js
├── config/
│ └── index.js
├── ecosystem.config.js
├── Dockerfile
├── docker-compose.yml
├── nginx.conf
├── .env.example
├── .dockerignore
└── package.json
▶ Exemplo: PM2 ecosystem.config.js
module.exports = {
apps: [
{
name: 'charlie-api',
script: 'app.js',
instances: 'max',
exec_mode: 'cluster',
autorestart: true,
watch: false,
max_memory_restart: '512M',
env_development: {
NODE_ENV: 'development',
PORT: 3000,
},
env_production: {
NODE_ENV: 'production',
PORT: 3000,
},
error_file: './logs/error.log',
out_file: './logs/out.log',
merge_logs: true,
log_date_format: 'YYYY-MM-DD HH:mm:ss',
max_restarts: 10,
restart_delay: 4000,
kill_timeout: 5000,
listen_timeout: 10000,
},
],
deploy: {
production: {
user: 'deploy',
host: 'api.example.com',
ref: 'origin/main',
repo: 'git@github.com:charlie/api.git',
path: '/var/www/charlie-api',
'pre-deploy-local': '',
'post-deploy':
'npm ci && pm2 reload ecosystem.config.js --env production',
'pre-setup': '',
},
},
};
▶ Exemplo: Dockerfile (otimizado para compilações em várias etapas)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build 2>/dev/null || true
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --from=builder /app ./
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
USER nodejs
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD node healthcheck.js
CMD ["node", "app.js"]
▶ Exemplo: .env.example
NODE_ENV=production
PORT=3000
MONGO_URI=mongodb://mongo:27017/charlie-api
REDIS_URL=redis://redis:6379
JWT_SECRET=your-secret-key-here
LOG_LEVEL=info
CORS_ORIGIN=https://example.com
RATE_LIMIT_WINDOW=60000
RATE_LIMIT_MAX=100
▶ Exemplo: healthcheck.js
const http = require('http');
const req = http.request(
{
hostname: '127.0.0.1',
port: parseInt(process.env.PORT, 10) || 3000,
path: '/healthz',
timeout: 2000,
},
(res) => {
process.exit(res.statusCode === 200 ? 0 : 1);
}
);
req.on('error', () => process.exit(1));
req.on('timeout', () => { req.destroy(); process.exit(1); });
req.end();
▶ Exemplo: Endpoint de verificação de integridade no arquivo app.js
const express = require('express');
const mongoose = require('mongoose');
const config = require('./config');
const app = express();
app.use(express.json());
app.get('/healthz', (req, res) => {
res.json({ status: 'alive', pid: process.pid });
});
app.get('/readyz', async (req, res) => {
try {
await mongoose.connection.db.admin().ping();
res.json({ status: 'ready', pid: process.pid, uptime: process.uptime() });
} catch {
res.status(503).json({ status: 'not ready', pid: process.pid });
}
});
app.get('/health', async (req, res) => {
const checks = {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
pid: process.pid,
};
try {
await mongoose.connection.db.admin().ping();
checks.database = 'ok';
} catch {
checks.database = 'error';
checks.status = 'degraded';
}
const isHealthy = checks.database === 'ok';
res.status(isHealthy ? 200 : 503).json(checks);
});
app.get('/api/users', async (req, res) => {
console.time('fetch-users');
const users = await mongoose.model('User').find().lean();
console.timeEnd('fetch-users');
res.json({ success: true, data: users });
});
mongoose.connect(config.mongoUri).then(() => {
app.listen(config.port, () => {
console.log(`Server running on port ${config.port} [${config.env}]`);
});
});
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully...');
mongoose.connection.close();
process.exit(0);
});
process.on('SIGINT', () => {
console.log('SIGINT received, shutting down gracefully...');
mongoose.connection.close();
process.exit(0);
});
❓ Perguntas Frequentes
P: O Docker e o PM2 podem ser usados juntos? R: Sim, mas normalmente opta-se por um ou por outro. O Node.js é executado diretamente dentro de um contêiner do Docker, com as reinicializações gerenciadas pelo K8s ou pelo Docker Compose; o PM2 é adequado para implantações não contêinerizadas.
P: Como escolho uma plataforma de serviços em nuvem? R: Para projetos pequenos, use o Vercel ou o Railway (sem necessidade de configuração); para projetos de médio porte, use a AWS ou o GCP (flexíveis, mas complexos); para projetos na China, use o Alibaba Cloud ou o Tencent Cloud. Escolha com base no seu orçamento e na experiência da sua equipe.
P: Quais são algumas das ferramentas de CI/CD mais utilizadas? R: GitHub Actions (integra-se bem com o GitHub), GitLab CI (prático para hospedagem própria), Jenkins (de nível empresarial, com muitos plug-ins) e CircleCI (nativo da nuvem).
P: Como faço para monitorar uma aplicação Node.js em um ambiente de produção? R: Use o PM2, o New Relic, o Datadog ou uma configuração autohospedada do Prometheus + Grafana para monitorar a CPU, a memória, o tempo de resposta e as taxas de erro.
P: Como faço para realizar uma verificação de integridade após a implantação? R: Adicione o endpoint /health, que retorna { status: 'ok' }; o Docker HEALTHCHECK ou o livenessProbe do K8s acessam esse endpoint periodicamente.
P: O que devo usar, o PM2 ou o Docker? R: Os dois não são mutuamente exclusivos. O Docker resolve questões relacionadas à consistência do ambiente, enquanto o PM2 lida com o gerenciamento de processos e o clustering. Para ambientes de produção, recomendamos usar o Docker e o PM2 em conjunto, com o PM2 rodando dentro de um contêiner do Docker para gerenciar vários processos.
P: Como posso realizar uma implantação sem tempo de inatividade? R: Substitua pm2 restart por pm2 reload. O PM2 reiniciará os Workers um por um, garantindo que algumas instâncias permaneçam online para atender às solicitações o tempo todo. Em um ambiente Docker, você pode usar estratégias de implantação azul-verde ou de atualização contínua.
P: Quais são as limitações do modo cluster? R: No modo cluster, os processos não compartilham memória; as sessões devem ser compartilhadas por meio de armazenamento externo, como o Redis; os WebSockets devem ser usados em conjunto com sessões persistentes ou sincronização de mensagens Pub/Sub; e o cache do sistema de arquivos é independente para cada processo.
P: Como faço para monitorar o ambiente de produção? R: O PM2 oferece pm2 monit monitoramento em tempo real e pm2 plus um painel online; entre as soluções comerciais estão o New Relic, o Datadog e o Prometheus + Grafana; as principais métricas incluem CPU, memória, latência do loop de eventos e tempo de resposta das solicitações.
P: Como posso reduzir o tamanho de uma imagem do Docker? R: Use a imagem base node:alpine, utilize compilações em múltiplas etapas para separar as dependências de compilação, .dockerignore exclua arquivos desnecessários, npm ci --only=production instale apenas as dependências de produção e limpe o cache do npm npm cache clean --force.
P: Quais são as vantagens de usar o Nginx como proxy reverso? R: O descarregamento de SSL reduz a sobrecarga de criptografia no Node.js; os recursos estáticos são tratados diretamente pelo Nginx; o balanceamento de carga distribui as solicitações; a limitação de taxa impede ataques DDoS; a compactação gzip reduz o volume de transferência de dados; e o armazenamento em cache acelera as respostas.
📖 Resumo
(1) Pontos-chave desta aula
- O PM2 oferece reinício automático de processos, modo de cluster e gerenciamento de logs
- A conteinerização com o Docker garante a consistência do ambiente e a implantação padronizada
- Configuração separada por meio de variáveis de ambiente, com suporte para alternar entre vários ambientes
- O modo cluster aproveita ao máximo as CPUs com vários núcleos para aumentar a taxa de transferência
- Performance Hooks e Clinic.js: Identificando gargalos de desempenho
- Os endpoints de verificação de integridade são a base da orquestração de contêineres e do balanceamento de carga
- Proxy reverso Nginx para SSL, balanceamento de carga e recursos estáticos
(2) Tarefa de casa
- Inicie seu projeto Express usando o PM2, configure
ecosystem.config.js, ative o modo de cluster e usepm2 monitpara monitorar o status de execução. - Escreva um Dockerfile para o projeto, utilize compilações em várias etapas para otimizar o tamanho da imagem e use
docker-composepara iniciar o Node.js e o MongoDB simultaneamente - Implemente os três endpoints de verificação de integridade
/health,/healthze/readyze configureHEALTHCHECKno Docker. - Use
autocannonpara realizar testes de carga na sua API, comparando as diferenças de taxa de transferência entre os modos de processo único e de cluster. - Escreva a configuração do proxy reverso do Nginx para implementar a terminação SSL e o balanceamento de carga, e use
aboucurlpara verificar se o encaminhamento do proxy está funcionando corretamente.
📝 Exercícios
- Conclua todos os exemplos de código desta lição e certifique-se de que cada um deles seja executado corretamente.
- Modifique o exemplo completo e adicione suas próprias extensões
- Analise a documentação oficial, identifique 1 ou 2 APIs que não foram abordadas nesta aula e escreva um código de teste para elas.
- Reflexão: Como você aplicaria o que aprendeu nesta aula a um projeto do mundo real?
- Tente combinar o que você aprendeu nesta aula com o conteúdo das aulas anteriores para criar um pequeno projeto.