Lleva a producción: NODE_ENV, gestor de procesos, una imagen Docker correcta y escalado multinúcleo con cluster.
Abrir esta lección en KodokonNODE_ENV no es más que una convención de bibliotecas: el núcleo de Node lo ignora casi por completo. Pero Express, entre otros, lo usa para cachear vistas y ocultar trazas de pila; algunos frameworks son varias veces más rápidos con NODE_ENV=production. Del lado de las dependencias, npm ci --omit=dev instala exactamente el lockfile, sin dependencias de desarrollo - reproducible y más rápido que npm install. Desde Node 20.6, --env-file carga un archivo .env de forma nativa, sin ninguna dependencia.
NODE_ENV=production node server.js
npm ci --omit=dev
node --env-file=.env server.jsUn proceso de Node siempre acaba muriendo: desbordamiento de memoria, bug, excepción. El papel del gestor de procesos es reiniciarlo. Dos escuelas: en una máquina desnuda, pm2 (o systemd) supervisa, reinicia y recarga sin tiempo de inactividad; en un contenedor, es el orquestador (Kubernetes, ECS) el que desempeña este papel - un proceso por contenedor, y pm2 se vuelve inútil, incluso perjudicial, porque oculta las caídas al orquestador.
npm install -g pm2
pm2 start server.js -i max --name api
pm2 reload api
pm2 logs api --lines 100Dos trampas de Docker específicas de Node. Una: la imagen debería ejecutarse como el usuario node sin privilegios, que proporcionan las imágenes oficiales. Dos: Node no está diseñado para ser PID 1 - no recolecta los procesos zombis; lanza el contenedor con docker run --init (o init: true en Compose) para insertar un init mínimo. Por último, escribe CMD en forma exec: la forma shell inserta /bin/sh, que no reenvía SIGTERM - el graceful shutdown de la lección anterior nunca se activaría.
FROM node:20-slim AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
FROM node:20-slim
ENV NODE_ENV=production
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
USER node
EXPOSE 3000
CMD ["node", "server.js"]Un solo proceso de Node usa un único núcleo. El módulo cluster crea un proceso worker por núcleo: el primario escucha en el puerto y distribuye las conexiones a los workers en round-robin (el comportamiento por defecto, salvo en Windows). Cada worker tiene su propia memoria y su propio event loop: sin estado compartido - las sesiones y las cachés deben vivir en Redis o algo equivalente. Para el cálculo puro, prefiere worker_threads, que comparte memoria mediante SharedArrayBuffer.
import cluster from "node:cluster";
import { createServer } from "node:http";
import { availableParallelism } from "node:os";
if (cluster.isPrimary) {
const count = availableParallelism();
for (let i = 0; i < count; i += 1) cluster.fork();
cluster.on("exit", (worker) => {
console.log(`worker ${worker.process.pid} down`);
cluster.fork();
});
} else {
createServer((req, res) => {
res.end(`pid ${process.pid}`);
}).listen(3000);
}