Kubernetes-Deployment
Kubernetes ist das häufigste Deployment-Ziel. Das Framework verträgt sich gut mit K8s, sobald ein paar Dinge sitzen: stabile Identität (für stateful Actors), Seed-Discovery (damit Nodes sich finden) und ein sauberer Shutdown-Pfad (damit Rolling Updates keinen Traffic verlieren).
Diese Seite ist ein funktionierendes Rezept — kopieren, anpassen, deployen.
Das vollständige Manifest
Abschnitt betitelt „Das vollständige Manifest“apiVersion: v1kind: ServiceAccountmetadata: name: actor-ts---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: actor-ts-pod-readerrules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: actor-tssubjects: - kind: ServiceAccount name: actor-tsroleRef: kind: Role name: actor-ts-pod-reader apiGroup: rbac.authorization.k8s.io---apiVersion: v1kind: Servicemetadata: name: actor-ts-clusterspec: clusterIP: None # headless — DNS liefert Pod-IPs selector: app: actor-ts ports: - name: cluster port: 2552 targetPort: 2552---apiVersion: v1kind: Servicemetadata: name: actor-tsspec: selector: app: actor-ts ports: - name: http port: 80 targetPort: 8080 - name: management port: 8558 targetPort: 8558---apiVersion: apps/v1kind: StatefulSetmetadata: name: actor-tsspec: serviceName: actor-ts-cluster replicas: 3 selector: matchLabels: app: actor-ts template: metadata: labels: app: actor-ts spec: serviceAccountName: actor-ts terminationGracePeriodSeconds: 30 containers: - name: app image: ghcr.io/your-org/your-app:1.2.3 ports: - name: cluster containerPort: 2552 - name: http containerPort: 8080 - name: management containerPort: 8558 env: - name: ACTOR_TS_HOSTNAME valueFrom: fieldRef: fieldPath: status.podIP - name: ACTOR_TS_PORT value: "2552" - name: K8S_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: K8S_LABEL_SELECTOR value: "app=actor-ts" - name: DB_PASSWORD valueFrom: secretKeyRef: name: actor-ts-secrets key: db-password readinessProbe: httpGet: path: /health/ready port: management initialDelaySeconds: 5 periodSeconds: 5 livenessProbe: httpGet: path: /health/alive port: management initialDelaySeconds: 15 periodSeconds: 10 lifecycle: preStop: exec: # LB drainen, bevor SIGTERM die App erreicht command: ["/bin/sh", "-c", "sleep 10"]Was die einzelnen Teile tun
Abschnitt betitelt „Was die einzelnen Teile tun“ServiceAccount + RBAC
Abschnitt betitelt „ServiceAccount + RBAC“ServiceAccount: actor-tsRole: actor-ts-pod-reader # get + list podsRoleBinding: bindet beidesDer K8s-API-Seed-Provider muss Pods listen können, die einem Label-Selector entsprechen, um Peers zu finden. Ohne dieses RBAC-Grant bekommt der Seed-Provider 403 und der Cluster bildet sich nie.
Headless Service für Cluster-Gossip
Abschnitt betitelt „Headless Service für Cluster-Gossip“clusterIP: NoneEin headless Service liefert die Pod-IPs direkt per DNS (keine ClusterIP-Virtual-Address). Nützlich, wenn Nodes stabile, direkte Peer-Identitäten brauchen — die Heartbeats des Failure Detectors zielen auf spezifische Pod-IPs, nicht auf eine load-balancete Abstraktion.
Regulärer Service für Traffic
Abschnitt betitelt „Regulärer Service für Traffic“clusterIP: <default>Ein normaler Service für HTTP und den Management-Endpunkt — die profitieren von Load Balancing. Cluster-Traffic geht über den Headless Service, App-Traffic über diesen.
StatefulSet vs Deployment
Abschnitt betitelt „StatefulSet vs Deployment“| Nimm | Wann |
|---|---|
| StatefulSet | Stabile Pod-Namen (actor-ts-0, actor-ts-1, …). Nützlich, wenn du vorhersagbare Identität für Entity-Placement willst oder wenn pro-Pod Persistent Volumes gemountet werden. |
| Deployment | Pod-Namen sind zufällig. Okay, wenn deine App stateless ist (keine pro-Pod-Identität erforderlich) und Persistence extern liegt (Cassandra-Journal, geteilter S3-Snapshot-Store). |
Für sharded Actors mit remember-entities = true auf pro-Pod
Persistent Volumes ist StatefulSet die richtige Wahl. Für
extern-persistierten State (Cluster gegen ein geteiltes
Postgres/Cassandra) ist Deployment in Ordnung und einfacher.
terminationGracePeriodSeconds
Abschnitt betitelt „terminationGracePeriodSeconds“terminationGracePeriodSeconds: 30K8s sendet SIGTERM, wartet dann diese Zeitspanne vor SIGKILL. Dimensioniert nach:
- HTTP-Drain — typischerweise 5-10 s.
- Cluster-Leave-Gossip — 5-15 s für Konvergenz.
- Journal-Flush — abhängig vom Journal.
30 s sind ein vernünftiger Default. Erhöhe, wenn dein Cluster groß oder das Failure-Detector-Fenster lang ist.
preStop-Sleep
Abschnitt betitelt „preStop-Sleep“preStop: exec: command: ["/bin/sh", "-c", "sleep 10"]Kritisch für saubere Rolling Updates. Der Ablauf:
- K8s markiert den Pod als terminating und startet den
preStop-Hook parallel zur Load-Balancer-Deregistrierung. sleep 10— gibt dem Load Balancer Zeit, keinen neuen Traffic mehr an diesen Pod zu schicken.- Nach dem Sleep sendet K8s SIGTERM.
- Die Coordinated-Shutdown-Hooks der App drainen in-flight Requests, verlassen den Cluster usw.
Ohne den Sleep läuft SIGTERM gegen die LB-Deregistrierung — in-flight Requests können “draining”-Antworten sehen.
Readiness- + Liveness-Probes
Abschnitt betitelt „Readiness- + Liveness-Probes“readinessProbe: /health/readylivenessProbe: /health/aliveDie HttpManagement-Extension des Frameworks stellt diese Endpunkte bereit.
/health/ready— “soll dieser Pod Traffic bekommen?” Liefert während des Shutdowns 503, damit der LB ihn vor SIGTERM rauswirft. Nutze, um App-spezifische Health-Checks abzusichern (DB erreichbar, Dependencies warm etc.)./health/alive— “ist dieser Pod grundlegend kaputt?” Schlägt fehl → K8s startet den Pod neu. Reserviere für wirklich unrettbare Zustände — Actor-System läuft nicht, OOM-artige Bedingungen.
App-seitige Verdrahtung
Abschnitt betitelt „App-seitige Verdrahtung“import { ActorSystem, Cluster, CoordinatedShutdownId, HttpManagement } from 'actor-ts';import { KubernetesApiSeedProvider } from 'actor-ts/discovery';
const system = ActorSystem.create('my-app');const cs = system.extension(CoordinatedShutdownId);
// 1. Cluster-Join mit K8s-API-Seed-Discoveryconst seeds = await new KubernetesApiSeedProvider({ namespace: process.env.K8S_NAMESPACE!, labelSelector: process.env.K8S_LABEL_SELECTOR!, containerPort: 2552,}).discover();
const cluster = await Cluster.join(system, { host: process.env.ACTOR_TS_HOSTNAME!, port: parseInt(process.env.ACTOR_TS_PORT!), seeds, roles: ['compute'],});
// 2. Management-Endpunkteconst management = await HttpManagement.start(system, { port: 8558, cluster,});
// 3. App-HTTP-Serverconst http = system.extension(HttpExtensionId);await http.newServerAt('0.0.0.0', 8080).bind(routes);
// 4. SIGTERM → Coordinated Shutdowncs.installProcessHooks();
// 5. Prozess am Leben haltenawait new Promise(() => {});Fünf Schritte in der Reihenfolge:
- Cluster-Join mit Seed-Discovery — der K8s-API-Seed-Provider
fragt Pods ab, die
app=actor-tsmatchen, und nutzt deren IPs als Seeds. Auf dem ersten Pod ist die Seed-Liste nur er selbst (der Auto-Promote-to-Leader-Pfad). - Management-Endpunkte —
/health/*für die K8s-Probes,/cluster/memberszum Debuggen. - App-HTTP — deine Routes, getrennter Port vom Management.
- SIGTERM-Hooks — Coordinated Shutdown installiert Hooks, die bei SIGTERM zum graceful Drain feuern.
- Am Leben halten — der Prozess hängt, bis SIGTERM kommt.
Siehe Discovery — Kubernetes API für die vollständigen Optionen des Seed-Providers.
Rolling Updates
Abschnitt betitelt „Rolling Updates“kubectl rollout restart statefulset/actor-tsFür jeden Pod, der Reihe nach (StatefulSet) oder beliebig (Deployment):
- K8s markiert den Pod als terminating + startet
preStop. - 10-Sekunden-LB-Drain.
- SIGTERM kommt an.
- Coordinated Shutdown läuft:
- Keine neuen HTTP-Requests mehr annehmen.
- In-flight Requests drainen.
cluster.leave()ausgeben.- Auf Bestätigung des Leaves durch den Cluster warten.
- Das Actor-System terminieren.
- Prozess beendet sich sauber.
- K8s startet einen neuen Pod aus dem neuen Image.
- Der neue Pod tritt dem Cluster via Seed-Provider bei.
Für sharded Entities passiert das Rebalancing automatisch — die Shards des gehenden Nodes werden neu zugeteilt; neue Entities respawnen auf dem neuen Pod aus dem Journal.
Wohin als nächstes
Abschnitt betitelt „Wohin als nächstes“- Operations-Überblick — das Gesamtbild aller Produktionsbelange.
- Discovery — Kubernetes API — die Optionen des Seed-Providers.
- Coordinated Shutdown — die DSL für phased Shutdown.
- HttpManagement — die /health/*-Endpunkte, gegen die K8s-Probes laufen.
- Cluster-Sicherheit — TLS + Auth für den Cluster-Transport.
- Rolling Migration — schema-brechende Änderungen während eines Rollouts.