PlatformConfig
Ambito: cluster · Singleton (nome cluster)
PlatformConfig configura l'operatore stesso — il modo in cui distribuisce e gestisce la piattaforma. Oggi configura il container registry: fa puntare ogni immagine distribuita dall'operatore a un registry personalizzato (ad es. un mirror air-gapped), con le credenziali di pull, in un unico punto.
apiVersion: core.navique.com/v1alpha1
kind: PlatformConfig
metadata:
name: cluster # singleton — only "cluster" is honoured
spec:
registry:
# Mirror host, optionally with a path prefix.
host: registry.example.com/mirror
auth:
secretRef:
name: registry-pull-secret # a kubernetes.io/dockerconfigjson Secret
namespace: navique-system # defaults to the operator system namespaceCosa copre
Quando spec.registry.host è impostato, l'operatore applica il mirroring dell'host a ogni immagine che distribuisce — sostituisce l'host del registry preservando percorso e tag dell'immagine, così ghcr.io/cloudnative-pg/cloudnative-pg:1.29 diventa registry.example.com/mirror/cloudnative-pg/cloudnative-pg:1.29. Questo vale per:
- Gli operatori di capacità inclusi — CloudNativePG, cert-manager, External Secrets, Sealed Secrets, gli operatori ClickHouse e Redis, MongoDB (MCK), gli operatori LiteLLM e Langfuse, Meilisearch, Sail/Istio. I valori delle immagini di ciascun chart vengono riscritti verso il mirror.
- Le istanze di datastore e workload gestite dagli operatori — Redis, LiteLLM (gateway + job di migrazione), Langfuse (web + worker), LibreChat, Meilisearch, il control plane di Istio (istiod / ztunnel / CNI).
- La console del management plane — il suo unico override del registry (non ha un'impostazione separata).
Il pull Secret referenziato da spec.registry.auth.secretRef viene propagato: l'operatore lo copia in ogni namespace in cui distribuisce (i namespace di sistema degli operatori di capacità e ogni namespace dei workload) e lo collega come imagePullSecrets sui chart, sui CR di istanza emessi e sui pod gestiti dall'operatore.
Cosa NON copre
- L'immagine dell'operatore stesso. L'operatore è già in esecuzione, quindi non può usare un CR per decidere da dove scaricare sé stesso. Impostala in fase di installazione tramite l'Helm chart dell'operatore —
image.repositoryeimagePullSecrets— che è anche il punto in cui fornire il registry di bootstrap per un'installazione air-gapped. - Le immagini operand di CloudNativePG e ClickHouse (le immagini server PostgreSQL / ClickHouse che l'operatore upstream seleziona autonomamente) ricevono il pull Secret ma mantengono il riferimento all'immagine predefinito dell'operatore — non esiste un tag stabile da fissare nuovamente in sicurezza. Replica queste immagini nel mirror allo stesso percorso di repository; il pull Secret consente ai pod delle istanze di autenticarsi presso il mirror.
- Un paio di immagini init
busyboxinterne ai chart Meilisearch / LibreChat non sono esposte come valori e non vengono riscritte.
Seeding in fase di installazione (consigliato)
Per un'installazione GitOps o air-gapped, precompila la PlatformConfig con l'Helm chart dell'operatore, così che già la prima installazione di un operatore di capacità avvenga tramite mirror:
helm install navique deploy/helm \
--set image.repository=registry.example.com/mirror/scigility/navique-ai-core-operator \
--set imagePullSecrets[0].name=registry-pull-secret \
--set registry.host=registry.example.com/mirror \
--set registry.pullSecret.name=registry-pull-secretÈ possibile impostare anche registry.pullSecret.dockerConfigJson per far creare il Secret al chart; in caso contrario deve già esistere nel namespace di sistema.
Valori predefiniti a livello di operatore
Oltre al registry, spec.defaults e spec.lifecycle configurano il modo in cui l'operatore distribuisce e gestisce tutto. La spec di un componente sovrascrive sempre il valore predefinito corrispondente.
spec:
defaults:
clusterDomain: cluster.local # custom cluster DNS domain (computed service URLs)
storageClass: fast-ssd # default for managed-datastore PVCs
commonLabels: { team: platform } # stamped on every operator-created object
commonAnnotations: { owner: ai }
proxy: # injected as HTTP(S)_PROXY / NO_PROXY env
httpProxy: http://proxy:3128
httpsProxy: http://proxy:3128
noProxy: .svc,.cluster.local,10.0.0.0/8
scheduling: # default pod placement
nodeSelector: { workload: platform }
tolerations: [{ key: platform, operator: Exists }]
priorityClassName: system-cluster-critical
securityContext: { runAsNonRoot: true, seccompProfile: { type: RuntimeDefault } }
imagePullPolicy: IfNotPresent
registry:
operandImages: # pin operator-chosen operand images (host-mirrored)
postgres: ghcr.io/cloudnative-pg/postgresql:17.2
clickhouse: clickhouse/clickhouse-server:25.3
lifecycle:
pauseUpgrades: false # stop version-driven capability-operator re-applies
retainOnUninstall: false # keep a shared operator's release when its last consumer is removed
maintenanceUntil: "2026-07-01T02:00:00Z" # pause capability-operator upgrades until this time, then auto-resume
runtime:
logLevel: info # debug|info|warn|error — changes the operator's verbosity LIVECome vengono applicati. clusterDomain, storageClass, commonLabels/Annotations e operandImages vengono applicati direttamente alle risorse emesse dall'operatore. I valori predefiniti a livello di pod — proxy, scheduling, securityContext, imagePullPolicy e le label/annotation comuni — vengono applicati a ogni oggetto renderizzato dai chart inclusi tramite un unico post-renderer Helm, così raggiungono in modo uniforme gli operatori di capacità e i chart dei workload senza configurazione per singolo chart.
scheduling raggiunge ogni pod della piattaforma, non solo quelli renderizzati dai chart. La maggior parte dei pod applicativi (il gateway LiteLLM, Langfuse e i datastore) viene creata dagli operatori upstream a partire dai CR emessi da questo operatore — il post-renderer Helm non li vede mai. Per questo scheduling (nodeSelector / tolerations / affinity / priorityClassName) viene inoltre tradotto nello schema di posizionamento proprio di ciascun CR emesso: il blocco spec.affinity di CloudNativePG, lo spec.podTemplate di ClickHouse/Keeper, lo spec di OT-Redis, i componenti web/worker di Langfuse, l'overlay dello StatefulSet MongoDB e il blocco spec.podScheduling di LiteLLM — più i Deployment costruiti direttamente dall'operatore (management plane, guardrail proxy) e i CronJob di backup. Ogni campo viene impostato solo quando il workload non ne definisce già uno proprio, quindi un valore esplicito prevale. Un valore predefinito già impostato dal workload ha sempre la precedenza.
Un'avvertenza sul posizionamento: priorityClassName. Due CRD upstream non modellano questo campo, quindi quel singolo valore predefinito viene saltato per loro mentre nodeSelector/tolerations/affinity vengono applicati normalmente — Langfuse e il gateway LiteLLM (il cui spec.podScheduling copre gli altri tre). Il Gateway genera un evento di avviso PlacementUnsupported che nomina priorityClassName quando è impostato, invece di scrivere un campo che l'API server eliminerebbe.
Il posizionamento di LiteLLM richiede litellm-operator 0.24.0+
spec.podScheduling è stato aggiunto upstream in litellm-operator 0.24.0 (incluso dalla release 0.21.0 dell'operatore). Si applica sia al Deployment del proxy sia al Job di migrazione del database, così una migrazione non può mai essere schedulata dove il proxy non può essere eseguito. Su un litellm-operator più vecchio, installato dall'utente e adottato dalla piattaforma, il campo viene eliminato e il gateway mantiene il posizionamento predefinito.
proxy non è il registry. Un registry privato (Artifactory, Harbor, …) si configura tramite spec.registry.host + auth — è il meccanismo air-gap per le immagini. spec.defaults.proxy inietta le variabili d'ambiente standard in uscita HTTP_PROXY/HTTPS_PROXY/NO_PROXY per i cluster con egress limitato in cui i workload raggiungono servizi esterni (ad es. un'API LLM esterna) attraverso un forward proxy aziendale. HTTP_PROXY e HTTPS_PROXY selezionano il proxy in base allo schema dell'URL di destinazione (di solito con lo stesso valore) — non sono due registry. Lascia proxy non impostato sui cluster completamente air-gapped privi di egress.
lifecycle.maintenanceUntil è una finestra di manutenzione: finché l'ora corrente è antecedente, gli upgrade/re-apply degli operatori di capacità guidati dalla versione sono sospesi (equivalente a pauseUpgrades), poi riprendono automaticamente una volta trascorsa la finestra (l'operatore riaccoda alla scadenza). Una release non sana viene comunque riapplicata, così nulla resta guasto.
runtime.logLevel modifica la verbosità dei log dell'operatore a caldo (senza riavvio) al reconcile successivo. L'immagine dell'operatore stesso resta un aspetto di installazione impostato tramite l'Helm chart dell'operatore (image.repository / imagePullSecrets), non tramite questo CR — l'operatore è già in esecuzione quando legge PlatformConfig. L'operatore non emette alcuna telemetria di utilizzo, quindi non c'è nulla da disattivare.
Namespace di installazione degli operatori
Di default l'operatore installa ciascun operatore di capacità incluso nel proprio namespace dedicato (cnpg-system, litellm-system, cert-manager, external-secrets, sealed-secrets, langfuse-system, redis-system, mongodb-system, clickhouse-system, sail-operator). spec.operators consente di cambiare dove vengono collocati — più comunemente per consolidare tutti gli operatori di capacità in un unico namespace.
spec:
operators:
namespace: navique-operators # install ALL capability operators here
namespaces: # optional per-operator overrides (win over `namespace`)
cloudnative-pg: data-systemRisoluzione (per operatore). Una voce per operatore in namespaces prevale → altrimenti lo spec.operators.namespace condiviso → altrimenti il namespace predefinito integrato dell'operatore. Lasciare spec.operators del tutto non impostato preserva il comportamento attuale: ogni operatore nel proprio namespace dedicato.
Chiavi di operatore valide per la mappa namespaces:
| Chiave | Namespace predefinito |
|---|---|
external-secrets | external-secrets |
sealed-secrets | sealed-secrets |
cloudnative-pg | cnpg-system |
cert-manager | cert-manager |
litellm-operator | litellm-system |
langfuse-operator | langfuse-system |
redis-operator | redis-system |
mongodb-kubernetes | mongodb-system |
clickhouse-operator | clickhouse-system |
sail-operator | sail-operator |
Il namespace scelto viene creato se non esiste alla prima installazione dell'operatore.
Nota —
spec.operatorsviene applicato in fase di installazione. Modificarlo dopo che un operatore di capacità è già installato non migra la release esistente nel nuovo namespace; per spostarlo l'operatore deve essere rimosso e ridistribuito.
Footprint del catalogo MCP incluso
spec.mcp regola la release del catalogo MCP incluso condivisa a livello di cluster (vedi MCPServer). Poiché un server del catalogo (ad es. la ricerca web) viene distribuito come un'unica release condivisa con conteggio dei riferimenti per l'intero cluster, il suo footprint si imposta qui anziché per singola ChatUI.
| Campo | Descrizione |
|---|---|
footprint | full (SearXNG + Presidio + Playwright; predefinito) oppure minimal (solo ricerca — PII/rendering JS disattivati) |
values | Overlay libero di valori Helm unito nella release condivisa del catalogo |
Come per spec.operators, una modifica del footprint non migra una release già distribuita.
Routing — Ingress o Gateway API
spec.routing seleziona come viene esposto l'ingress dei workload: Ingress classico (predefinito) oppure la Gateway API (HTTPRoute).
spec:
routing:
mode: Gateway # Ingress (default) | Gateway
gatewayClassName: istio # required for standalone; default "istio" under a mesh
gateway:
mode: managed # managed (operator owns it) | adopt | external
name: navique
namespace: navique-system
clusterIssuer: letsencrypt-prod # cert-manager gateway-shim for HTTPS listenersQuando viene usata la modalità Gateway (risolta per workload): prevale lo spec.ingress.api del workload (Gateway/Ingress), altrimenti routing.mode == Gateway, altrimenti una ServiceMesh attiva (Istio è un'implementazione della Gateway API, quindi abilitare la mesh cambia automaticamente il routing). Il predefinito è Ingress — nessun cambiamento di comportamento finché non si sceglie esplicitamente.
Cosa fa l'operatore in modalità Gateway: sopprime l'Ingress upstream del workload ed emette una HTTPRoute (hostnames=[il tuo host], backend = il Service del workload) collegata a un Gateway condiviso che gestisce nel namespace di sistema — un listener HTTP per tutti i workload, più un listener HTTPS per host (gateway-shim di cert-manager, mode: Terminate) quando un workload imposta ingress.tls ed è configurato un clusterIssuer. La console del management plane, che non ha un Ingress classico, ottiene così finalmente un vero routing.
Un workload può forzare l'Ingress classico anche sotto una mesh con la propria via di fuga:
# on a Gateway / Langfuse / ChatUI / ManagementPlane
spec:
ingress:
enabled: true
host: legacy.example.com
api: Ingress # override: keep classic Ingress for this workloadLa modalità Gateway richiede che siano installati i CRD della Gateway API (gateway.networking.k8s.io) — li installa la ServiceMesh (Sail/Istio), oppure un controller standalone (Envoy Gateway, NGINX Gateway Fabric); l'operatore ne subordina il funzionamento alla loro presenza e, se mancano, riporta uno stato di attesa. Le annotation di basic-auth dell'ingress non hanno un equivalente nella Gateway API e non vengono trasferite (usa una AuthorizationPolicy della mesh).
Adottare un Gateway esistente (non crearne un secondo)
Se il tuo cluster ha già un Gateway per la stessa gatewayClassName — ad esempio uno creato con Terraform o Helm verso cui un load balancer esterno (Azure Application Gateway, un ALB, …) instrada già il traffico — devi indicare all'operatore di usarlo tramite routing.gateway:
spec:
routing:
mode: Gateway
gatewayClassName: nginx
gateway:
mode: external # reference it; the operator never modifies it (use "adopt" to manage labels)
name: nginx-gateway
namespace: nginx-gatewaySe lasci routing.gateway non impostato, l'operatore ripiega sulla creazione di un proprio Gateway di sua proprietà (navique nel namespace di sistema). Quando esiste già un altro Gateway per la stessa classe, quel secondo Gateway avvia un LoadBalancer di data plane parallelo che entra in conflitto con quello esistente sul load balancer interno del cloud (ad es. entrambi reclamano la porta 80), per cui il nuovo LoadBalancer non ottiene mai un indirizzo e il tuo proxy esterno continua a instradare verso un Gateway che ora non ha route — un 502 su tutta la piattaforma. Per evitarlo, l'operatore si rifiuta di creare un duplicato: lascia il workload in Pending con uno stato/evento InfrastructureBlocked che nomina il Gateway esistente e chiede di impostare routing.gateway. Impostarlo (come sopra) risolve il blocco.
L'operatore segnala inoltre un Gateway gestito il cui LoadBalancer di data plane è bloccato senza indirizzo (ad es. un SyncLoadBalancerFailed del cloud): il workload riporta InfrastructureBlocked con il dettaglio sottostante invece di apparire sano nel cluster pur essendo irraggiungibile dall'esterno.
Stratificazione e precedenza
- In fase di installazione (Helm/flag): l'immagine dell'operatore stesso + il suo pull Secret.
- A runtime (questo CR): tutto ciò che l'operatore distribuisce a valle.
- Per componente: un
Gateway/Observabilitypuò comunque impostare il propriospec.image.repository; un valore esplicito viene sottoposto a mirroring dell'host, non sostituito.
La modifica del registry dopo la distribuzione della piattaforma ha effetto man mano che ciascun componente esegue il reconcile successivo. La rimozione della PlatformConfig interrompe la riscrittura (le immagini tornano ai rispettivi registry upstream al reconcile successivo).
Stato
| Campo | Significato |
|---|---|
status.active | La riscrittura con mirroring dell'host è attiva. |
status.registry | L'host del mirror attivo. |
status.pullSecret | Il pull Secret di origine risolto (namespace/name). |
status.propagatedNamespaces | Namespace in cui è stato copiato il pull Secret. |
Una PlatformConfig con un nome diverso da cluster viene rifiutata con una condition NotSingleton e ignorata.