PlatformConfig
Portée : cluster · Singleton (nom cluster)
PlatformConfig configure l'opérateur lui-même — la façon dont il déploie et gère la plateforme. Pour l'instant, il configure le registre de conteneurs : diriger en un seul endroit toutes les images que l'opérateur déploie vers un registre personnalisé (par exemple un miroir en air-gap), avec des identifiants de pull.
apiVersion: core.navique.com/v1alpha1
kind: PlatformConfig
metadata:
name: cluster # singleton — seul « cluster » est pris en compte
spec:
registry:
# Hôte du miroir, éventuellement avec un préfixe de chemin.
host: registry.example.com/mirror
auth:
secretRef:
name: registry-pull-secret # un Secret kubernetes.io/dockerconfigjson
namespace: navique-system # par défaut, le namespace système de l'opérateurCe qui est couvert
Lorsque spec.registry.host est défini, l'opérateur applique un host-mirror à chaque image qu'il déploie : il remplace l'hôte du registre tout en conservant le chemin et le tag de l'image, de sorte que ghcr.io/cloudnative-pg/cloudnative-pg:1.29 devient registry.example.com/mirror/cloudnative-pg/cloudnative-pg:1.29. Cela s'applique à :
- Les opérateurs de capacité fournis — CloudNativePG, cert-manager, External Secrets, Sealed Secrets, les opérateurs ClickHouse et Redis, MongoDB (MCK), les opérateurs LiteLLM et Langfuse, Meilisearch, Sail/Istio. Les valeurs d'image de chaque chart sont réécrites vers le miroir.
- Les instances de datastore et de workload gérées par les opérateurs — Redis, LiteLLM (passerelle + job de migration), Langfuse (web + worker), LibreChat, Meilisearch, le plan de contrôle Istio (istiod / ztunnel / CNI).
- La console du plan de gestion — sa seule possibilité de redirection de registre (elle n'a pas de réglage distinct).
Le Secret de pull référencé par spec.registry.auth.secretRef est propagé : l'opérateur le copie dans chaque namespace où il déploie (les namespaces système des opérateurs de capacité et chaque namespace de workload) et le câble comme imagePullSecrets sur les charts, les CR d'instance émises et les pods gérés par l'opérateur.
Ce qui n'est PAS couvert
- L'image propre de l'opérateur. L'opérateur est déjà en cours d'exécution ; il ne peut donc pas décider via une CR d'où se télécharger lui-même. Définissez cela au moment de l'installation, via le chart Helm de l'opérateur —
image.repositoryetimagePullSecrets—, qui fournit aussi le registre d'amorçage pour une installation en air-gap. - Les images d'opérande CloudNativePG et ClickHouse (les images serveur PostgreSQL / ClickHouse que l'opérateur amont choisit lui-même) reçoivent le Secret de pull mais conservent la référence d'image par défaut de l'opérateur — il n'existe pas de tag stable à réépingler en toute sécurité depuis ici. Mettez-les en miroir au même chemin de dépôt ; le Secret de pull permet aux pods d'instance de s'authentifier auprès du miroir.
- Quelques images d'init
busyboxprofondément imbriquées dans les charts Meilisearch / LibreChat ne sont pas exposées comme valeurs et ne sont pas réécrites.
Amorçage à l'installation (recommandé)
Pour une installation GitOps ou en air-gap, amorcez la PlatformConfig avec le chart Helm de l'opérateur afin que la toute première installation d'opérateur de capacité soit déjà mise en miroir :
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-secretregistry.pullSecret.dockerConfigJson peut aussi être défini pour que le chart crée le Secret ; sinon, il doit déjà exister dans le namespace système.
Valeurs par défaut à l'échelle de l'opérateur
Au-delà du registre, spec.defaults et spec.lifecycle configurent la façon dont l'opérateur déploie et gère tout. La spécification propre d'un composant remplace toujours la valeur par défaut correspondante.
spec:
defaults:
clusterDomain: cluster.local # domaine DNS de cluster personnalisé (URLs de service calculées)
storageClass: fast-ssd # défaut pour les PVC des datastores gérés
commonLabels: { team: platform } # apposé sur chaque objet créé par l'opérateur
commonAnnotations: { owner: ai }
proxy: # injecté comme HTTP(S)_PROXY / NO_PROXY
httpProxy: http://proxy:3128
httpsProxy: http://proxy:3128
noProxy: .svc,.cluster.local,10.0.0.0/8
scheduling: # placement de pod par défaut
nodeSelector: { workload: platform }
tolerations: [{ key: platform, operator: Exists }]
priorityClassName: system-cluster-critical
securityContext: { runAsNonRoot: true, seccompProfile: { type: RuntimeDefault } }
imagePullPolicy: IfNotPresent
registry:
operandImages: # épingler les images d'opérande choisies par l'opérateur (mises en miroir)
postgres: ghcr.io/cloudnative-pg/postgresql:17.2
clickhouse: clickhouse/clickhouse-server:25.3
lifecycle:
pauseUpgrades: false # arrêter les ré-applications pilotées par version des opérateurs de capacité
retainOnUninstall: false # conserver la release d'un opérateur partagé quand son dernier consommateur est retiré
maintenanceUntil: "2026-07-01T02:00:00Z" # suspendre les mises à niveau des opérateurs jusqu'à cette date, puis reprendre automatiquement
runtime:
logLevel: info # debug|info|warn|error — change la verbosité À CHAUDComment elles s'appliquent. clusterDomain, storageClass, commonLabels/Annotations et operandImages sont appliquées directement aux ressources que l'opérateur émet. Les valeurs par défaut au niveau du pod — proxy, scheduling, securityContext, imagePullPolicy et les labels/ annotations communs — sont appliquées à chaque objet rendu par les charts fournis via un unique post-renderer Helm, afin d'atteindre uniformément les opérateurs de capacité et les charts de workload, sans configuration par chart.
scheduling atteint chaque pod de la plateforme, pas seulement ceux rendus par les charts. La plupart des pods applicatifs (la passerelle LiteLLM, Langfuse et les datastores) sont créés par des opérateurs amont à partir des CR que cet opérateur émet — le post-renderer Helm ne les voit jamais. C'est pourquoi scheduling (nodeSelector / tolerations / affinity / priorityClassName) est en plus traduit dans le schéma de placement propre à chaque CR émis : le bloc spec.affinity de CloudNativePG, le spec.podTemplate de ClickHouse/Keeper, le spec d'OT-Redis, les composants web/worker de Langfuse, l'overlay StatefulSet de MongoDB et le bloc spec.podScheduling de LiteLLM — ainsi que les Deployments construits par l'opérateur (management plane, proxy guardrail) et les CronJobs de sauvegarde. Chaque champ n'est défini que si le workload ne le définit pas déjà lui-même : une valeur explicite l'emporte donc.
Une réserve sur le placement : priorityClassName. Deux CRD amont ne modélisent pas ce champ, cette seule valeur par défaut est donc ignorée pour elles tandis que leurs nodeSelector/tolerations/affinity sont appliqués normalement — Langfuse, et la passerelle LiteLLM (dont spec.podScheduling couvre les trois autres). La passerelle émet un événement d'avertissement PlacementUnsupported nommant priorityClassName, plutôt que d'écrire un champ que le serveur d'API supprimerait.
Le placement LiteLLM nécessite litellm-operator 0.24.0+
spec.podScheduling a été ajouté en amont dans litellm-operator 0.24.0 (embarqué depuis la version 0.21.0 de l'opérateur). Il s'applique au Deployment du proxy et au Job de migration de base de données, si bien qu'une migration ne peut jamais être planifiée là où le proxy n'a pas le droit de s'exécuter. Avec un litellm-operator plus ancien, installé par l'utilisateur et adopté par la plateforme, le champ est supprimé et la passerelle conserve le placement par défaut.
proxy n'est pas le registre. Un registre privé (Artifactory, Harbor, …) se configure via spec.registry.host + auth — c'est le mécanisme d'images air-gap. spec.defaults.proxy injecte les variables standard HTTP_PROXY/HTTPS_PROXY/NO_PROXY pour les clusters à egress restreint où les workloads atteignent des services externes (p. ex. une API LLM externe) via un proxy d'entreprise. HTTP_PROXY vs HTTPS_PROXY choisissent le proxy selon le schéma de l'URL cible (généralement la même valeur de proxy) — ce ne sont pas deux registres. Laissez proxy non défini sur les clusters entièrement air-gap sans egress.
lifecycle.maintenanceUntil est une fenêtre de maintenance : tant que l'heure actuelle la précède, les mises à niveau/ré-applications pilotées par version des opérateurs de capacité sont suspendues (équivalent à pauseUpgrades), puis reprennent automatiquement à l'échéance (l'opérateur se replanifie à la date limite). Une release défaillante est toujours ré-appliquée pour que rien ne reste cassé.
runtime.logLevel change la verbosité des logs de l'opérateur à chaud (sans redémarrage) au prochain rapprochement. L'image propre de l'opérateur reste un réglage d'installation via le chart Helm (image.repository / imagePullSecrets), pas via cette CR. L'opérateur n'émet aucune télémétrie d'usage : il n'y a donc rien à désactiver.
Namespaces d'installation des opérateurs
Par défaut, l'opérateur installe chaque opérateur de capacité fourni dans son propre namespace dédié (cnpg-system, litellm-system, cert-manager, external-secrets, sealed-secrets, langfuse-system, redis-system, mongodb-system, clickhouse-system, sail-operator). spec.operators permet de choisir où ils sont déployés — le plus souvent pour regrouper tous les opérateurs de capacité dans un seul namespace.
spec:
operators:
namespace: navique-operators # installer TOUS les opérateurs de capacité ici
namespaces: # surcharges optionnelles par opérateur (priment sur `namespace`)
cloudnative-pg: data-systemRésolution (par opérateur). Une entrée par opérateur dans namespaces l'emporte → sinon le spec.operators.namespace partagé → sinon le namespace par défaut intégré de l'opérateur. Laisser spec.operators entièrement non défini conserve le comportement actuel : chaque opérateur dans son propre namespace dédié.
Clés d'opérateur valides pour la map namespaces :
| Clé | Namespace par défaut |
|---|---|
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 |
Le namespace choisi est créé s'il n'existe pas lors de la première installation de l'opérateur.
Note —
spec.operatorsest appliqué au moment de l'installation. Le modifier après l'installation d'un opérateur de capacité ne migre pas la release existante vers le nouveau namespace ; l'opérateur doit être démantelé puis redéployé pour le déplacer.
Empreinte du catalogue MCP intégré
spec.mcp règle la release partagée du catalogue MCP intégré au niveau du cluster (voir MCPServer). Comme un serveur de catalogue (p. ex. recherche web) est déployé en une seule release partagée et comptée par référence pour tout le cluster, son empreinte se règle ici, et non par ChatUI.
| Champ | Description |
|---|---|
footprint | full (SearXNG + Presidio + Playwright ; par défaut) ou minimal (recherche seule — PII/rendu JS désactivés) |
values | Surcouche de valeurs Helm libre, fusionnée dans la release partagée du catalogue |
Comme pour spec.operators, un changement d'empreinte ne migre pas une release déjà déployée.
Routage — Ingress ou Gateway API
spec.routing choisit la façon d'exposer l'ingress des workloads : Ingress classique (par défaut) ou la Gateway API (HTTPRoutes).
spec:
routing:
mode: Gateway # Ingress (par défaut) | Gateway
gatewayClassName: istio # requis en autonome ; défaut « istio » avec un mesh
gateway:
mode: managed # managed (l'opérateur le possède) | adopt | external
name: navique
namespace: navique-system
clusterIssuer: letsencrypt-prod # gateway-shim cert-manager pour les listeners HTTPSQuand le mode Gateway s'applique (résolu par workload) : le spec.ingress.api du workload l'emporte (Gateway/Ingress), sinon routing.mode == Gateway, sinon un ServiceMesh actif (Istio est une implémentation de la Gateway API, donc activer le mesh bascule le routage automatiquement). Le défaut est Ingress — aucun changement de comportement tant que vous n'optez pas.
Ce que fait l'opérateur en mode Gateway : il supprime l'Ingress amont du workload et émet une HTTPRoute (hostnames=[votre hôte], backend = le Service du workload) rattachée à un Gateway partagé qu'il gère dans le namespace système — un listener HTTP pour tous les workloads, plus un listener HTTPS par hôte (gateway-shim cert-manager, mode: Terminate) lorsqu'un workload définit ingress.tls et qu'un clusterIssuer est configuré. La console du plan de gestion, qui n'a pas d'Ingress classique, obtient enfin un vrai routage.
Un workload force l'Ingress classique même sous un mesh via sa propre échappatoire :
# sur un Gateway / Observability / ChatUI / ManagementPlane
spec:
ingress:
enabled: true
host: legacy.example.com
api: Ingress # override : conserver l'Ingress classique pour ce workloadLe mode Gateway nécessite les CRD Gateway API (gateway.networking.k8s.io) — le ServiceMesh (Sail/Istio) les installe, ou un contrôleur autonome (Envoy Gateway, NGINX Gateway Fabric) ; l'opérateur s'en assure et signale un statut d'attente si elles manquent. Les annotations d'auth basique d'Ingress n'ont pas d'équivalent Gateway API et ne sont pas reportées (utilisez une AuthorizationPolicy de mesh).
Adopter un Gateway existant (ne pas en créer un second)
Si votre cluster possède déjà un Gateway pour le même gatewayClassName — par exemple un Gateway provisionné par Terraform ou Helm vers lequel un répartiteur de charge externe (Azure Application Gateway, un ALB, …) route déjà —, vous devez indiquer à l'opérateur de l'utiliser via routing.gateway :
spec:
routing:
mode: Gateway
gatewayClassName: nginx
gateway:
mode: external # le référencer ; l'opérateur ne le modifie jamais (« adopt » pour gérer les labels)
name: nginx-gateway
namespace: nginx-gatewaySi vous laissez routing.gateway non défini, l'opérateur crée et possède son propre Gateway (navique dans le namespace système). Lorsqu'un autre Gateway de la même classe existe déjà, ce second Gateway démarre un LoadBalancer de plan de données parallèle qui entre en conflit avec l'existant sur le répartiteur de charge interne du cloud (par ex. les deux réclament le port 80) ; le nouveau LoadBalancer n'obtient donc jamais d'adresse, et votre proxy externe continue de router vers un Gateway désormais sans routes — un 502 à l'échelle de la plateforme. Pour l'éviter, l'opérateur refuse de créer un doublon : il laisse la charge de travail en Pending avec un statut/événement InfrastructureBlocked qui nomme le Gateway existant et vous demande de définir routing.gateway. Le définir (comme ci-dessus) lève le blocage.
L'opérateur signale également un Gateway géré dont le LoadBalancer de plan de données n'obtient pas d'adresse (par ex. un SyncLoadBalancerFailed du cloud) : la charge de travail rapporte InfrastructureBlocked avec le détail sous-jacent, au lieu de paraître saine dans le cluster tout en étant injoignable de l'extérieur.
Superposition et priorité
- Installation (Helm/flags) : l'image propre de l'opérateur + son Secret de pull.
- Exécution (cette CR) : tout ce que l'opérateur déploie en aval.
- Par composant : un
Gateway/Observabilitypeut toujours définir sa proprespec.image.repository; une valeur explicite est mise en miroir au niveau de l'hôte, pas remplacée.
Changer le registre après le déploiement de la plateforme prend effet au prochain rapprochement de chaque composant. Supprimer la PlatformConfig arrête la réécriture (les images reviennent à leurs registres amont au prochain rapprochement).
Statut
| Champ | Signification |
|---|---|
status.active | La réécriture host-mirror est active. |
status.registry | L'hôte du miroir actif. |
status.pullSecret | Le Secret de pull source résolu (namespace/name). |
status.propagatedNamespaces | Namespaces dans lesquels le Secret a été copié. |
Une PlatformConfig portant un nom autre que cluster est rejetée avec une condition NotSingleton et ignorée.