Polycrate Operator
Polycrate Operator
Der Polycrate Operator ist ein Kubernetes Operator, der Cluster-Ressourcen automatisch mit der Polycrate API synchronisiert: Ingresses, HTTPRoutes (Gateway API), Nodes, Backups (Velero und CloudNativePG), TLS-Zertifikate, Volumes und deployte Apps werden kontinuierlich erkannt und an die API gemeldet.
Übersicht
Der Operator überwacht Kubernetes-Ressourcen und synchronisiert sie mit der Polycrate API:
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ polycrate Namespace │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Polycrate Operator │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Custom Resources (CRs) │ │ │
│ │ │ - OperatorConfig │ │ │
│ │ │ - K8sCluster │ │ │
│ │ │ - Endpoint / Host / K8sApp / K8sVolume │ │ │
│ │ │ - Backup / BackupSchedule / Certificate │ │ │
│ │ │ - S3Bucket │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────┐
│ Polycrate API │
└─────────────────────┘
Custom Resource Definitions (CRDs)
Der Operator verwaltet 10 Custom Resource Typen:
| CRD | Kurzname | Beschreibung |
|---|---|---|
OperatorConfig |
oc, opcfg |
Operator-weite Konfiguration (Discovery, API-Sync, Local Cluster) |
K8sCluster |
k8sc |
Registrierung externer Kubernetes Cluster |
Endpoint |
ep, pep |
Endpoint für HTTP-Monitoring (automatisch aus Ingress oder HTTPRoute erstellt) |
Host |
— | Node im Cluster (automatisch aus Kubernetes-Nodes erstellt) |
K8sApp |
app, pca |
Deployed App (aus Meta-Secrets oder Pod-Annotations erkannt) |
K8sVolume |
vol, pvol |
PersistentVolume im Cluster (automatisch aus PersistentVolumes erstellt) |
Backup |
backup |
Backup-Status (Velero und CloudNativePG) |
BackupSchedule |
bs |
Backup-Schedule (Velero und CloudNativePG) |
Certificate |
cert, pcert |
TLS-Zertifikat (automatisch aus cert-manager erstellt) |
S3Bucket |
s3 |
S3 Bucket (deklarativ per CR provisioniert, Credentials als Secret) |
Installation
1. CRDs installieren
Ab Polycrate CLI 0.30.0 sind die CRDs im polycrate-operator Block enthalten (im crds/-Verzeichnis) und werden beim install-Playbook automatisch angewendet. Der empfohlene Weg ist daher:
# Empfohlen: CRDs werden automatisch bei der Installation angewendet
polycrate run polycrate-operator install
Für manuelle CRD-Installation (z.B. GitOps):
# CRDs direkt aus der CLI installieren (legacy)
polycrate operator install-crds
# Alternativ: CRDs ausgeben und manuell installieren
polycrate operator crds | kubectl apply -f -
# Nur bestimmte CRDs ausgeben
polycrate operator crds --kind Organization
polycrate operator crds --kind OperatorConfig
# Alle verfügbaren CRDs auflisten
polycrate operator crds --list
Für GitOps-Setups, bei denen CRDs separat verwaltet werden, kann die automatische Installation deaktiviert werden:
# workspace.poly
blocks:
- name: polycrate-operator
from: cargo.ayedo.cloud/ayedo/k8s/polycrate-operator
config:
crds:
install: false # CRDs werden separat via GitOps deployed
2. Operator starten
# Lokal ausführen (Development)
polycrate operator run --kubeconfig ~/.kube/config
# Mit spezifischem Kubeconfig
polycrate operator run --kubeconfig /path/to/kubeconfig.yml
# Mit Leader Election (für HA-Deployments)
polycrate operator run --leader-elect
Ressourcen erstellen
K8sCluster
Registriert einen externen Kubernetes Cluster:
apiVersion: polycrate.io/v1alpha1
kind: K8sCluster
metadata:
name: production-cluster
namespace: polycrate
spec:
displayName: "Production Cluster"
apiServer: "https://api.prod.example.com:6443"
authentication:
serviceAccountToken:
secretRef:
name: prod-cluster-token
key: token
# Oder via Kubeconfig
# authentication:
# kubeconfig:
# secretRef:
# name: prod-cluster-kubeconfig
# key: kubeconfig
caCertificate: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
→ Loopback-Integration — Anwendersicht
CLI Commands
polycrate operator crds
Gibt CRD-Definitionen aus:
# Alle CRDs ausgeben
polycrate operator crds
# Bestimmten CRD-Typ ausgeben
polycrate operator crds --kind Organization
# In Datei speichern
polycrate operator crds --output-dir ./crds/
# CRDs auflisten
polycrate operator crds --list
polycrate operator install-crds
Installiert CRDs direkt ins Cluster:
# Mit Standard-Kubeconfig
polycrate operator install-crds
# Mit spezifischer Kubeconfig
polycrate operator install-crds --kubeconfig ~/.kube/admin.yaml
# Dry-run (zeigt was installiert würde)
polycrate operator install-crds --dry-run
polycrate operator run
Startet den Operator:
# Lokal mit Kubeconfig
polycrate operator run --kubeconfig ~/.kube/config
# Mit Leader Election
polycrate operator run --leader-elect
# Mit Metrics aktiviert
polycrate operator run --metrics-bind-address :8080
Optionen:
| Flag | Standard | Beschreibung |
|---|---|---|
--kubeconfig |
$KUBECONFIG oder ~/.kube/config |
Pfad zur Kubeconfig |
--leader-elect |
false |
Leader Election für HA aktivieren |
--metrics-bind-address |
:8080 |
Metrics Endpoint |
--health-probe-bind-address |
:8081 |
Health Probe Endpoint |
polycrate operator health
Prüft den Health-Status des Operators:
# Lokalen Operator prüfen
polycrate operator health
# Remote-Operator prüfen
polycrate operator health --url http://polycrate-operator.polycrate:8081
polycrate operator status
Zeigt Status aller Operator-Ressourcen:
polycrate operator status
# Ausgabe:
# Polycrate Operator Status
# =========================
# Cluster: https://kubernetes.default.svc
#
# K8sClusters
# -----------
# NAME NAMESPACE PHASE
# production-cluster polycrate Ready
Rate Limiting & Error Handling
Der Operator implementiert Rate Limiting via Controller-Manager:
- Base Delay: 5 Sekunden
- Max Delay: 60 Sekunden (Exponential Backoff)
- Max Concurrent Reconciles: konfigurierbar via
--max-concurrent-reconciles(Default: 4 für Endpoint-Monitoring und API-Agent)
Bei Fehlern (z.B. API nicht erreichbar) wird die Reconciliation mit exponentiell wachsendem Delay wiederholt.
OperatorConfig: Zentrale Discovery-Konfiguration
Die Discovery wird über eine OperatorConfig CR konfiguriert. Diese ist ein Singleton (Name: default, Namespace: polycrate).
apiVersion: polycrate.io/v1alpha1
kind: OperatorConfig
metadata:
name: default
namespace: polycrate
spec:
# API-Sync Konfiguration
api_sync:
enabled: true
api_url: "https://api.polycrate.io"
credentials_ref:
secret_name: polycrate-api-creds
token_key: token
# workspace_id und organization_id werden NICHT mehr im Spec konfiguriert!
# Diese Werte werden automatisch vom Agent Token abgeleitet und im Status gespeichert.
# Siehe "Auto Workspace Resolution" weiter unten.
# Lokales Cluster bei der API registrieren
# Wird ignoriert wenn API bereits ein Cluster für diesen Workspace hat
local_cluster:
enabled: true
cluster_name: "my-workspace" # Optional: Wird ignoriert wenn Cluster bereits existiert
# Endpoint-Discovery (Ingress + HTTPRoute → API)
endpoint_discovery:
enabled: true
# "annotation" (Standard ab 0.38.0): Nur Ingress/HTTPRoute mit polycrate_endpoint_monitor=true
# "auto": Alle, außer polycrate_endpoint_monitor=false
# HTTPRoute: an, sobald Gateway-API-CRDs existieren (kein Extra-Flag)
mode: annotation
watch_namespaces: [] # Leer = alle Namespaces
ignore_namespaces: # Diese Namespaces werden ignoriert
- kube-system
- kube-public
- polycrate
ingress_classes: [] # Leer = alle Ingress-Klassen
gateway_classes: [] # Leer = alle GatewayClasses
# App-Discovery (Meta-Secrets + Pod-Labels → API)
app_discovery:
enabled: true
watch_namespaces: []
ignore_namespaces:
- kube-system
- kube-public
label_discovery_enabled: true # Label-basierte K8sApp Discovery (ab 0.29.17)
stale_cleanup_minutes: 10 # Intervall fuer Stale-Sweep (ab 0.29.17)
# Node-Discovery (Nodes → API)
node_discovery:
enabled: true
label_selector: ""
# Backup-Discovery (Velero → API)
backup_discovery:
enabled: true
velero_namespace: velero
ignore_namespaces: []
cnpg_enabled: true
Namespace-Filterung
Die Namespace-Filterung funktioniert für alle Discovery-Typen gleich:
| Konfiguration | Verhalten |
|---|---|
watch_namespaces: [] |
Alle Namespaces überwachen |
watch_namespaces: [prod, staging] |
Nur diese Namespaces |
ignore_namespaces: [kube-system] |
Diese Namespaces immer ignorieren |
Wichtig: ignore_namespaces wird von watch_namespaces subtrahiert. Wenn ein Namespace in beiden Listen ist, wird er ignoriert.
Local Cluster Registration
Der Operator kann das Cluster, in dem er läuft, mit der Polycrate API verknüpfen:
Idempotenz-Logik:
- Prüft ob in der API bereits ein Cluster für diesen Workspace existiert
- Falls ja: API-Cluster hat Vorrang,
cluster_namewird ignoriert - Falls nein und
cluster_namegesetzt: Neues Cluster wird angelegt - Falls nein und
cluster_namenicht gesetzt: Kein Cluster wird angelegt
Die aufgelöste Cluster-ID wird im OperatorConfig.status.local_cluster gespeichert für idempotente Operationen.
Wichtig: Die Kubeconfig wird nicht automatisch synchronisiert – dies übernimmt der Git-Sync der Workspaces.
Auto Workspace Resolution (ab 0.29.0)
Ab Version 0.29.0 werden workspace_id und organization_id automatisch vom Agent Token abgeleitet.
Wie funktioniert es:
- Der Operator sendet einen Health-Report an die API mit dem konfigurierten Agent Token
- Die API erkennt anhand des Tokens den zugehörigen Workspace und die Organization
- Die API gibt
workspace_id,workspace_name,organization_idundorganization_namein der Health-Response zurück - Der Operator speichert diese Werte im
OperatorConfig.status.api_sync
Vorteile:
- Keine manuelle UUID-Konfiguration mehr erforderlich
- Token-basierte Authentifizierung = automatische Workspace-Zuordnung
- Reduzierte Fehlerquellen bei der Konfiguration
Status-Felder:
status:
api_sync:
workspace_id: "550e8400-e29b-41d4-a716-446655440000"
workspace_name: "production"
organization_id: "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
organization_name: "acme-corp"
resolved_at: "2026-01-06T14:30:00Z"
Migration von älteren Versionen:
Falls in älteren Versionen workspace_id und organization_id im Spec konfiguriert waren, können diese Felder nach dem Update auf 0.29.0 entfernt werden. Die Werte werden automatisch aus dem Token abgeleitet.
Discovery: Operator-Driven API-Synchronisation
Der Operator erkennt automatisch Kubernetes-Ressourcen und synchronisiert sie direkt mit der Polycrate API. Im Gegensatz zu server-side Import geschieht die Synchronisation in Echtzeit durch den Operator.
┌──────────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ K8s Ressource │ │ Operator │ │ Polycrate API │
│ Ingress / HTTPRoute │ │ Controller │ │ (Django) │
└──────────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ 1. Watch │ │
│◄──────────────────────│ │
│ │ 2. Create/Update │
│ │──────────────────────►│
│ 3. Annotate │ 4. Response: id │
│ (id, synced-at) │◄──────────────────────│
│◄──────────────────────│ │
│ │ │
│ [Deletion] │ 5. Delete │
│ Finalizer Cleanup │──────────────────────►│
│◄──────────────────────│ │
Erkannte Ressourcen
| K8s Ressource | API-Ziel | Discovery Controller |
|---|---|---|
| Ingress | Endpoint | EndpointDiscoveryReconciler |
| HTTPRoute | Endpoint | HTTPRouteDiscoveryReconciler (nur wenn Gateway-API-CRDs existieren; Gateway queued nur) |
| Polycrate Meta-Secret | K8sApp | K8sAppDiscoveryReconciler |
Pod mit polycrate_k8sapp_name Annotation |
K8sApp | K8sAppLabelDiscoveryReconciler |
| Node | Host | HostDiscoveryReconciler |
| Velero Backup | Backup | BackupDiscoveryReconciler |
| Velero Schedule | BackupSchedule | BackupDiscoveryReconciler |
| cert-manager Certificate | Certificate | CertificateDiscoveryReconciler |
| PersistentVolume | K8sVolume | K8sVolumeDiscoveryReconciler |
| S3Bucket CR | S3Bucket | S3BucketReconciler |
Die Umstellung war nötig, weil Kubernetes Finalizer-Namen als DNS-Subdomain validiert und Werte wie `polycrate.io/s3bucket-finalizer` nicht mehr akzeptierte. Finalizer folgen jetzt dem Format `polycrate.io/<resource>-<purpose>` (korrekt FQDN), Labels dem Format `polycrate_<key>` (ebenfalls gültig).
→ [Labels & Konventionen](polycrate-api/labels-conventions.md)
API-Sync und Selbstreparatur (ab 0.30.4)
Der Operator hält discovered Ressourcen automatisch mit der API in Einklang – inkl. Selbstreparatur bei Abweichungen:
| Ressource | Verhalten |
|---|---|
| Endpoint | API-Existenzprüfung: Vor jedem Sync prüft der Operator, ob der Endpoint in der API noch existiert. Wenn die API den Endpoint gelöscht hat (z.B. durch Reconciliation oder manuell), wird er automatisch neu angelegt. |
| K8sApp | Adopt Existing: Wenn eine K8sApp bereits in der API existiert (z.B. durch Workspace-Sync und API-Reconciliation aus Block-Instanzen), übernimmt der Operator deren API-ID statt einen doppelten Eintrag zu erstellen. |
Praktische Auswirkung:
- Endpoints bleiben nach API-Brown-Outs oder manueller Löschung nicht dauerhaft „failed“ – der Operator repariert sie beim nächsten Reconcile.
- K8sApp-Duplikate entstehen nicht mehr, wenn Workspace und API vor dem Operator-Sync aktualisiert wurden.
Label-basierte K8sApp Discovery (ab 0.29.17)
Neben der bestehenden Secret-basierten Erkennung (Polycrate CLI Meta-Secrets) kann der Operator auch Pods erkennen, die mit der Annotation polycrate_k8sapp_name versehen wurden. So koennen Apps in der Polycrate API registriert werden, die nicht ueber die Polycrate CLI deployed wurden.
Verwendung
Pods (bzw. Deployments/StatefulSets) mit folgender Annotation versehen:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-namespace
spec:
template:
metadata:
annotations:
polycrate_k8sapp_name: my-app
spec:
containers:
- name: app
image: my-registry/my-app:latest
Regeln
- Der Label-Wert muss innerhalb eines Namespaces eindeutig auf eine App zeigen
- Mehrere Pods mit dem gleichen Label-Wert im selben Namespace = 1 K8sApp (mehrere Replicas)
- Wenn der gleiche Label-Wert in mehreren Namespaces vorkommt, wird die App ignoriert (Cross-Namespace-Konflikt)
- Fuer Apps in mehreren Namespaces: Disjunkte Namen verwenden (
my-app-ns-a,my-app-ns-b) - Wenn eine App auch ueber die Polycrate CLI deployed wurde (Meta-Secret existiert), hat die Secret-Discovery Vorrang
Konfiguration
app_discovery:
enabled: true
label_discovery_enabled: true # Default: true
watch_namespaces: []
ignore_namespaces:
- kube-system
- kube-public
Unterschiede zur Secret-basierten Discovery
| Eigenschaft | Secret-basiert | Annotation-basiert |
|---|---|---|
| Quelle | Polycrate CLI Meta-Secret | Pod mit polycrate_k8sapp_name Annotation |
| Block-Kind | polycrate, helm, etc. |
external |
| Block-Version | aus Secret | nicht verfuegbar |
| Pod-Status | alle Pods im Namespace | nur Pods mit passender Annotation |
| Cleanup | bei Secret-Loeschung | wenn keine passenden Pods mehr existieren |
Pod-Status: Job- und CronJob-Pods ausgeschlossen (ab 0.29.16)
Der K8sApp-Pod-Status beruecksichtigt keine Pods von Jobs oder CronJobs – z.B. Velero Backup- oder Maintain-Jobs. Diese transienten Pods wuerden die Metriken verfaelschen (Status „Completed“/„Failed“). Nur Pods von Deployments, StatefulSets und DaemonSets fliessen in den K8sApp-Status ein.
Endpoint-K8sApp-Assoziation (ab 0.29.17, Annotation ab 0.38.0)
Endpoints aus der Ingress- und HTTPRoute-Discovery koennen einer K8sApp zugeordnet werden. Ab Version 0.38.0 wird dafür eine Annotation statt einem Label verwendet:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
polycrate_endpoint_monitor: "true"
polycrate_k8sapp_name: my-app # Verknuepfung mit K8sApp (Annotation, ab 0.38.0)
# Deprecated (Label, wird on-reconcile automatisch entfernt):
# k8sapps.polycrate.io/name: my-app
spec:
rules:
- host: my-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
Die Zuordnung wird im Endpoint CR Status gespeichert und an die API uebermittelt. Dies ermoeglicht spaeter praezisere Downtime Detection pro K8sApp.
Bring-your-own-App
Drei Annotations zusammen — der BYOA-Block setzt sie am Ingress und an der HTTPRoute automatisch:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: acme-shop
annotations:
polycrate_endpoint_monitor: "true"
polycrate_k8sapp_name: acme-shop
polycrate_k8sapp_byoa: "true"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: acme-shop
annotations:
polycrate_endpoint_monitor: "true"
polycrate_k8sapp_name: acme-shop
polycrate_k8sapp_byoa: "true"
| Annotation | Pflicht für | Wirkung |
|---|---|---|
polycrate_endpoint_monitor=true |
Endpoint (Modus annotation) |
Pro Host eine Endpoint-CR → API-Checks |
polycrate_k8sapp_name=<name> |
K8sApp | CR kind=external anlegen oder Secret-/Block-App gleichen Namens adoptieren |
polycrate_k8sapp_byoa=true |
BYOA-Flag | API K8sApp.byoa=true (nur auf CRs mit discovery-source=ingress oder httproute) |
Ohne polycrate_k8sapp_name bleibt der Endpoint unclaimed — Monitoring ja, keine App-Downtime. HTTPRoute-Discovery ist an, sobald die Gateway-API-CRDs im Cluster liegen. Gleicher Host im selben Namespace: HTTPRoute gewinnt den Endpoint-CR.
Companion: API Spec 763 (K8sApp.byoa), CLI Spec 292 / 300, Endpoint-Monitoring.
Stale-Sweep und Ressourcen-Cleanup (ab 0.29.17)
Alle Discovery-Controller verfuegen ueber einen periodischen Stale-Sweep, der verwaiste Custom Resources aufraeumt. Dies ist ein Safety-Net fuer Szenarien, in denen Source-Objekte geloescht werden, ohne dass der Operator die Loeschung direkt mitbekommt.
Funktionsweise
- Aktive Loeschung: Wenn ein Source-Objekt (Ingress, HTTPRoute, Secret, etc.) geloescht wird und der Operator laeuft, werden die zugehoerigen CRs sofort aktiv geloescht
- Stale-Sweep: Periodisch prueft jeder Discovery-Controller, ob die Source-Objekte seiner CRs noch existieren. Verwaiste CRs werden automatisch geloescht
- Finalizer: Der Sync-Controller eines geloeschten CRs sorgt via Finalizer fuer die Entfernung des API-Objekts
Konfiguration
Das Sweep-Intervall ist pro Discovery-Controller konfigurierbar:
endpoint_discovery:
stale_cleanup_minutes: 10 # Default: 10
app_discovery:
stale_cleanup_minutes: 10 # Default: 10
backup_discovery:
stale_cleanup_minutes: 10 # Default: 10
certificate_discovery:
stale_cleanup_minutes: 10 # Default: 10
Scope
Der Stale-Sweep behandelt ausschliesslich discovered Objekte (Label polycrate_discovered=true). API-zugewiesene Objekte (z.B. Endpoints die ueber die API konfiguriert wurden) haben einen eigenen Lifecycle und werden nicht vom Stale-Sweep betroffen.
Endpoint Discovery: Ingress und HTTPRoute → API Endpoint
Der Operator überwacht Ingress immer und HTTPRoute, sobald die Gateway-API-CRDs (gateway.networking.k8s.io/v1, Kind HTTPRoute) im Cluster liegen. Kein Extra-Flag: Gate ist CRD + endpoint_discovery.enabled. Pro Host entsteht ein Endpoint in der Polycrate API für HTTP-Monitoring (Endpoint-Monitoring).
Das Gateway-Objekt ist keine Quelle. Es queued abhängige HTTPRoutes neu (Listener-TLS/Port). GRPCRoute und TLSRoute werden nicht beobachtet.
Gleicher Hostname im selben Namespace: HTTPRoute gewinnt den Endpoint-CR (SourceHTTPRoute); Ingress holt ihn nicht zurück.
HTTPRoute (Gateway API)
Hosts kommen aus spec.hostnames (leer = kein Endpoint). Pfad: Annotation polycrate_endpoint_path, sonst erstes PathPrefix, sonst /. TLS und Port vom Parent-Gateway-Listener (parentRefs.sectionName, sonst erster HTTPS-, sonst HTTP-Listener). gateway_classes filtert über die GatewayClass des Parents (leer = alle).
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: acme-shop
namespace: acme-shop
annotations:
polycrate_endpoint_monitor: "true"
polycrate_endpoint_path: "/readyz"
polycrate_k8sapp_name: acme-shop
polycrate_k8sapp_byoa: "true"
spec:
parentRefs:
- name: acme-shop-https
namespace: envoy-gateway-system
sectionName: https
hostnames:
- shop.acme.example
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: acme-shop
port: 8080
Ingress-Beispiele unten gelten analog: dieselben polycrate_*-Annotationen auf der HTTPRoute.
Discovery-Modi (ab 0.38.0)
Ab Version 0.38.0 unterstützt der Operator zwei Entdeckungs-Modi:
| Modus | Verhalten |
|---|---|
annotation (Standard) |
Nur Ingress/HTTPRoute mit polycrate_endpoint_monitor: "true" werden verarbeitet (opt-in) |
auto |
Alle Ingress/HTTPRoute werden verarbeitet, außer polycrate_endpoint_monitor: "false" ist gesetzt (opt-out, Legacy-Verhalten) |
Konfiguration in OperatorConfig:
endpoint_discovery:
enabled: true
mode: annotation # "annotation" (Standard) oder "auto"
gateway_classes: [] # Leer = alle; filtert HTTPRoute über Parent-GatewayClass
Alle verfügbaren Annotationen (ab 0.38.0)
| Annotation (neu, canonical) | Deprecated | Typ | Default | Beschreibung |
|---|---|---|---|---|
polycrate_endpoint_monitor |
(ersetzt endpoints.polycrate.io/enabled + ignore) |
String | — | "true": Monitor aktivieren (in annotation-Mode Pflicht); "false": Ingress/HTTPRoute ignorieren |
polycrate_endpoint_path |
endpoints.polycrate.io/path |
String | / |
HTTP-Pfad für Health-Check (Fallback für alle Hosts) |
polycrate_endpoint_timeout |
endpoints.polycrate.io/timeout |
String | "5" |
Timeout in Sekunden (Fallback für alle Hosts) |
polycrate_endpoint_interval |
endpoints.polycrate.io/interval |
String | "60" |
Check-Intervall in Sekunden (Fallback für alle Hosts) |
polycrate_endpoint_expected_status |
endpoints.polycrate.io/expected-status |
String | "200" |
Erwarteter HTTP-Status (Fallback für alle Hosts) |
polycrate_endpoint_tls |
endpoints.polycrate.io/tls |
String | — | TLS-Konfiguration (Fallback für alle Hosts) |
polycrate_endpoint_port |
endpoints.polycrate.io/port |
String | 443 |
Port für Health-Check (Fallback für alle Hosts) |
polycrate_endpoint_host_config |
— | JSON | — | Per-Host-Overrides für alle obigen Felder (siehe unten) |
polycrate_k8sapp_name |
(ersetzt Label k8sapps.polycrate.io/name) |
String | — | K8sApp anlegen oder bestehende zuordnen (CLI ≥ 0.53.0, Spec 292) |
polycrate_k8sapp_byoa |
— | String | — | "true": ingress- oder httproute-sourced K8sApp als Bring-your-own-App, API K8sApp.byoa=true |
Per-Host-Konfiguration: polycrate_endpoint_host_config
Ingresses mit mehreren Hostnamen können pro Host unterschiedliche Health-Check-Parameter benötigen. Die Annotation polycrate_endpoint_host_config nimmt ein JSON-Objekt entgegen, das Hostnamen auf individuelle Konfigurationen abbildet.
Unterstützte Felder pro Host (alle optional — nicht gesetzte Felder erben den ingress-weiten Fallback-Wert):
| Feld | Typ | Beschreibung |
|---|---|---|
path |
String | HTTP-Pfad für Health-Check |
expected_status |
Array[int] | Erwartete HTTP-Statuscodes |
timeout |
int | Timeout in Sekunden |
interval |
int | Check-Intervall in Sekunden |
tls |
bool | TLS aktivieren/deaktivieren |
port |
int | Port für Health-Check |
Verhalten:
- Hosts, die nicht in
host_configstehen, erhalten unverändert die ingress-weiten Werte (polycrate_endpoint_path,polycrate_endpoint_expected_status, etc.) - Felder, die im Host-Eintrag fehlen, fallen ebenfalls auf den ingress-weiten Fallback zurück
- Malformed JSON wird als Warning geloggt; die Discovery läuft weiter und ignoriert die per-Host-Overrides
Beispiel 1: Minimale Konfiguration (annotation-Mode)
Im Standard-Modus (annotation) muss der Ingress explizit opt-in:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp
namespace: production
annotations:
# Opt-in für Endpoint-Monitoring (Pflicht im annotation-Mode)
polycrate_endpoint_monitor: "true"
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp
port:
number: 80
Ergebnis: Endpoint mit GET / auf https://app.example.com:443, Timeout 30s, Intervall 60s.
Beispiel 2: Custom Health-Check Pfad
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-service
namespace: production
annotations:
polycrate_endpoint_monitor: "true"
# Health-Check auf /api/health statt Root
polycrate_endpoint_path: "/api/health"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 8080
Ergebnis: Endpoint mit GET /api/health auf https://api.example.com:443.
Beispiel 3: Kritischer Service mit kurzem Intervall
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: payment-gateway
namespace: production
annotations:
polycrate_endpoint_monitor: "true"
# Kritischer Service: Häufigere Checks
polycrate_endpoint_path: "/health/ready"
polycrate_endpoint_interval: "30"
polycrate_endpoint_timeout: "10"
polycrate_endpoint_expected_status: "200"
spec:
ingressClassName: nginx
tls:
- hosts:
- pay.example.com
secretName: payment-tls
rules:
- host: pay.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: payment-api
port:
number: 443
Ergebnis: Endpoint mit 30s Intervall (statt 60s), 10s Timeout, erwartet HTTP 200.
Beispiel 4: Body-Validierung für Deep Health-Check
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: database-api
namespace: production
annotations:
polycrate_endpoint_monitor: "true"
polycrate_endpoint_path: "/health/deep"
polycrate_endpoint_expected_status: "200"
# Prüft ob "database":"connected" im Response-Body enthalten ist
endpoints.polycrate.io/expected-body: '"database":"connected"'
spec:
ingressClassName: nginx
tls:
- hosts:
- db-api.example.com
secretName: db-api-tls
rules:
- host: db-api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: db-api
port:
number: 8080
Ergebnis: Check ist nur erfolgreich wenn HTTP 200 UND Body enthält "database":"connected".
Beispiel 5: Ingress von Discovery ausschließen
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: internal-admin
namespace: kube-system
annotations:
# Diesen Ingress NICHT monitoren (in beiden Modi wirksam)
polycrate_endpoint_monitor: "false"
spec:
ingressClassName: nginx
rules:
- host: admin.internal.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin-dashboard
port:
number: 80
Ergebnis: Kein Endpoint wird erstellt. Nützlich für interne Services oder Test-Umgebungen.
Beispiel 6: Langsamer Backend-Service
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: report-generator
namespace: production
annotations:
# Report-Generierung braucht länger
polycrate_endpoint_monitor: "true"
polycrate_endpoint_path: "/api/health"
polycrate_endpoint_timeout: "120"
polycrate_endpoint_interval: "300"
spec:
ingressClassName: nginx
tls:
- hosts:
- reports.example.com
secretName: reports-tls
rules:
- host: reports.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: report-service
port:
number: 8080
Ergebnis: 120s Timeout, 5-Minuten-Intervall für langsame Services.
Beispiel 7: Redirect-fähiger Endpoint (3xx erlauben)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: legacy-app
namespace: production
annotations:
polycrate_endpoint_monitor: "true"
polycrate_endpoint_path: "/"
# Redirect ist OK (z.B. HTTP→HTTPS oder /→/login)
polycrate_endpoint_expected_status: "302"
spec:
ingressClassName: nginx
rules:
- host: legacy.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: legacy-app
port:
number: 80
Beispiel 8: Komplettes Produktions-Setup
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main-api
namespace: production
labels:
app: main-api
environment: production
annotations:
# === Polycrate Endpoint Discovery ===
polycrate_endpoint_monitor: "true"
polycrate_endpoint_path: "/api/v1/health"
polycrate_endpoint_timeout: "15"
polycrate_endpoint_interval: "30"
polycrate_endpoint_expected_status: "200"
# === Nginx Ingress Controller ===
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
# === Cert-Manager ===
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.mycompany.com
secretName: api-tls-cert
rules:
- host: api.mycompany.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- path: /docs
pathType: Prefix
backend:
service:
name: docs-service
port:
number: 80
Beispiel 9: Unterschiedliche Health-Check-Pfade pro Hostname
Ein Ingress mit mehreren Hostnamen, deren Backend-Services unterschiedliche Health-Endpunkte haben:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-service-platform
namespace: production
annotations:
polycrate_endpoint_monitor: "true"
# Fallback: gilt für alle Hosts ohne Eintrag in host_config
polycrate_endpoint_path: "/health"
polycrate_endpoint_expected_status: "200"
# Per-Host-Overrides
polycrate_endpoint_host_config: |
{
"api.example.com": {
"path": "/api/health",
"expected_status": [200]
},
"events.example.com": {
"path": "/health",
"expected_status": [202]
},
"data.example.com": {
"path": "/readyz",
"expected_status": [200],
"timeout": 10
}
}
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
- events.example.com
- data.example.com
- booking.example.com
secretName: platform-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- host: events.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: events-service
port:
number: 80
- host: data.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: data-service
port:
number: 8080
- host: booking.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: booking-service
port:
number: 80
Ergebnis: Der Operator erstellt vier Endpoint CRs mit unterschiedlichen Konfigurationen:
| Hostname | Pfad | Erwarteter Status | Timeout |
|---|---|---|---|
api.example.com |
/api/health |
200 | 5s (Fallback) |
events.example.com |
/health |
202 | 5s (Fallback) |
data.example.com |
/readyz |
200 | 10s (Override) |
booking.example.com |
/health |
200 | 5s (Fallback) |
booking.example.com hat keinen Eintrag in host_config und erbt deshalb automatisch path: /health und expected_status: 200 aus den ingress-weiten Annotationen.
Endpoint Custom Resource
Für jeden Hostname in einem Ingress oder einer HTTPRoute erstellt der Operator eine separate Endpoint Custom Resource:
apiVersion: polycrate.io/v1alpha1
kind: Endpoint
metadata:
name: app-example-com
namespace: production
labels:
endpoints.polycrate.io/source-ingress: webapp
polycrate_discovered: "true"
spec:
displayName: "app.example.com"
remoteAddress: "app.example.com"
remotePort: 443
kind: "http"
tls: true
path: "/api/health"
timeout: 30
interval: 60
expectedStatusCodes: [200]
status:
phase: "synced"
apiID: "550e8400-e29b-41d4-a716-446655440000"
configHash: "abc123..."
Vorteile des CRD-Modells:
- Klare 1:1 Zuordnung: Jeder Hostname = eine CR = eine API-ID
- Garbage Collection: OwnerReferences ermöglichen automatisches Cleanup
- Einfaches Debugging:
kubectl get endpoints.polycrate.iozeigt alle Endpoints
# Alle Endpoint CRs anzeigen
kubectl get endpoints.polycrate.io -A
# Details einer Endpoint CR
kubectl describe endpoint app-example-com -n production
Endpoint Monitoring: Operator → API → Agent
Der Operator ist der erste Schritt in der Monitoring-Pipeline. Nach der Synchronisation mit der API übernehmen Monitoring-Agents die eigentlichen HTTP-Checks.
Architektur-Übersicht
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ Ingress / HTTPRoute Polycrate Operator ││
│ │ + Annotations ───► Endpoint Discovery ││
│ │ + API Sync ││
│ └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
│ │
│ ▼
│ ┌─────────────────┐
│ │ Polycrate API │
│ │ - Endpoints │
│ │ - Agent-Zuweis.│
│ └─────────────────┘
│ │
│ ▼
│ ┌─────────────────┐
│◄────────────────────│ Monitoring Agent│
│ HTTP Check │ - Fetch Config │
│ │ - Execute Check │
│ │ - Prometheus │
└─────────────────────┴─────────────────┘
Datenfluss im Detail
| Schritt | Komponente | Aktion |
|---|---|---|
| 1 | Ingress / HTTPRoute | Annotation polycrate_endpoint_path=/health gesetzt |
| 2 | Operator | Erkennt die Source, erstellt Endpoint CRD mit spec.path |
| 3 | Operator | Synchronisiert CRD mit API (spec.http.path) |
| 4 | API | Speichert Endpoint, weist Agents zu |
| 5 | Agent | Fragt zugewiesene Endpoints ab (inkl. spec) |
| 6 | Agent | Baut Check-URL: https://host:443/health |
| 7 | Agent | Führt HTTP-Check aus, exposed Prometheus-Metriken |
Änderungen propagieren
Wenn Sie eine Annotation ändern, wird die Änderung automatisch propagiert:
# Annotation ändern
kubectl annotate ingress my-app \
polycrate_endpoint_path="/api/v2/health" \
--overwrite
# analog: kubectl annotate httproute my-app polycrate_endpoint_path="/api/v2/health" --overwrite
Propagierungs-Kette:
- Sofort: Kubernetes triggert Operator-Reconciliation
- Sofort: Operator erkennt geändertes
path, aktualisiert CRD - Sofort: CRD-Änderung triggert API-Sync
- ~60s: Agent holt bei nächstem Refresh neue Konfiguration
- Nächster Check: Agent prüft gegen neue URL
Prometheus-Metriken
Die Monitoring-Agents exposen Check-Ergebnisse als Prometheus-Metriken:
# Endpoint-Verfügbarkeit
probe_success{endpoint="api-example-com", organization="acme-corp"}
# Response-Zeit in Sekunden
probe_duration_seconds{endpoint="api-example-com"}
# HTTP Status Code
probe_http_status_code{endpoint="api-example-com"}
Diese Metriken werden von VictoriaMetrics/Prometheus gescraped und stehen für Alerting und Dashboards zur Verfügung.
→ Ausführliche Agent-Dokumentation: Polycrate API - Endpoint Monitoring
Finalizer: Automatisches Cleanup
Der Operator setzt einen Finalizer auf alle entdeckten Ressourcen:
metadata:
finalizers:
- endpoints.polycrate.io/discovery-cleanup # Ingress
- endpoints.polycrate.io/httproute-discovery-cleanup # HTTPRoute
Workflow bei Deletion:
- User löscht Ingress oder HTTPRoute
- Kubernetes setzt
deletionTimestampauf die Source - Operator erkennt Deletion, führt Cleanup durch:
- Löscht Endpoint aus Polycrate API via
DELETE /api/v1/endpoints/{id}/ - Operator entfernt Finalizer
- Kubernetes löscht das Source-Objekt
Dies stellt sicher, dass API-Objekte immer mit ihren K8s-Quellen synchron bleiben.
K8sApp Discovery: Meta-Secrets → CR → API
Nach einer Action schreibt die CLI oft ein Meta-Secret. Der Operator erzeugt daraus ein K8sApp-CR und synct es in die API (PatchK8sApp). Das API-Modell K8sAppInstance ist entfernt (API 0.32.0) — nicht verwechseln mit Block-Kind k8sappinstance.
Details (CRD-Beispiel, Label-Discovery, Adopt): K8sApp Discovery: Deployed Blocks → API und Label-basierte Discovery.
Host Discovery: Nodes → API
Kubernetes Nodes werden automatisch als Hosts in der API erfasst.
Gemappte Felder
| Node-Feld | Host-Feld | Quelle |
|---|---|---|
metadata.name |
name, hostname |
Node Name |
status.addresses[ExternalIP] |
default_ipv4 |
Node IP |
status.capacity.cpu |
resource_cpu_cores |
CPU Cores |
status.capacity.memory |
resource_memory |
Memory (MB) |
status.nodeInfo.architecture |
resource_cpu_architecture |
Architecture |
spec.providerID |
provider_id |
Cloud Provider ID |
Backup Discovery
Der Operator synchronisiert Backups und Schedules aus Velero und (ab CLI/Operator 0.49.0) CloudNativePG (CNPG) in die Polycrate API. Pro Eintrag werden u. a. kind, provider und source_namespace mitgeliefert.
Quellen
| Quelle | Erkannte Ressourcen |
|---|---|
| Velero | Backup, Schedule |
| CloudNativePG | Backup, ScheduledBackup |
Backup-Mapping (Velero)
| Velero Backup | API Backup | Mapping |
|---|---|---|
metadata.name |
name |
Direkt |
status.phase |
status |
Completed→completed, Failed→failed |
spec.ttl |
retention_policy |
TTL als String |
status.startTimestamp |
started_at |
ISO 8601 |
status.completionTimestamp |
completed_at |
ISO 8601 |
status.progress.itemsBackedUp |
items_backed_up |
Integer |
Schedule-Mapping (Velero)
| Velero Schedule | API BackupSchedule | Mapping |
|---|---|---|
metadata.name |
name |
Direkt |
spec.schedule |
schedule_cron |
Cron-Expression |
spec.paused |
paused |
Boolean |
status.lastBackup.startTime |
last_backup_at |
Timestamp |
status.lastBackup.name |
last_backup_name |
String |
CNPG-Objekte werden analog als Backups bzw. Schedules mit Provider cloudnative-pg (bzw. dem erkannten Provider) registriert.
Certificate Discovery: cert-manager → API
Der Operator überwacht cert-manager Certificate CRs und synchronisiert sie mit der Polycrate API für zentrales TLS-Zertifikatsmanagement.
Voraussetzungen
- cert-manager muss im Cluster installiert sein
- Der Operator erkennt automatisch ob cert-manager CRDs vorhanden sind
- Falls cert-manager nicht installiert ist, wird der
CertificateDiscoveryControllerübersprungen
Architektur
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌──────────────────┐ ┌─────────────────────────────────┐ │
│ │ cert-manager │ │ Polycrate Operator │ │
│ │ Certificate CR │────►│ CertificateDiscoveryController │ │
│ │ (cert-manager.io)│ │ ▼ │ │
│ └──────────────────┘ │ polycrate.io/Certificate CR │ │
│ │ ▼ │ │
│ │ CertificateController │ │
│ │ │ │ │
│ └───────────┼──────────────────────┘ │
└───────────────────────────────────────┼──────────────────────────┘
│
▼
┌─────────────────┐
│ Polycrate API │
│ /certificates/ │
└─────────────────┘
Zwei-Controller-Architektur
| Controller | Watchet | Erstellt/Aktualisiert |
|---|---|---|
CertificateDiscoveryController |
cert-manager Certificate |
polycrate.io Certificate CR |
CertificateController |
polycrate.io Certificate |
API-Objekt via REST |
Vorteile:
- CRD-basiert: Zertifikate sind als Kubernetes-Ressourcen sichtbar (
kubectl get certificates.polycrate.io) - Finalizer-Cleanup: Automatische API-Deletion bei K8s-Deletion
- Change Detection: Hash-basierte Änderungserkennung verhindert unnötige API-Updates
- Inventarprüfung: API-Objekte werden bei jedem Reconcile auf Existenz geprüft
Certificate CRD
Für jedes cert-manager Certificate erstellt der Operator eine polycrate.io/Certificate CR:
apiVersion: polycrate.io/v1alpha1
kind: Certificate
metadata:
name: certmanager-production-api-example-com-tls
namespace: production
labels:
certificates.polycrate.io/issuer: letsencrypt-production
certificates.polycrate.io/source: api-example-com-tls
certificates.polycrate.io/ready: "true"
operator.polycrate.io/discovered: "true"
spec:
name: api-example-com-tls
display_name: "api.example.com-tls"
namespace: production
secret_name: api-example-com-tls
dns_names:
- api.example.com
issuer_name: letsencrypt-production
issuer_kind: clusterissuer
issuer_group: cert-manager.io
certificate_status: ready
is_ready: true
not_before: "2024-01-01T00:00:00Z"
not_after: "2024-04-01T00:00:00Z"
renewal_time: "2024-03-01T00:00:00Z"
source_cert_manager:
name: api-example-com-tls
namespace: production
uid: "abc-123-..."
revision: 1
status:
phase: Synced
sync_status: synced
api_id: "550e8400-e29b-41d4-a716-446655440000"
config_hash: "abc123..."
last_synced_at: "2024-01-15T10:30:00Z"
API-Mapping
| cert-manager Certificate | Polycrate Certificate CR | API Certificate |
|---|---|---|
metadata.name |
spec.source_cert_manager.name |
- (Referenz) |
spec.secretName |
spec.secret_name |
secret_name |
spec.dnsNames |
spec.dns_names |
dns_names |
spec.ipAddresses |
spec.ip_addresses |
ip_addresses |
spec.issuerRef.name |
spec.issuer_name |
issuer_name |
spec.issuerRef.kind |
spec.issuer_kind |
issuer_kind |
status.notBefore |
spec.not_before |
not_before |
status.notAfter |
spec.not_after |
not_after |
status.renewalTime |
spec.renewal_time |
renewal_time |
| Ready Condition | spec.is_ready, spec.certificate_status |
is_ready, certificate_status |
Status-Mapping
| cert-manager Status | certificate_status |
|---|---|
| Ready=True | ready |
| Ready=False, Reason=Issuing | pending |
| Ready=False, Reason=Failed | failed |
| Ready=True, NotAfter < now | expired |
| Ready=True, NotAfter < now+30d | expiring_soon |
OperatorConfig: Certificate Discovery
apiVersion: polycrate.io/v1alpha1
kind: OperatorConfig
metadata:
name: default
namespace: polycrate
spec:
# ... andere Konfiguration ...
# Certificate-Discovery (cert-manager → API)
certificate_discovery:
enabled: true
watch_namespaces: [] # Leer = alle Namespaces
ignore_namespaces: # Diese Namespaces ignorieren
- kube-system
- cert-manager
issuer_filter: # Optional: Nur bestimmte Issuers
include_issuers: [] # Leer = alle Issuers
exclude_issuers: # Diese Issuers ignorieren
- selfsigned-issuer
CLI-Commands
# Alle Certificate CRs anzeigen
kubectl get certificates.polycrate.io -A
# Certificate-Details anzeigen
kubectl describe certificate.polycrate.io certmanager-production-api-example-com-tls -n production
# API-Sync-Status prüfen
kubectl get certificates.polycrate.io -A -o custom-columns=\
NAME:.metadata.name,\
NAMESPACE:.metadata.namespace,\
ISSUER:.spec.issuer_name,\
READY:.spec.is_ready,\
STATUS:.spec.certificate_status,\
SYNC:.status.sync_status,\
API_ID:.status.api_id
Verwendung in der Polycrate API
Die synchronisierten Zertifikate sind in der Polycrate API unter /api/v1/certificates/ verfügbar:
- Zertifikate aus dem Cluster in der API sichtbar
- Warnungen bei Status
expiring_soon - Zuordnung zu Endpoints und K8sClusters
K8sApp Discovery: Deployed Blocks → API
Der Operator erkennt automatisch deployte Polycrate Blocks anhand der Meta-Secrets (*.poly) und erstellt K8sApp Custom Resources.
Architektur
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌──────────────────┐ ┌─────────────────────────────────┐ │
│ │ Meta-Secret │ │ Polycrate Operator │ │
│ │ (*.poly) │────►│ K8sAppDiscoveryController │ │
│ │ │ │ ▼ │ │
│ └──────────────────┘ │ polycrate.io/K8sApp CR │ │
│ │ ▼ │ │
│ │ K8sAppController │ │
│ │ │ │ │
│ └───────────┼──────────────────────┘ │
└───────────────────────────────────────┼──────────────────────────┘
│
▼
┌─────────────────┐
│ Polycrate API │
│ /k8sapps/ │
└─────────────────┘
K8sApp CRD
Für jedes Meta-Secret erstellt der Operator eine K8sApp CR:
apiVersion: polycrate.io/v1alpha1
kind: K8sApp
metadata:
name: nginx-ingress
namespace: production
labels:
k8sapps.polycrate.io/block: nginx-ingress
k8sapps.polycrate.io/kind: helm
k8sapps.polycrate.io/type: ingress-controller
operator.polycrate.io/discovered: "true"
ownerReferences:
- apiVersion: v1
kind: Secret
name: nginx-ingress.poly
uid: abc-123-...
spec:
block:
name: "nginx-ingress"
display_name: "NGINX Ingress Controller"
version: "1.2.0"
app_version: "1.9.6"
from: "cargo.ayedo.cloud/ayedo/k8s/nginx-ingress"
type: "ingress-controller"
flavor: "nginx"
chart:
name: "ingress-nginx"
version: "4.9.0"
repo_url: "https://kubernetes.github.io/ingress-nginx"
workspace:
name: "production"
organization: "acme-corp"
action:
name: "install"
status: "success"
last_executed_at: "2025-12-20T10:00:00Z"
status:
phase: Synced
installed: true
api_id: "550e8400-e29b-41d4-a716-446655440000"
sync_status: synced
artifacts:
- name: "cargo-ayedo-cloud-library-nginx-1-25-3"
image: "cargo.ayedo.cloud/library/nginx:1.25.3"
pod_count: 3
artifact_count: 2
CLI-Commands
# Alle K8sApps anzeigen
kubectl get k8sapps -A
# Ausgabe:
# NAMESPACE NAME BLOCK VERSION ARTIFACTS PHASE AGE
# prod nginx-ingress nginx-ingress 1.2.0 3 Synced 7d
# prod redis redis 7.2.0 1 Synced 14d
# Details anzeigen
kubectl describe k8sapp nginx-ingress -n prod
Installation-Status Ableitung
Der Operator leitet den Installation-Status aus dem letzten Action-Run ab:
| action_name | action_status | Ergebnis |
|---|---|---|
install |
success |
installed=true |
install |
running |
installation_running=true |
install |
failed |
installation_failed=true |
uninstall |
success |
uninstalled=true |
Block-ID Verknüpfung
Bei der API-Synchronisierung wird automatisch die Block-ID aufgelöst:
- Workspace aus
spec.workspace.name+spec.workspace.organizationfinden - Block aus
spec.block.nameim Workspace finden - K8sApp mit Block-ID an API senden
Dies ermöglicht vollständige Korrelation zwischen K8sApp ↔ Block ↔ Workspace in der API.
S3Bucket Provisioning: CR → API → Secret (ab 0.35.0)
Der Operator kann S3 Buckets deklarativ über Custom Resources provisionieren. Ein Kubernetes-User erstellt eine S3Bucket CR — der Operator erstellt den Bucket in der Polycrate API und legt die Access Credentials als Secret im Namespace ab.
Verwendung
apiVersion: polycrate.io/v1alpha1
kind: S3Bucket
metadata:
name: my-app-data
namespace: production
spec:
name: my-app-data
region: fsn1
reclaim_policy: retain
| Feld | Pflicht | Default | Beschreibung |
|---|---|---|---|
spec.name |
Ja | - | Name des S3 Buckets |
spec.region |
Nein | default_s3_bucket_cluster aus SystemConfig |
Region-String (muss einem S3Cluster in der API entsprechen) |
spec.reclaim_policy |
Nein | retain |
retain = Bucket bleibt bei CR-Löschung, delete = Bucket wird gelöscht |
Credential Secret
Der Operator erstellt automatisch ein Secret s3-<bucket-name>-credentials im Namespace der CR:
apiVersion: v1
kind: Secret
metadata:
name: s3-my-app-data-credentials
namespace: production
labels:
polycrate_managed_by: operator
polycrate_object_type: s3-credentials
polycrate_s3_bucket_name: my-app-data
polycrate_s3_bucket_region: fsn1
ownerReferences:
- apiVersion: polycrate.io/v1alpha1
kind: S3Bucket
name: my-app-data
type: Opaque
stringData:
AWS_ACCESS_KEY_ID: "<access-key>"
AWS_SECRET_ACCESS_KEY: "<secret-key>"
S3_ENDPOINT: "https://s3.fsn1.example.com"
S3_REGION: "fsn1"
BUCKET_NAME: "my-app-data"
Das Secret wird über ownerReferences an die CR gebunden — bei CR-Löschung wird es automatisch entfernt.
Status-Phasen
| Phase | Bedeutung |
|---|---|
Pending |
CR erstellt, Verarbeitung steht aus |
Provisioning |
Bucket wird in der API erstellt |
WaitingForCredentials |
Bucket existiert, Credentials werden abgerufen |
Ready |
Bucket und Secret sind verfügbar |
Error |
Fehler bei Provisioning oder Credential-Abruf |
Adopt-Pattern
Wenn ein Bucket mit dem angegebenen Namen und der Region bereits in der API existiert, wird er übernommen (Adopt). Der Operator erstellt nur das Credential Secret — der Bucket wird nicht doppelt angelegt.
Konfiguration
apiVersion: polycrate.io/v1alpha1
kind: OperatorConfig
metadata:
name: default
namespace: polycrate
spec:
s3_bucket_provisioning:
enabled: true # Default: true (ab 0.35.0)
Default-Region
Wenn keine region in der CR angegeben ist, verwendet der Operator die default_s3_bucket_cluster-Einstellung aus der Polycrate API SystemConfig. Diese kann im Dashboard unter System Settings konfiguriert werden.
Cloud Controller (CCM-lite)
Der Operator kann als einziger Cloud Controller im Cluster fungieren (kein paralleles Provider-CCM wie hcloud-ccm). Opt-in über OperatorConfig.spec.cloud_controller.
Semantik
| Fähigkeit | Verhalten |
|---|---|
| Node Init | Nach erfolgreichem Host-API-Sync: providerID=polycrate://<clusterID>/<node>, Label topology.kubernetes.io/region = status.agent.pop_name, Taint node.cloudprovider.kubernetes.io/uninitialized entfernen. Ohne pop_name: Requeue, Taint bleibt. Kein automatisches Löschen von Nodes. |
| LoadBalancer | Service vom Typ LoadBalancer → Polycrate API LoadbalancerInstance (NodeIP:NodePort). Öffentliche IP wird gepollt und in Service.status.loadBalancer.ingress geschrieben. |
Voraussetzungen Cluster
- kubelet mit
--cloud-provider=external - Kein anderes CCM parallel für dieselben Nodes/LoadBalancer
Konfiguration
apiVersion: polycrate.io/v1alpha1
kind: OperatorConfig
metadata:
name: default
namespace: polycrate
spec:
cloud_controller:
enabled: true
load_balancer:
enabled: true
# all (Default): alle Services type=LoadBalancer außer polycrate.io/loadbalancer=ignore
# opt-in: nur Services mit polycrate.io/loadbalancer=managed
service_scope: all
| Annotation | Wirkung |
|---|---|
polycrate.io/loadbalancer=ignore |
Service wird nicht verwaltet (service_scope: all) |
polycrate.io/loadbalancer=managed |
Service wird verwaltet (service_scope: opt-in) |
DNS01 Webhook (cert-manager)
Der Operator kann den cert-manager DNS01 Challenge-Webhook einbetten (kein separates Binary). Challenges schreiben TXT-Records über die Polycrate DNS-API (ResolveDNSZone + DNSRecord CRUD).
Abgrenzung
- DNS01 Webhook = Present/CleanUp für ACME-Challenges
- Certificate Discovery (siehe oben) = Inventar von cert-manager
CertificateCRs → API — unabhängig vom Webhook
Konfiguration
apiVersion: polycrate.io/v1alpha1
kind: OperatorConfig
metadata:
name: default
namespace: polycrate
spec:
dns01_webhook:
enabled: true
group_name: webhook.dns.polycrate.io
solver_name: polycrate
port: 10250
Default: enabled: false (opt-in). Beim Aktivieren deployt der Operator-Block TLS-Secret, APIService und RBAC (siehe Block-Templates).
ClusterIssuer Beispiel
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-dns-account
solvers:
- dns01:
webhook:
groupName: webhook.dns.polycrate.io
solverName: polycrate
Zone-Auflösung: API bevorzugt kind=internal, sonst eine externe Zone mit primary_zone=true. Agent-Token braucht DNS-Permissions (Zone lesen, Records CRUD).
external-dns Webhook
Der Operator stellt einen external-dns Webhook-Provider bereit (HTTP, Default-Port 8888). external-dns bleibt ein separates Deployment; Sources, Policy und Ownership sind Sache von external-dns.
Operator-Konfiguration
apiVersion: polycrate.io/v1alpha1
kind: OperatorConfig
metadata:
name: default
namespace: polycrate
spec:
external_dns_webhook:
enabled: true # Default: true
port: 8888
DomainFilter und Record-CRUD nutzen die Polycrate DNS-API im Scope des Agent-Tokens (alle sichtbaren Zonen der Organisation). Der Webhook ist per ClusterIP erreichbar; die NetworkPolicy erlaubt Ingress auf Port 8888 aus dem gesamten Cluster (keine Label-/Namespace-Kopplung an die external-dns-Installation).
external-dns Deployment Beispiel
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: external-dns
spec:
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: registry.k8s.io/external-dns/external-dns:v0.18.0
args:
- --source=ingress
- --provider=webhook
- --webhook-provider-url=http://polycrate-operator.polycrate.svc:8888
- --txt-owner-id=my-cluster
- --policy=upsert-only
| Flag | Hinweis |
|---|---|
--provider=webhook |
Pflicht — Provider ist der Operator-Webhook |
--webhook-provider-url |
ClusterIP-Service des Operators (Port aus external_dns_webhook.port) |
--txt-owner-id |
Kernlogik von external-dns — pro Cluster eindeutig setzen, damit Records anderer Writer (z. B. DNS01 _acme-challenge) nicht gelöscht werden |
--source / --policy |
Nur external-dns; nicht Teil der OperatorConfig |