Skip to main content

Architekturskizze: Redpanda Connect

Dieses Dokument beschreibt, wie die Anforderungen mit Redpanda Connect architektonisch umgesetzt werden können.

Siehe auch: Vergleich mit Apache NiFi | Architekturskizze Apache NiFi

Architekturübersicht

Redpanda Connect Architektur

Kernprinzip: Mehrere redpanda-connect-Prozesse pro Pod.

Jede Pipeline läuft als eigenständiger Betriebssystem-Prozess innerhalb eines Worker-Pods. Der Provisioner (Eigenentwicklung) verwaltet den Pod-Pool (HPA) und weist Pipelines an Worker-Pods zu. Die Isolation erfolgt auf Prozess-Ebene, nicht auf Pod-Ebene -- damit bleibt die Pod-Anzahl beherrschbar, auch bei 3-4-stelligen Pipeline-Zahlen.

Warum nicht 1 Pipeline = 1 Pod?

Bei der erwarteten Anzahl von Pipelines (500+ aktiv, potenziell vierstellig) wäre ein Pod-pro-Pipeline-Modell nicht tragfähig:

  • K8s-Scheduling-Overhead bei hunderten/tausenden Pods
  • etcd-Belastung durch entsprechend viele Deployment-, ConfigMap- und Service-Objekte
  • Netzwerk-Overhead (jeder Pod braucht IP, iptables-Regeln, DNS-Eintrag)

Stattdessen werden mehrere redpanda-connect-Prozesse in einem Worker-Pod gestartet. Der Worker-Pod ist ein Container mit der redpanda-connect-Binary und einem Supervisor-Prozess (Teil des Provisioners).

Architekturkomponenten

Provisioner (Eigenentwicklung)

Der Provisioner ist die zentrale Steuerungskomponente:

  • Pipeline-Zuweisung: Entscheidet, welche Pipeline auf welchem Worker-Pod läuft. Zwei Modi denkbar:
    • Push: Provisioner weist Pipelines aktiv an Worker zu (z.B. via gRPC/REST an den Supervisor im Pod).
    • Pull: Worker-Pods fragen beim Provisioner nach offenen Pipelines und starten sie selbstständig.
  • Pod-Pool-Management: Steuert die Anzahl der Worker-Pods über HPA (CPU/Memory) oder eigene Skalierungslogik. Für deterministisches Scale-Down benötigt der Provisioner Zugriff auf die K8s API (Namespace-scoped Role). Siehe "Scale-Down-Strategie".
  • Lifecycle-Management: Erstellen, Stoppen, Neustarten von Pipelines. Rolling Updates bei Config-Änderungen.
  • RBAC: Berechtigungsprüfung auf API-Ebene (Welcher User darf welche Pipeline erstellen/sehen/bearbeiten?).

Worker-Pods

Jeder Worker-Pod enthält:

  • Supervisor-Prozess: Empfängt Pipeline-Zuweisungen vom Provisioner, startet/stoppt redpanda-connect-Prozesse, überwacht deren Health, meldet Status zurück.
  • N redpanda-connect-Prozesse: Jeder Prozess läuft mit eigener YAML-Config, eigenem PID, eigenem Speicherbereich. Betriebssystem-Prozess-Isolation (kein Shared Memory zwischen Pipelines).
  • Metrics-Aggregation: Jeder redpanda-connect-Prozess exponiert Metriken auf einem eigenen Port. Der Supervisor aggregiert oder der Prometheus-Scrape erfolgt pro Port.

Prozess-Isolation innerhalb eines Pods

Da mehrere Pipelines im selben Pod laufen, muss die Isolation aktiv sichergestellt werden:

IsolationsaspektMaßnahme
SpeicherSeparate OS-Prozesse -- kein Shared Memory. Go-Runtime isoliert Heap pro Prozess.
REST-API-Zugriffredpanda-connect exponiert standardmäßig eine HTTP-API auf Port 4195 (Metriken, Health, ggf. CRUD). Jeder Prozess muss auf einen eigenen Port gebunden werden, und der Zugriff auf diese Ports muss auf den Supervisor beschränkt werden (z.B. via localhost-Binding oder iptables-Regeln im Pod).
DateisystemJeder Prozess bekommt ein eigenes Arbeitsverzeichnis. Keine gemeinsamen Temp-/Cache-Verzeichnisse.
NetzwerkAlle Prozesse teilen sich die Pod-IP. Ausgehender Netzwerkzugriff wird über NetworkPolicies auf Pod-Ebene eingeschränkt (DB, MQTT-Broker, erlaubte Endpunkte). Feingranulare Netzwerk-Isolation pro Prozess ist innerhalb eines Pods nicht möglich.
RessourcenGesamtlimits auf Pod-Ebene (K8s Resource Limits). Pro-Prozess-Limits ggf. über cgroups v2 oder Supervisor-Logik.

Einschränkung: Netzwerk-Isolation zwischen Pipelines im selben Pod ist nicht möglich -- alle Prozesse teilen die Pod-IP. Ein kompromittierter Prozess könnte theoretisch auf die REST-Ports anderer Prozesse im selben Pod zugreifen. Mitigations: localhost-only-Binding, Port-Randomisierung, ggf. Unix-Domain-Sockets statt TCP.

Scale-Down-Strategie

Beim Herunterskalieren (HPA Scale-Down) darf kein Pod abgeräumt werden, der noch aktive Pipelines enthält. Dafür wird ein aktives Drain-Verfahren eingesetzt:

Ablauf:

1. Provisioner erkennt Unterauslastung
(z.B. Worker-Pods im Schnitt nur 30% belegt)


2. Provisioner wählt Drain-Kandidaten
(Pod mit wenigsten aktiven Pipelines)


3. Provisioner verschiebt Pipelines
von Drain-Kandidat auf andere Worker
(Stop auf altem Worker, Start auf neuem)


4. Drain-Kandidat ist leer
Provisioner setzt Annotation via K8s API:
controller.kubernetes.io/pod-deletion-cost: "-1000"


5. HPA skaliert herunter
ReplicaSet wählt Pod mit niedrigster
Deletion Cost → der leere Pod wird abgeräumt


Sauberes Terminate, keine laufenden Pipelines betroffen.

Pod Deletion Cost Annotation: K8s ReplicaSet bevorzugt beim Löschen Pods mit dem niedrigsten Wert der Annotation controller.kubernetes.io/pod-deletion-cost. Der Provisioner setzt diese Annotation auf -1000 für leere Pods und auf einen positiven Wert (z.B. Anzahl aktiver Pipelines) für aktive Pods. Damit ist deterministisch sichergestellt, dass nur leere Pods abgeräumt werden.

Voraussetzung: K8s-API-Zugriff. Der Provisioner benötigt Schreibzugriff auf die K8s API (PATCH auf Pod-Objekte). Dies ist mit einer Namespace-scoped Role möglich -- keine ClusterRole nötig:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: civitas-pipelines
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "patch"]
- apiGroups: ["apps"]
resources: ["deployments/scale"]
verbs: ["get", "patch"]

Nachteil: Diese Lösung setzt voraus, dass der Provisioner eine K8s-API-Berechtigung innerhalb des Namespace erhält. Das ist ein Zugeständnis gegenüber der Anforderung "limitierte Rechte". Ohne K8s-API-Zugriff ist kein deterministisches Scale-Down möglich -- der ReplicaSet-Controller könnte dann auch einen aktiven Pod terminieren, und das Scale-Down müsste vollständig über SIGTERM-Fallback gelöst werden (siehe unten).

Fallback (SIGTERM): Falls ein Pod unerwartet terminiert wird (z.B. Node-Ausfall, Preemption, oder Scale-Down ohne K8s-API-Zugriff), greift der SIGTERM-Mechanismus: Der Supervisor fängt SIGTERM ab, meldet die betroffenen Pipelines an den Provisioner, der sie auf verbleibende Worker umverteilt. terminationGracePeriodSeconds sollte großzügig konfiguriert werden (z.B. 120-300s). Zusätzlich verhindert ein Pod Disruption Budget (PDB) mit minAvailable, dass zu viele Worker gleichzeitig abgeräumt werden.

Umsetzung der Anforderungen

Pipeline-Varianten

AnforderungUmsetzung
Cron/Schedulergenerate-Input mit cron-Schedule. Provisioner startet den Prozess im zugewiesenen Worker-Pod.
Ereignisgetriebene Verarbeitung (MQTT)mqtt-Input subscribt auf Topics. Prozess läuft dauerhaft im Worker-Pod.
Ereignisgetriebene Verarbeitung (Webhook)http_server-Input empfängt POST-Requests. Ingress routet /webhook/<pipeline-id> an den richtigen Worker-Pod + Port.
HTTP-Endpunkte (Request/Response)http_server-Input mit synchroner Verarbeitung. Siehe Abschnitt "HTTP-Server-Pipelines im Multi-Prozess-Modell" unten.
Batch-Verarbeitungsql_select- oder file-Input liest Batch-Daten, verarbeitet sie mit Bloblang, schreibt in Output.
Stream-VerarbeitungNativer Modus: Input > Processor-Pipeline > Output als kontinuierlicher Stream.

HTTP-Server-Pipelines im Multi-Prozess-Modell

Pipelines, die selbst HTTP-Endpunkte bereitstellen (http_server-Input), stellen im Multi-Prozess-Modell eine besondere Herausforderung dar: Da alle Prozesse im selben Pod die gleiche IP teilen, kann nicht jeder Prozess auf demselben Port lauschen.

Lösungsansatz: Dedizierte Ports pro Pipeline

Jede http_server-Pipeline bekommt vom Supervisor einen eindeutigen Port zugewiesen (z.B. aus einem konfigurierbaren Range wie 10000-19999). Der Supervisor trägt den Port in die generierte YAML-Config ein und meldet das Mapping pipeline-id → pod-ip:port an den Provisioner.

Externer Request                    Worker-Pod (10.0.1.42)
────────────── ──────────────────────

GET /api/pipeline-A/data ┌──────────────────────┐
│ │ Supervisor │
▼ │ │
┌──────────┐ route to │ rpk-connect A │
│ Ingress │ ──────────────────► │ http_server:10001 │
│ │ 10.0.1.42:10001 │ │
└──────────┘ │ rpk-connect B │
│ │ http_server:10002 │
│ route to │ │
└─────────────────────────► │ rpk-connect C │
GET /api/pipeline-C/data │ mqtt (kein Port) │
(rejected: C hat │ │
keinen http_server) └──────────────────────┘

Routing-Kette:

  1. Provisioner pflegt ein Mapping: pipeline-id → worker-pod-ip:port
  2. API Management wird dynamisch konfiguriert:
    • Route /api/pipeline-A/* → Upstream 10.0.1.42:10001
    • Path /api/pipeline-C/* → kein Backend (Pipeline C hat keinen HTTP-Endpunkt)
  3. Alternativ: Ein zentraler Proxy-Prozess im Pod, der auf einem festen Port lauscht und anhand des URL-Pfads an den richtigen lokalen Prozess-Port weiterleitet. Das vereinfacht das Upstream-Routing (nur ein Port pro Pod), verlagert die Routing-Logik aber in den Pod.

Herausforderungen:

  • Dynamisches Upstream-Routing: Wenn eine Pipeline auf einen anderen Worker-Pod verschoben wird (z.B. durch Autoscaling), muss das Upstream-Routing aktualisiert werden. Der Provisioner muss Upstream- (und Routen)-Konfigurationen dynamisch anpassen.
  • Port-Management: Der Supervisor muss freie Ports verwalten und Kollisionen vermeiden. Bei Pod-Neustart müssen Ports stabil reassigned werden (oder der Provisioner aktualisiert das Routing).
  • K8s Service-Abstraktion eingeschränkt: Ein K8s Service kann nur auf einen festen Port pro Pod zeigen. Für mehrere HTTP-Pipelines pro Pod braucht es entweder mehrere Services, einen vorgeschalteten Proxy, oder direktes Pod-IP-Routing über den Ingress.

Bewertung: Machbar, aber ein signifikanter Komplexitätstreiber. Für Pipelines, die nur konsumieren (MQTT, Cron, Batch), ist das Multi-Prozess-Modell unkompliziert. Für Pipelines, die HTTP-Endpunkte bereitstellen, steigt die Komplexität in Upstream-Routing und Lifecycle-Management deutlich.

Konnektoren

AnforderungUmsetzung
MQTTmqtt Input/Output (v3.1.1 + v5)
HTTPhttp_client (ausgehend), http_server (eingehend)
SQLsql_select, sql_insert, sql_raw (PostgreSQL, MySQL, MSSQL, etc.)
Dateienfile, csv, parquet, blob Inputs/Outputs

Prozessoren

AnforderungUmsetzung
JSON-TransformationBloblang-Mapping: root.target = this.source.map_each(...) -- mächtige funktionale Sprache für JSON-Transformationen
Mapping/ValidierungBloblang + json_schema-Prozessor für Validierung
Kontrollflussswitch-Prozessor (if/else), branch-Prozessor (Fan-out), workflow-Prozessor (DAG)
PersistierungOutput-Konnektoren: sql_insert, http_client, mqtt
Fehlerbehandlungretry-Prozessor, reject/dead_letter-Output, max_in_flight-Konfiguration

Performance & Skalierung

AnforderungUmsetzung
Horizontale SkalierungWorker-Pod-Pool mit HPA. Provisioner verteilt Pipelines auf verfügbare Worker. Neue Pods werden bei Bedarf hinzugefügt.
Vertikale SkalierungK8s Resource Requests/Limits pro Worker-Pod. Anzahl Pipelines pro Pod als Konfigurationsparameter des Provisioners.
RessourceneffizienzCa. 10-30 MB RAM pro idle redpanda-connect-Prozess. Bei z.B. 50 Pipelines pro Pod und 10 Worker-Pods: 500 Pipelines in nur 10 Pods. Deutlich weniger K8s-Objekte als im 1:1-Modell.
Hochfrequente Daten (0.5-1s)Go-basiert, Sub-Millisekunde Verarbeitung. Kein Problem für das beschriebene Mengengerüst.

Security & Isolation

AnforderungUmsetzung
Pipeline-IsolationOS-Prozess-Isolation: Jede Pipeline ist ein eigener redpanda-connect-Prozess mit eigenem Heap. Kein Shared Memory. Schwächer als Container-Isolation, aber ausreichend für die Angreifer-Klassen "Authentifizierter Nutzer (lesend/verwaltend)" gemäß Threat Model.
Pipeline-to-Pipeline IsolationPipelines verschiedener DataSets laufen als separate OS-Prozesse -- getrennte Security Domains. Pipelines innerhalb eines DataSets sind ebenfalls separate Prozesse, teilen aber logisch eine gemeinsame Domain (gleiche Berechtigungen im Provisioner). Netzwerk-Isolation zwischen Prozessen im selben Pod ist nicht möglich (shared Pod-IP).
User-to-User IsolationProvisioner stellt sicher, dass Pipelines verschiedener User/DataSets auf Prozess-Ebene getrennt sind. REST-APIs der einzelnen Prozesse sind nur für den Supervisor erreichbar.
User-to-Platform IsolationNetworkPolicies auf Pod-Ebene beschränken Netzwerkzugriff auf erlaubte Ziele (DB, MQTT-Broker, erlaubte externe Endpunkte).
RBACNicht eingebaut. RBAC wird im Provisioner und der Civitas-Plattform implementiert. Redpanda Connect selbst kennt keine Benutzer.
SecretsEnv-Vars pro Prozess (vom Supervisor injiziert), K8s Secrets im Pod-Volume, oder Vault-Integration.

Betrieb & Deployment

AnforderungUmsetzung
K8s ohne CRDsJa. Worker-Pods sind ein normales K8s Deployment + HPA. Provisioner ist ein separates Deployment. Keine CRDs, keine ClusterRoles. Provisioner benötigt eine Namespace-scoped Role für Pod-Annotations (siehe Scale-Down-Strategie).
HelmCustom Helm Chart für Provisioner + Worker-Deployment.
VersionierungPipeline-YAML-Definitionen werden in einer externen Registry versioniert. Der Provisioner liest die aktuelle Version und übergibt sie an den Supervisor im Worker-Pod.
Air-gappedSingle Container Image (redpanda-connect + Supervisor), keine Runtime-Abhängigkeiten. Image vorab in Registry ladbar.

Eigenentwicklungsanteil

Redpanda Connect ist eine Runtime, keine Plattform. Der Eigenentwicklungsanteil ist signifikant:

KomponenteBeschreibungAufwand
ProvisionerZentrale API: Pipeline CRUD, Zuweisung an Worker-Pods, Drain-/Konsolidierungslogik, Lifecycle-Management. Kommuniziert mit K8s API (Pod Deletion Cost Annotations) und Supervisors in den Worker-Pods. Muss verwaiste Pipelines nach Pod-Terminierung erkennen und umverteilen.hoch
Supervisor (im Worker-Pod)Startet/stoppt redpanda-connect-Prozesse, überwacht Health, meldet Status an Provisioner, isoliert REST-Ports, verwaltet Arbeitsverzeichnisse.mittel-hoch
RBAC-SchichtBerechtigungsprüfung im Provisioner: Welcher User darf welche Pipeline erstellen/sehen/bearbeiten? DataSet-Freigabe.mittel
API-RoutingDynamisches Routing von HTTP-Requests an den richtigen Worker-Pod + Port (für http_server-Pipelines). Komplexer als bei 1:1-Pod, da Port-Mapping nötig.mittel
Monitoring-AggregationPrometheus-Scraping der Metriken aller Prozesse in allen Worker-Pods, Dashboard pro Pipeline.gering
Config-ValidierungValidierung der Pipeline-YAML gegen erlaubte Konnektoren und Ziele (Security).mittel
Auditierbarkeit der PipelinesRedpanda Connect bietet keine eingebaute Data Provenance. Für Nachvollziehbarkeit (welche Daten wann wohin geflossen sind) muss ein eigenes Audit-Logging implementiert werden -- z.B. über strukturierte Logs pro Pipeline-Prozess, die zentral aggregiert und korreliert werden. Replay einzelner Nachrichten ist nicht nativ möglich und müsste über Quell-Systeme (MQTT Retained, DB-Snapshots) oder ein vorgeschaltetes Message-Log gelöst werden.mittel

Risiken

RisikoBewertung
Hoher EigenentwicklungsanteilProvisioner + Supervisor sind eine signifikante Eigenentwicklung. Insbesondere der Supervisor (Prozess-Management, Health-Checks, Port-Isolation) ist fehleranfällig und muss robust implementiert werden.
Schwächere Isolation als ContainerProzesse im selben Pod teilen Netzwerk und Kernel. Ein kompromittierter Prozess könnte auf andere Prozesse im Pod zugreifen (z.B. über /proc, localhost-Ports). Gemäß Threat-Model akzeptabel, aber schwächer als bei NiFi (das immerhin RBAC auf App-Ebene hat).
API Management-Komplexität bei HTTP-PipelinesHTTP-Server-Pipelines erfordern dedizierte Ports pro Prozess, dynamisches Upstream-Routing und Port-Management (siehe Abschnitt "HTTP-Server-Pipelines im Multi-Prozess-Modell"). Bei Pipeline-Umzug zwischen Pods muss das Routing aktualisiert werden. Signifikanter Komplexitätstreiber -- für rein konsumierende Pipelines (MQTT, Cron, Batch) entfällt dieses Problem.
Single-Vendor-AbhängigkeitRedpanda Inc. als Hauptentwickler. Apache-2.0-Lizenz schützt vor Lock-in, aber Community ist kleiner als bei Apache-Projekten.
Keine native Multi-TenancyMuss vollständig über Provisioner-Logik, Prozess-Isolation und K8s-Primitives abgebildet werden.
K8s-API-Zugriff für Scale-DownDeterministisches Scale-Down (Pod Deletion Cost) setzt voraus, dass der Provisioner eine Namespace-scoped Role mit pods/patch-Berechtigung erhält (siehe Abschnitt "Scale-Down-Strategie"). Das ist ein Zugeständnis gegenüber der Anforderung "limitierte Rechte". Ohne diesen Zugriff ist Scale-Down nicht-deterministisch: der ReplicaSet-Controller kann auch aktive Pods terminieren, SIGTERM-Fallback wird dann zum Normalfall, und idempotente Pipeline-Designs werden Pflicht.