Zum Inhalt

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-corp mit Subgruppen acme-corp-admins, acme-corp-developers, acme-corp-viewers
  • GitLab: Gruppe acme-corp
  • Harbor: Projekt acme-corp mit 50Gi Quota
  • ArgoCD: Projekt acme-corp mit 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:

  1. Benutzer in Keycloak nachschlagen (via Username oder Email)
  2. Zu allen referenzierten Organization-Gruppen hinzufügen
  3. Zu allen ContactGroup-Gruppen hinzufügen
  4. 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

  1. Organization: Erstellt Keycloak-Gruppe → GitLab-Gruppe → Harbor-Projekt → ArgoCD-Projekt
  2. Contact: Sucht Keycloak-User → Fügt zu Gruppen hinzu → Synchronisiert GitLab-Mitgliedschaft
  3. ContactGroup: Erstellt Keycloak-Gruppe

Löschung

Alle CRs verwenden Finalizer für sauberes Cleanup:

  1. Organization löschen: ArgoCD-Projekt → Harbor-Projekt → GitLab-Gruppe → Keycloak-Gruppe (in umgekehrter Reihenfolge)
  2. 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:

  1. Prüft ob in der API bereits ein Cluster für diesen Workspace existiert
  2. Falls ja: API-Cluster hat Vorrang, cluster_name wird ignoriert
  3. Falls nein und cluster_name gesetzt: Neues Cluster wird angelegt
  4. Falls nein und cluster_name nicht 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:

  1. Der Operator sendet einen Health-Report an die API mit dem konfigurierten Agent Token
  2. Die API erkennt anhand des Tokens den zugehörigen Workspace und die Organization
  3. Die API gibt workspace_id, workspace_name, organization_id und organization_name in der Health-Response zurück
  4. 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.io zeigt 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:

  1. Sofort: Kubernetes triggert Operator-Reconciliation
  2. Sofort: Operator erkennt geändertes path, aktualisiert CRD
  3. Sofort: CRD-Änderung triggert API-Sync
  4. ~60s: Agent holt bei nächstem Refresh neue Konfiguration
  5. 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:

metadata:
  finalizers:
    - endpoints.polycrate.io/cleanup

Workflow bei Deletion:

  1. User löscht Ingress: kubectl delete ingress webapp
  2. Kubernetes setzt deletionTimestamp auf Ingress
  3. Operator erkennt Deletion, führt Cleanup durch:
  4. Löscht Endpoint aus Polycrate API via DELETE /api/v1/endpoints/{id}/
  5. Operator entfernt Finalizer
  6. 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:

  1. Findet Secrets mit Label polycrate.io/type: meta
  2. Löst organization und workspace Namen zu UUIDs auf (API-Lookup)
  3. 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:

  1. Workspace aus spec.workspace.name + spec.workspace.organization finden
  2. Block aus spec.block.name im Workspace finden
  3. 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?

Siehe auch