Skip to content

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.

yaml
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érateur

Ce 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.repository et imagePullSecrets —, 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 busybox profondé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 :

bash
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

registry.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.

yaml
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é À CHAUD

Comment 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.

yaml
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-system

Ré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-secretsexternal-secrets
sealed-secretssealed-secrets
cloudnative-pgcnpg-system
cert-managercert-manager
litellm-operatorlitellm-system
langfuse-operatorlangfuse-system
redis-operatorredis-system
mongodb-kubernetesmongodb-system
clickhouse-operatorclickhouse-system
sail-operatorsail-operator

Le namespace choisi est créé s'il n'existe pas lors de la première installation de l'opérateur.

Note — spec.operators est 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.

ChampDescription
footprintfull (SearXNG + Presidio + Playwright ; par défaut) ou minimal (recherche seule — PII/rendu JS désactivés)
valuesSurcouche 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).

yaml
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 HTTPS

Quand 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 :

yaml
# sur un Gateway / Observability / ChatUI / ManagementPlane
spec:
  ingress:
    enabled: true
    host: legacy.example.com
    api: Ingress        # override : conserver l'Ingress classique pour ce workload

Le 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 :

yaml
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-gateway

Si 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é ​

  1. Installation (Helm/flags) : l'image propre de l'opérateur + son Secret de pull.
  2. Exécution (cette CR) : tout ce que l'opérateur déploie en aval.
  3. Par composant : un Gateway/Observability peut toujours définir sa propre spec.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 ​

ChampSignification
status.activeLa réécriture host-mirror est active.
status.registryL'hôte du miroir actif.
status.pullSecretLe Secret de pull source résolu (namespace/name).
status.propagatedNamespacesNamespaces 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.

Cœur open source sous AGPL-3.0. Les composants Enterprise sont propriétaires et soumis à licence.