Zum Inhalt springen
Deutsch

HTTP-Endpunkte

Der Management-Server stellt einen kleinen Satz operativer Endpunkte bereit. Die meisten sind read-only; die Admin-Endpunkte (/cluster/down, /cluster/leave) sind Opt-in und standardmäßig aus.

Sicherheit (security audit #8): die read-only-Endpunkte legen dennoch interne Topologie offen — Member-Adressen (host:port), Rollen, den Leader und die Shard-Map. Behandle den Management-Server als sensibel: binde ihn an ein internes Interface und schütze ihn mit den Optionen ipAllowlist (und/oder auth) von managementRoutes(...), statt ihn öffentlich zu exponieren.

Liveness — ist dieser Prozess fundamental gesund?

GET /health
→ 200 OK
{
"status": "UP",
"checks": [
{ "name": "event-loop", "status": true }
]
}

503 mit { "status": "DOWN", ... }, wenn ein Liveness-Check fehlschlägt. Siehe Health Checks.

Readiness — sollte dieser Pod Traffic bekommen?

GET /ready
→ 200 OK
{
"status": "UP",
"clusterReady": true,
"checks": [
{ "name": "database", "status": true }
]
}

clusterReady ist false, bis der lokale Node Up erreicht; die Readiness-Checks, die du registrierst, füllen das checks-Array. 503, wenn der Cluster nicht bereit ist oder ein Check fehlschlägt.

Verfügbar, wenn ein cluster an managementRoutes übergeben wurde.

GET /cluster/members
→ 200 OK
{
"members": [
{
"address": "actor-ts://my-app@10.0.0.5:2552",
"status": "up",
"version": 3,
"roles": ["compute"]
},
...
],
"self": "actor-ts://my-app@10.0.0.5:2552"
}

Voller Membership-Snapshot. Nützlich für:

  • Manuelle Cluster-Zustand-Checks während Incidents.
  • Externe Dashboards, die den Cluster visualisieren.
  • Tests, die das Cluster-Joining verifizieren.
GET /cluster/leader
→ 200 OK
{ "leader": "actor-ts://my-app@10.0.0.5:2552", "isSelf": true }

Die Adresse des Leaders plus isSelf (ist der lokale Node der Leader). leader ist null, bevor ein Leader gewählt wurde. Nützlich zum Monitoring von Leader-Wechseln.

GET /cluster/shards?type=cart
→ 200 OK
{
"typeName": "cart",
"leader": "my-app@10.0.0.5:2552",
"takenAt": 1710000000000,
"regions": [
{
"key": "my-app@10.0.0.5:2552",
"address": "my-app@10.0.0.5:2552",
"path": "/system/cluster/sharding/region-cart",
"proxy": false,
"shards": [0, 1, 2, ..., 33]
},
...
],
"shardHome": [ { "shard": 0, "regionKey": "my-app@10.0.0.5:2552" }, ... ]
}

Zeigt die Shard-zu-Region-Allokation für einen sharded Typ. Nutze für:

  • Verifizieren gleichmäßiger Verteilung nach einem Rebalance.
  • Diagnostizieren heißer Regionen (eine Region mit zu vielen Shards).
  • Manuelle Rebalance-Trigger in der Entwicklung.

Opt-in via enableMetricsEndpoint: true.

GET /metrics
→ 200 OK
Content-Type: text/plain; version=0.0.4; charset=utf-8
# HELP actor_messages_processed_total ...
# TYPE actor_messages_processed_total counter
actor_messages_processed_total{class="Worker",path="..."} 12345
...

Prometheus-Textformat. Siehe Prometheus-Exporter.

Opt-in via enableLeaveEndpoint: true.

POST /cluster/leave
→ 202 Accepted
leaving

Der 202-Body ist das Klartext-Wort leaving (kein JSON) — der Leave ist Fire-and-forget.

Löst einen Graceful Cluster-Leave aus. Der Node läuft durch leavingexitingremoved; Shards rebalancen weg; das Actor-System terminiert (konfigurierbar).

Nutze für:

  • Pod-Außerbetriebnahme vor einem Rolling Update.
  • Manuellen Node-Out während Incidents.

Hinter Authentifizierung gaten — jeder mit Portzugriff kann deinen Node drainen.

Opt-in via enableDownEndpoint: true.

POST /cluster/down
{ "address": "actor-ts://my-app@10.0.0.5:2552" }
→ 202 Accepted
{ "downed": "actor-ts://my-app@10.0.0.5:2552" }

Gibt 202 mit { downed } zurück, wenn das Mitglied gedownt wurde, oder 404, wenn die Adresse unbekannt oder bereits terminal ist.

Force-downt ein Remote-Mitglied per Adresse. Destruktiv — die Actor des Targets stoppen, seine Shards reallokieren woanders.

Nutze für:

  • Split-Brain-Recovery, wenn keine Downing-Strategie konfiguriert ist.
  • Entfernen von hängenden unreachable-Mitgliedern, die sich weigern, sich zu erholen.

Hochrisiko-Endpunkt — hinter Auth + Audit-Logs gaten.

import { managementRoutes, path, get, concat, completeJson } from 'actor-ts';
const baseRoutes = managementRoutes(system, cluster).routes;
const customRoutes = concat(
baseRoutes,
path('admin',
get(async () => completeJson(200, { appVersion: '1.2.3' })),
),
);
await system.http(port, { host }).bind(customRoutes);

managementRoutes(system, cluster, options) gibt die Basis-Routen zurück; kombiniere mit eigenen via concat. Nützlich, um app-spezifische Admin-Endpunkte neben dem Standard-Satz hinzuzufügen.

Die Read-Endpunkte (/health, /ready, /cluster/members, /cluster/leader, /cluster/shards) und /cluster/down geben JSON zurück. /metrics liefert Prometheus-text/plain, und /cluster/leave gibt den Klartext-Body leaving zurück.

Fehler sind Klartext-Bodies (das complete(status, message) des Frameworks), kein strukturiertes Objekt — z. B. ergibt ein fehlender type-Query-Param ein 400, dessen Body missing query param \type“ ist.

Status-Codes folgen HTTP-Konventionen: 200 für Reads, 202 Accepted für die Admin-Aktionen (/cluster/leave, /cluster/down), 4xx für Client-Fehler (404, wenn eine /cluster/down-Adresse unbekannt ist) und 503, wenn Cluster oder ein Health-Check nicht verfügbar ist.

Die privilegierten Endpunkte (/cluster/down, /cluster/leave, /cluster/members, /cluster/shards, /metrics) akzeptieren standardmäßig anonyme Requests. Das ist nur sicher, wenn der Management-Port auf einem netzwerkisolierten Bind sitzt (separater Service, anderer Port, 127.0.0.1-only) oder hinter einem Reverse-Proxy, der die Auth für dich übernimmt.

Für Produktions-Deployments, die Management-Endpunkte einem breiteren Netz exponieren, hänge die mitgelieferten Middlewares über managementRoutes(system, cluster, { auth, ipAllowlist }) an:

import {
BearerTokenAuth,
IpAllowlist,
managementRoutes,
} from 'actor-ts';
const { routes } = managementRoutes(system, cluster, {
enableLeaveEndpoint: true,
enableDownEndpoint: true,
enableMetricsEndpoint: true,
// Shared-Secret-Bearer-Token (Rotation über mehrere Einträge).
auth: BearerTokenAuth({
tokens: [process.env.MGMT_TOKEN!],
realm: 'my-app-mgmt',
}),
// Netzwerk-Level-Fence — nur Requests aus diesen CIDRs
// erreichen die Anwendung überhaupt.
ipAllowlist: IpAllowlist({
allow: ['10.0.0.0/8', '127.0.0.1/32'],
// Operatoren hinter einem trusted Reverse-Proxy: gib einen
// Custom-Extractor mit, der x-forwarded-for liest (der
// Default liest die Socket-Peer und ignoriert
// Forwarded-Header).
// getClientIp: (req) => req.headers['x-forwarded-for']?.split(',')[0]?.trim(),
}),
});

Policy-Trennung:

  • auth wrappt die privilegierte Teilmenge (/cluster/*, /metrics). /health und /ready bleiben anonym — Kubernetes- Liveness-/Readiness-Probes können nicht so leicht einen Authorization-Header mitschicken. Setze authProtectHealth: true, wenn dein Deployment garantiert, dass die Probes Credentials präsentieren können.
  • ipAllowlist wrappt JEDEN Endpunkt inklusive /health und /ready. Netzwerk-Level-Isolation ist unabhängig davon, wer authentifiziert ist.

Fehlermodi:

  • Fehlender / falscher Authorization-Header → 401 Unauthorized mit WWW-Authenticate: Bearer realm="...".
  • Client-IP außerhalb der Allowlist (oder unauflösbar) → 403 Forbidden.

Die Middlewares sind general-purpose — du kannst sie auch außerhalb des Management-Subtrees über den withMiddleware(mw, route)-Builder einsetzen.