Bring dein Projekt in Produktion: NODE_ENV, Prozessmanager, ein korrektes Docker-Image und Skalierung über mehrere Kerne mit cluster.
Diese Lektion in Kodokon öffnenNODE_ENV ist lediglich eine Konvention der Bibliotheken: Der Node-Kern ignoriert sie fast vollständig. Aber Express und andere nutzen sie, um Views zu cachen und Stacktraces zu verbergen; manche Frameworks sind mit NODE_ENV=production um ein Vielfaches schneller. Auf Seiten der Abhängigkeiten installiert npm ci --omit=dev exakt die Lockfile, ohne Dev-Abhängigkeiten - reproduzierbar und schneller als npm install. Seit Node 20.6 lädt --env-file eine .env-Datei nativ, ganz ohne Abhängigkeit.
NODE_ENV=production node server.js
npm ci --omit=dev
node --env-file=.env server.jsEin Node-Prozess stirbt irgendwann immer: Speicherüberlauf, Bug, Ausnahme. Die Rolle des Prozessmanagers ist es, ihn neu zu starten. Zwei Schulen: Auf einer nackten Maschine überwacht, startet und lädt pm2 (oder systemd) ohne Ausfallzeit neu; in einem Container übernimmt der Orchestrator (Kubernetes, ECS) diese Rolle - ein Prozess pro Container, und pm2 wird nutzlos, sogar schädlich, weil es Abstürze vor dem Orchestrator verbirgt.
npm install -g pm2
pm2 start server.js -i max --name api
pm2 reload api
pm2 logs api --lines 100Zwei Docker-Fallstricke, die typisch für Node sind. Erstens: Das Image sollte als unprivilegierter Nutzer node laufen, den die offiziellen Images mitbringen. Zweitens: Node ist nicht dafür gebaut, PID 1 zu sein - es räumt keine Zombie-Prozesse ab; starte den Container mit docker run --init (oder init: true in Compose), um ein minimales init einzuschieben. Und schließlich schreibe CMD in Exec-Form: Die Shell-Form schiebt /bin/sh dazwischen, das SIGTERM nicht weiterreicht - das saubere Herunterfahren aus der vorherigen Lektion würde nie ausgelöst.
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"]Ein einzelner Node-Prozess nutzt nur einen Kern. Das Modul cluster erstellt einen Worker-Prozess pro Kern: Der Primary lauscht auf dem Port und verteilt die Verbindungen im Round-Robin an die Worker (das Standardverhalten, außer unter Windows). Jeder Worker hat seinen eigenen Speicher und seine eigene Event-Loop: kein geteilter Zustand - Sessions und Caches müssen in Redis oder etwas Gleichwertigem leben. Für reine Berechnungen bevorzugst du worker_threads, die den Speicher über SharedArrayBuffer teilen.
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);
}