Polycrate Operator¶
Der Polycrate Operator ist ein Kubernetes Operator, der die automatisierte Verwaltung von Multi-Tenant-Umgebungen über mehrere Systeme hinweg ermöglicht: Keycloak, GitLab, Harbor, ArgoCD und externe Kubernetes Cluster.
Übersicht¶
Der Operator synchronisiert Custom Resources (CRs) mit dem gewünschten Zustand in den verbundenen Systemen:
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ polycrate Namespace │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Polycrate Operator │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Custom Resources (CRs) │ │ │
│ │ │ - SoftwareDeliveryPlatform │ │ │
│ │ │ - Organization │ │ │
│ │ │ - Contact │ │ │
│ │ │ - ContactGroup │ │ │
│ │ │ - GitLabGroup / HarborProject / ArgoCDProject │ │ │
│ │ │ - K8sCluster │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│Keycloak │ │ GitLab │ │ Harbor │ │ ArgoCD │ │K8s Cluster│
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
Custom Resource Definitions (CRDs)¶
Der Operator verwaltet 15 verschiedene Custom Resource Typen:
| CRD | Kurzname | Beschreibung |
|---|---|---|
SoftwareDeliveryPlatform | sdp | Konfiguration der Plattform-Komponenten (Keycloak, GitLab, etc.) |
Organization | org | Multi-System-Organisation (Keycloak-Gruppe, GitLab-Gruppe, Harbor-Projekt, ArgoCD-Projekt) |
Contact | ct | Benutzer-Mapping zu Organisationen und Gruppen |
ContactGroup | cg | Keycloak-Gruppe als Identity Source |
GitLabGroup | glg | Standalone GitLab-Gruppe |
HarborProject | hp | Standalone Harbor-Projekt |
ArgoCDProject | acp | Standalone ArgoCD-Projekt |
K8sCluster | k8sc | Registrierung externer Kubernetes Cluster |
OperatorConfig | oc, opcfg | Operator-weite Konfiguration (Discovery, API-Sync, Local Cluster) |
Endpoint | ep, pep | Endpoint für HTTP-Monitoring (automatisch aus Ingress erstellt) |
Backup | backup | Backup-Status (automatisch aus Velero erstellt) |
BackupSchedule | bs | Backup-Schedule (automatisch aus Velero erstellt) |
Certificate | cert, pcert | TLS-Zertifikat (automatisch aus cert-manager erstellt) |
K8sApp | app, pca | Deployed Polycrate Block (automatisch aus Meta-Secrets erstellt) |
Artifact | art | Container-Image im Cluster (cluster-scoped, automatisch aus Pods erstellt) |
Installation¶
1. CRDs installieren¶
# CRDs direkt aus der CLI installieren
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
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
3. SoftwareDeliveryPlatform konfigurieren¶
apiVersion: polycrate.io/v1alpha1
kind: SoftwareDeliveryPlatform
metadata:
name: production
namespace: polycrate
spec:
keycloak:
url: "https://keycloak.example.com"
realm: "acme"
credentialsRef:
secretName: keycloak-creds
clientIdKey: client-id
clientSecretKey: client-secret
gitlab:
url: "https://gitlab.example.com"
credentialsRef:
secretName: gitlab-creds
tokenKey: token
harbor:
url: "https://harbor.example.com"
credentialsRef:
secretName: harbor-creds
usernameKey: username
passwordKey: password
argocd:
url: "https://argocd.example.com"
credentialsRef:
secretName: argocd-creds
tokenKey: token
roles:
admin:
policies:
- "p, proj:{project}:admin, applications, *, {project}/*, allow"
- "p, proj:{project}:admin, repositories, *, {project}/*, allow"
developer:
policies:
- "p, proj:{project}:developer, applications, get, {project}/*, allow"
- "p, proj:{project}:developer, applications, sync, {project}/*, allow"
viewer:
policies:
- "p, proj:{project}:viewer, applications, get, {project}/*, allow"
settings:
autoCreateRoleSubgroups: true
defaultRoles:
- admins
- developers
- viewers
subgroupNamePattern: "{org}-{role}"
Ressourcen erstellen¶
Organization¶
Eine Organization erstellt automatisch Ressourcen in allen konfigurierten Systemen:
apiVersion: polycrate.io/v1alpha1
kind: Organization
metadata:
name: acme-corp
namespace: polycrate
spec:
sdp: production
displayName: "ACME Corporation"
keycloak:
groupName: acme-corp
attributes:
description: ["Main organization group"]
gitlab:
enabled: true
groupPath: acme-corp
visibility: private
harbor:
enabled: true
projectName: acme-corp
storageQuota: "50Gi"
argocd:
enabled: true
projectName: acme-corp
sourceRepos:
- "https://gitlab.example.com/acme-corp/*"
roleBindings:
- role: admin
keycloakGroups:
- acme-corp-admins
- role: developer
keycloakGroups:
- acme-corp-developers
Erstellte Ressourcen:
- Keycloak: Gruppe
acme-corpmit Subgruppenacme-corp-admins,acme-corp-developers,acme-corp-viewers - GitLab: Gruppe
acme-corp - Harbor: Projekt
acme-corpmit 50Gi Quota - ArgoCD: Projekt
acme-corpmit konfigurierten Rollen
Contact¶
Ein Contact mappt einen Keycloak-Benutzer zu Organisationen:
apiVersion: polycrate.io/v1alpha1
kind: Contact
metadata:
name: john-doe
namespace: polycrate
spec:
keycloak:
username: john.doe
email: john.doe@example.com
organizations:
- name: acme-corp
role: developer
additionalGroups:
- contactGroup: shared-developers
Automatische Aktionen:
- Benutzer in Keycloak nachschlagen (via Username oder Email)
- Zu allen referenzierten Organization-Gruppen hinzufügen
- Zu allen ContactGroup-Gruppen hinzufügen
- GitLab-Gruppenmitgliedschaft synchronisieren
ContactGroup¶
Eine ContactGroup erstellt eine Keycloak-Gruppe als Identity Source:
apiVersion: polycrate.io/v1alpha1
kind: ContactGroup
metadata:
name: platform-admins
namespace: polycrate
spec:
sdp:
- production
keycloak:
groupName: platform-admins
attributes:
description: ["Platform administrators"]
Standalone Ressourcen¶
GitLabGroup¶
apiVersion: polycrate.io/v1alpha1
kind: GitLabGroup
metadata:
name: shared-libs
namespace: polycrate
spec:
sdp:
- production
groupPath: shared-libs
visibility: internal
accessControl:
- keycloakGroup: platform-admins
accessLevel: owner
organizationAccess:
- organization: acme-corp
accessLevel: developer
HarborProject¶
apiVersion: polycrate.io/v1alpha1
kind: HarborProject
metadata:
name: shared-images
namespace: polycrate
spec:
sdp:
- production
projectName: shared-images
public: false
storageQuota: "100Gi"
accessControl:
- keycloakGroup: platform-admins
role: projectAdmin
organizationAccess:
- organization: acme-corp
role: developer
ArgoCDProject¶
apiVersion: polycrate.io/v1alpha1
kind: ArgoCDProject
metadata:
name: infrastructure
namespace: polycrate
spec:
sdp:
- production
projectName: infrastructure
description: "Shared infrastructure applications"
sourceRepos:
- "https://gitlab.example.com/infrastructure/*"
destinations:
- server: https://kubernetes.default.svc
namespace: "*"
- clusterRef: production-cluster
namespace: "*"
accessControl:
- keycloakGroup: platform-admins
role: admin
K8sCluster¶
Registriert einen externen Kubernetes Cluster:
apiVersion: polycrate.io/v1alpha1
kind: K8sCluster
metadata:
name: production-cluster
namespace: polycrate
spec:
sdp: production
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-----
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
#
# SoftwareDeliveryPlatforms
# -------------------------
# NAME NAMESPACE PHASE KEYCLOAK GITLAB HARBOR ARGOCD
# production polycrate Ready ✓ ✓ ✓ ✓
#
# Organizations
# -------------
# NAME NAMESPACE SDP PHASE
# acme-corp polycrate production Ready
#
# ...
Reconciliation-Logik¶
Erstellung¶
- Organization: Erstellt Keycloak-Gruppe → GitLab-Gruppe → Harbor-Projekt → ArgoCD-Projekt
- Contact: Sucht Keycloak-User → Fügt zu Gruppen hinzu → Synchronisiert GitLab-Mitgliedschaft
- ContactGroup: Erstellt Keycloak-Gruppe
Löschung¶
Alle CRs verwenden Finalizer für sauberes Cleanup:
- Organization löschen: ArgoCD-Projekt → Harbor-Projekt → GitLab-Gruppe → Keycloak-Gruppe (in umgekehrter Reihenfolge)
- Contact löschen: Entfernt aus allen Gruppen und Projekten
Status-Updates¶
Jeder Controller aktualisiert den Status der CR:
status:
phase: Ready # oder: Provisioning, Error, Deleting
keycloakGroupID: "abc-123"
gitlabGroupID: 42
harborProjectID: 7
argoCDProjectCreated: true
conditions:
- type: Ready
status: "True"
reason: Provisioned
message: "All organization resources provisioned"
Rate Limiting & Error Handling¶
Der Operator implementiert intelligentes Rate Limiting:
- Base Delay: 5 Sekunden
- Max Delay: 60 Sekunden (Exponential Backoff)
- Max Concurrent Reconciles: 1 pro Controller
Bei Fehlern (z.B. Keycloak nicht erreichbar) wird die Reconciliation mit exponentiell wachsendem Delay wiederholt.
Multi-SDP Support¶
Einige CRs können mehrere SDPs referenzieren für Cross-Platform-Erstellung:
apiVersion: polycrate.io/v1alpha1
kind: ContactGroup
metadata:
name: global-admins
spec:
sdp:
- production
- staging
- development
keycloak:
groupName: global-admins
Dies erstellt die Gruppe in allen drei SDP-Instanzen.
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 → API)
endpoint_discovery:
enabled: true
watch_namespaces: [] # Leer = alle Namespaces
ignore_namespaces: # Diese Namespaces werden ignoriert
- kube-system
- kube-public
- polycrate
ingress_classes: [] # Leer = alle Ingress-Klassen
default_check_interval: 60
# App-Discovery (Meta-Secrets → API)
app_discovery:
enabled: true
watch_namespaces: []
ignore_namespaces:
- kube-system
- kube-public
# 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, etc) │ │ 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 | EndpointDiscoveryController |
| Polycrate Meta-Secret | K8sAppInstance | K8sAppDiscoveryController |
| Node | Host | HostDiscoveryController |
| Velero Backup | Backup | BackupDiscoveryController |
| Velero Schedule | BackupSchedule | BackupDiscoveryController |
| cert-manager Certificate | Certificate | CertificateDiscoveryController |
Endpoint Discovery: Ingress → API Endpoint¶
Der Operator überwacht Ingress-Objekte und erstellt automatisch Endpoints in der Polycrate API für HTTP-Monitoring.
Alle verfügbaren Annotations¶
| Annotation | Typ | Default | Beschreibung |
|---|---|---|---|
endpoints.polycrate.io/enabled | String | "true" | Discovery aktivieren/deaktivieren |
endpoints.polycrate.io/ignore | String | "false" | Diesen Ingress ignorieren |
endpoints.polycrate.io/path | String | / | HTTP-Pfad für Health-Check |
endpoints.polycrate.io/timeout | String | "30" | Timeout in Sekunden |
endpoints.polycrate.io/interval | String | "60" | Check-Intervall in Sekunden |
endpoints.polycrate.io/expected-status | String | "200" | Erwarteter HTTP-Status |
Endpoint Custom Resources
Der Operator erstellt für jeden Hostname im Ingress eine separate Endpoint Custom Resource. Die API-ID wird im Status der Endpoint CR gespeichert, nicht als Annotation auf dem Ingress.
Beispiel 1: Minimale Konfiguration¶
Der einfachste Fall - Endpoint wird mit Defaults erstellt:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp
namespace: production
# Keine Annotations nötig - Defaults werden verwendet
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:
# Health-Check auf /api/health statt Root
endpoints.polycrate.io/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:
# Kritischer Service: Häufigere Checks
endpoints.polycrate.io/path: "/health/ready"
endpoints.polycrate.io/interval: "30"
endpoints.polycrate.io/timeout: "10"
endpoints.polycrate.io/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:
endpoints.polycrate.io/path: "/health/deep"
endpoints.polycrate.io/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
endpoints.polycrate.io/ignore: "true"
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
endpoints.polycrate.io/path: "/api/health"
endpoints.polycrate.io/timeout: "120"
endpoints.polycrate.io/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:
endpoints.polycrate.io/path: "/"
# Redirect ist OK (z.B. HTTP→HTTPS oder /→/login)
endpoints.polycrate.io/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 ===
endpoints.polycrate.io/path: "/api/v1/health"
endpoints.polycrate.io/timeout: "15"
endpoints.polycrate.io/interval: "30"
endpoints.polycrate.io/expected-status: "200"
endpoints.polycrate.io/expected-body: '"status":"healthy"'
# === 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
Endpoint Custom Resource¶
Für jeden Hostname in einem Ingress 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
operator.polycrate.io/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 CRs nicht manuell ändern
Die Endpoint CRs werden vom Operator verwaltet. Änderungen an Annotations auf dem Ingress werden automatisch synchronisiert.
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 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 | Annotation endpoints.polycrate.io/path=/health gesetzt |
| 2 | Operator | Erkennt Ingress, 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 \
endpoints.polycrate.io/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
Latenz
Annotation-Änderungen sind typischerweise innerhalb von 1-2 Minuten aktiv.
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:
Workflow bei Deletion:
- User löscht Ingress:
kubectl delete ingress webapp - Kubernetes setzt
deletionTimestampauf Ingress - 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 Ingress-Objekt
Dies stellt sicher, dass API-Objekte immer mit ihren K8s-Quellen synchron bleiben.
K8sApp Discovery: Meta-Secrets → API¶
Polycrate Blocks erstellen Meta-Secrets die vom Operator als K8sAppInstances synchronisiert werden.
Meta-Secret Format¶
apiVersion: v1
kind: Secret
metadata:
name: polycrate-myapp-meta
namespace: production
labels:
app.kubernetes.io/managed-by: polycrate
polycrate.io/type: meta
data:
# Base64-encoded
organization: YWNtZS1jb3Jw # acme-corp
workspace: cHJvZHVjdGlvbg== # production
block: bXlhcHA= # myapp
name: bXlhcHA= # myapp
namespace: cHJvZHVjdGlvbg== # production
Der Operator:
- Findet Secrets mit Label
polycrate.io/type: meta - Löst
organizationundworkspaceNamen zu UUIDs auf (API-Lookup) - Erstellt/aktualisiert K8sAppInstance in API
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: Velero → API¶
Velero Backups und Schedules werden automatisch zur API synchronisiert.
Backup-Mapping¶
| 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 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 |
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:
- Zentrales Zertifikatsmanagement: Alle Cluster-Zertifikate auf einen Blick
- Ablaufwarnungen: Automatische Benachrichtigungen bei
expiring_soon - Compliance: Übersicht über alle TLS-Endpunkte und deren Zertifikate
- Korrelation: Verknüpfung mit 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.
Artifact Discovery: Container Images → API¶
Der Operator trackt automatisch alle Container-Images im Cluster und synchronisiert sie mit der API.
Besonderheiten¶
- Cluster-scoped: Artifacts sind cluster-weit, nicht namespace-gebunden
- Kein OwnerRef: Pods können aus vielen Namespaces kommen
- Archivierung: Bei CR-Löschung wird das API-Objekt NICHT gelöscht (Archiv-Zwecke)
- Stale-Cleanup: CRs werden nach 1 Stunde ohne aktive Pods automatisch entfernt
Artifact CRD¶
apiVersion: polycrate.io/v1alpha1
kind: Artifact
metadata:
name: cargo-ayedo-cloud-library-nginx-1-25-3
labels:
artifacts.polycrate.io/registry: cargo.ayedo.cloud
artifacts.polycrate.io/repository: library-nginx
artifacts.polycrate.io/tag: "1.25.3"
operator.polycrate.io/discovered: "true"
spec:
image: "cargo.ayedo.cloud/library/nginx:1.25.3"
registry:
hostname: "cargo.ayedo.cloud"
port: 443
scheme: "https"
repository:
path: "library/nginx"
name: "nginx"
namespace: "library"
tag: "1.25.3"
digest: "sha256:abc123..."
status:
phase: Discovered
used_in_namespaces:
- name: production
pod_count: 3
first_seen_at: "2025-12-20T10:00:00Z"
last_seen_at: "2025-12-26T15:30:00Z"
- name: staging
pod_count: 1
used_by_k8sapps:
- name: nginx-ingress
namespace: production
k8s_app_id: "550e8400-..."
first_discovered_at: "2025-12-20T10:00:00Z"
last_seen_at: "2025-12-26T15:30:00Z"
total_pod_count: 4
api_id: "660e8400-..."
sync_status: synced
CLI-Commands¶
# Alle Artifacts anzeigen
kubectl get artifacts
# Ausgabe:
# NAME IMAGE PODS PHASE
# cargo-ayedo-cloud-library-nginx-1-25-3 cargo.ayedo.cloud/library/nginx:1.25.3 5 Discovered
# docker-io-library-redis-7-2 redis:7.2 2 Discovered
# Details anzeigen
kubectl describe artifact cargo-ayedo-cloud-library-nginx-1-25-3
Image-Parsing¶
Der Operator parst Container-Images vollständig:
| Image | Registry | Repository | Tag |
|---|---|---|---|
nginx:1.25 | docker.io | library/nginx | 1.25 |
cargo.ayedo.cloud/ayedo/polycrate:0.28.0 | cargo.ayedo.cloud | ayedo/polycrate | 0.28.0 |
ghcr.io/org/app@sha256:abc | ghcr.io | org/app | - (digest) |
Bidirektionale Verknüpfung¶
Artifacts und K8sApps sind bidirektional verknüpft:
- Artifact.status.used_by_k8sapps[]: Welche K8sApps nutzen dieses Image?
- K8sApp.status.artifacts[]: Welche Images nutzt diese K8sApp?
Die Verknüpfung basiert auf Namespace-Matching.
OperatorConfig: Artifact Discovery¶
apiVersion: polycrate.io/v1alpha1
kind: OperatorConfig
metadata:
name: default
namespace: polycrate
spec:
artifact_discovery:
enabled: true
watch_namespaces: [] # Leer = alle Namespaces
ignore_namespaces:
- kube-system
- kube-public
exclude_registries: # System-Images ignorieren
- registry.k8s.io
- k8s.gcr.io
cleanup:
enabled: true
stale_threshold_hours: 1 # Nach 1h ohne Pods löschen
sync_to_api: true
Use Cases¶
- Security Auditing: Welche Images laufen im Cluster? Von welchen Registries?
- Dependency Tracking: Welche K8sApps nutzen welche Images?
- Compliance: Nachweis aller Software-Artefakte im Cluster
- Update-Planung: Welche Apps sind von einem Image-Update betroffen?