Veröffentlicht am

Node.js auf Kubernetes: Express-App deployen

Teilen:
Authors

Node.js Express-App auf Kubernetes deployen

TL;DR

Node.js auf Kubernetes erfordert ein schlankes Multi-Stage Dockerfile, einen Non-Root-User, korrektes SIGTERM-Handling für Graceful Shutdown und passende Resource Limits. HPA skaliert die Pods automatisch basierend auf CPU-Auslastung, während Liveness- und Readiness-Probes die Verfügbarkeit absichern.


Express ist das meistgenutzte Web-Framework für Node.js. Auf Kubernetes läuft es stabil und skalierbar -- vorausgesetzt, Dockerfile und Manifeste sind richtig konfiguriert. Dieser Guide deckt alle Schritte von der Containerisierung bis zum Autoscaling ab.

Multi-Stage Dockerfile für Node.js

Ein optimiertes Dockerfile nutzt zwei Stages und reduziert die Image-Größe erheblich:

# Build-Stage
FROM node:20-alpine AS builder

WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production=false

COPY . .
RUN yarn build

# Runtime-Stage
FROM node:20-alpine

RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

USER appuser

EXPOSE 3000

CMD ["node", "dist/server.js"]

Alpine-basierte Images sind rund 5x kleiner als Debian-Varianten. Der Non-Root-User appuser verhindert, dass ein kompromittierter Container Root-Rechte im Cluster erhält. --frozen-lockfile stellt sicher, dass exakt die gleichen Dependency-Versionen wie in der Entwicklung installiert werden.

Graceful Shutdown mit SIGTERM

Kubernetes sendet SIGTERM an den Container vor dem Herunterfahren. Ohne explizites Handling bricht Node.js laufende Requests ab:

// server.js
import express from 'express';

const app = express();
const PORT = process.env.PORT || 3000;

app.get('/healthz', (req, res) => res.json({ status: 'ok' }));
app.get('/ready', (req, res) => res.json({ status: 'ready' }));

app.get('/', (req, res) => {
  res.json({ message: 'Hello from Kubernetes' });
});

const server = app.listen(PORT, () => {
  console.log(`Server läuft auf Port ${PORT}`);
});

// Graceful Shutdown
const shutdown = (signal) => {
  console.log(`${signal} empfangen, fahre herunter...`);
  server.close(() => {
    console.log('Alle Verbindungen geschlossen');
    process.exit(0);
  });

  // Erzwinge Shutdown nach 30 Sekunden
  setTimeout(() => {
    console.error('Erzwungener Shutdown nach Timeout');
    process.exit(1);
  }, 30000);
};

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

server.close() beendet keine aktiven Verbindungen sofort, sondern wartet bis laufende Requests abgeschlossen sind. Der Timeout-Fallback verhindert, dass der Container ewig hängt. Setzen Sie terminationGracePeriodSeconds im Pod-Spec auf denselben Wert.

Kubernetes Deployment mit Resource Limits

Node.js ist single-threaded. Ein einzelner Worker nutzt maximal einen CPU-Kern. Die Resource Limits sollten das berücksichtigen:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: express-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: express-app
  template:
    metadata:
      labels:
        app: express-app
    spec:
      terminationGracePeriodSeconds: 35
      containers:
        - name: express
          image: registry.example.com/express-app:1.0.0
          ports:
            - containerPort: 3000
          env:
            - name: NODE_ENV
              value: "production"
            - name: PORT
              value: "3000"
          resources:
            requests:
              cpu: 200m
              memory: 128Mi
            limits:
              cpu: "1"
              memory: 256Mi
          livenessProbe:
            httpGet:
              path: /healthz
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 15
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /ready
              port: 3000
            initialDelaySeconds: 3
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: express-service
spec:
  selector:
    app: express-app
  ports:
    - port: 80
      targetPort: 3000
  type: ClusterIP

Der terminationGracePeriodSeconds-Wert liegt absichtlich 5 Sekunden über dem Shutdown-Timeout im Code. So hat der Container genug Zeit für den sauberen Shutdown, bevor Kubernetes den Prozess mit SIGKILL beendet.

ParameterEmpfehlungBegründung
CPU Request200mNode.js ist single-threaded, braucht wenig Baseline-CPU
CPU Limit1000mMaximal ein Kern pro Pod sinnvoll
Memory Request128MiExpress-Apps starten mit ca. 50-80 MB
Memory Limit256MiPuffer für Request-Spitzen und Garbage Collection

Horizontal Pod Autoscaler (HPA)

Statt manuell Replicas hochzusetzen, skaliert der HPA automatisch basierend auf CPU:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: express-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: express-app
  minReplicas: 3
  maxReplicas: 15
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
        - type: Pods
          value: 3
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Pods
          value: 1
          periodSeconds: 120

Das behavior-Feld steuert die Skalierungsgeschwindigkeit. Scale-Up ist aggressiv konfiguriert (3 Pods pro Minute), Scale-Down konservativ (1 Pod alle 2 Minuten). Das verhindert Flapping bei schwankender Last.

Voraussetzung ist ein laufender Metrics Server im Cluster. Prüfen Sie das mit:

kubectl top pods -n default

ConfigMap für Anwendungskonfiguration

Umgebungsspezifische Werte gehören in eine ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: express-config
data:
  LOG_LEVEL: "info"
  CORS_ORIGIN: "https://www.kubernetes-administration.de"
  RATE_LIMIT_MAX: "100"
  RATE_LIMIT_WINDOW_MS: "60000"

Referenzieren Sie die ConfigMap im Deployment mit envFrom:

envFrom:
  - configMapRef:
      name: express-config

Für Secrets wie API-Keys oder Datenbank-Passwörter nutzen Sie ein Kubernetes Secret mit stringData statt einer ConfigMap.

Production-Checkliste

Vor dem Go-Live sollten diese Punkte erfüllt sein:

PunktStatus-Prüfung
Non-Root User im DockerfileUSER appuser gesetzt
Graceful ShutdownSIGTERM-Handler implementiert
Health ChecksLiveness + Readiness Probes aktiv
Resource LimitsCPU und Memory definiert
HPA konfiguriertMin/Max Replicas + Metriken
NODE_ENV=productionWeniger Logging, kein Stack-Trace

FAQ

Warum nicht npm start als CMD im Dockerfile?

npm start startet einen zusätzlichen npm-Prozess, der SIGTERM nicht korrekt an Node.js weiterleitet. node dist/server.js startet den Prozess direkt als PID 1 und empfängt Signale korrekt.

Wie viele Replicas brauche ich für eine Express-App?

Starten Sie mit 3 Replicas für Hochverfügbarkeit und nutzen Sie HPA für automatische Skalierung. Drei Pods ermöglichen Rolling Updates ohne Downtime und verteilen sich auf verschiedene Nodes.

Soll ich das Node.js Cluster-Modul auf Kubernetes verwenden?

Nein. Auf Kubernetes skalieren Sie horizontal über mehrere Pods statt vertikal über Worker-Threads in einem Pod. Jeder Pod läuft als einzelner Node.js-Prozess. Das vereinfacht Debugging und Ressourcen-Management.

Wie debugge ich Memory Leaks in Node.js auf Kubernetes?

Setzen Sie das Memory Limit bewusst knapp und überwachen Sie OOMKilled-Events mit kubectl get events. Nutzen Sie --max-old-space-size als Node.js-Flag, um den Heap-Speicher zu begrenzen, bevor Kubernetes den Pod killt.

Warum ist das Scale-Down langsamer als das Scale-Up?

Zu schnelles Herunterskalieren kann bei wiederkehrenden Traffic-Spitzen zu Engpässen führen. Das stabilizationWindowSeconds von 300 Sekunden beim Scale-Down wartet 5 Minuten ab, ob die Last wirklich dauerhaft gesunken ist.

Legacy zu Kubernetes migrieren?

Wir begleiten Ihre Migration von VMs zu Containern – ohne Produktionsausfall. Erfahrung aus 50+ Migrationsprojekten.

Kubernetes-Beratung gesucht?

Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.

📖 Verwandte Artikel

Weitere interessante Beiträge zu ähnlichen Themen