Kubernetes n’est pas compliqué.
Mais mal compris… c’est un enfer.
Kubernetes : délire d’Ops ?
On nous l’a surtout vendu comme un truc d’infra. Moi aussi, je suis passé par ce réflexe.
« Trop complexe »
Réservé aux spécialistes plateforme
Magie noire / pas « mon » métier
Kubernetes c'est un délire de dev
Kubernetes = API + état + boucle
API REST
Base d’état (etcd)
Boucle de réconciliation → events
Pas de mystère → des mécanismes explicites (controllers, kubelet, containerd, etc.)
De vos outils au noyau Linux
flowchart LR
B[kube-api]
subgraph Clients
A01[kubectl]
A02@{ img: "img/helmsh-icon.svg", pos: "b", h: 44, constraint: "on" }
A03@{ img: "img/logo-kustomize.B5HO6GxI_1tyDev.webp", pos: "b", h: 36, constraint: "on" }
end
A01 --> B
A02 --> B
A03 --> B
D --> B
B --> C[etcd]
B --> D[Controllers]
D --> E[kubelet]
E --> F[containerd]
F --> G[Kernel Linux]
classDef logoBare fill:transparent,stroke:transparent,stroke-width:0px;
class A02,A03 logoBare;
kube-api — point d’entrée
etcd — source de vérité
kubelet — agent sur chaque nœud
container runtime (ex. containerd) — exécution des conteneurs
Kubernetes ne fait pas tourner vos apps : la boucle de contrôle
Il orchestre · il délègue · il observe
flowchart LR
DS[Spec · désir] --> AP[kube-apiserver]
AP <-->|objets cluster| ET[(etcd)]
AP --> EV[Watch · événements]
EV --> CTRL[Controllers]
CTRL -->|réconciliation · requêtes API| AP
subgraph NODE[Nœud]
K[kubelet] -->|CRI| CD[containerd]
end
AP -->|spec Pods · synchro| K
K -->|status| AP
↻ Les changements d’objets (Pods, etc.) vivent dans l’API ; le kubelet les
synchronise depuis l’API, puis traduit en appels CRI vers
containerd. Le status remonte par le même kubelet vers l’API.
Sous containerd : cgroups et namespaces
flowchart TD
F[containerd] --> K[Linux Kernel]
cgroups
namespaces
Kubernetes vous évite de vivre dans le noyau
Manipuler les cgroups à la main
Gérer isolation, CPU, mémoire au niveau des noyaux de vos differents noeuds
À côté de ça, un cluster qui râle, c'est presque du repos.
CNI, CSI, CRI : de l’infra branchable
CNI → réseau (ex : Istio, Cilium ,Calico, Flannel, etc.)
CSI → stockage (ex : Ceph, Longhorn , Amazon,etc.)
CRI → runtime (ex : containerd, CRI-O,etc.)
Ici : brancher l’infra — pas encoder la logique métier (≠ opérateur)
Brancher l’infra ou coder le métier ?
Type
Rôle
CNI / CSI / CRI
Infrastructure
Opérateur
Logique métier
Qui écrit la logique du cluster ?
La logique vit dans les controllers
Ils ferment la boucle : observation, décision, action.
Les opérateurs : des controllers spécialisés
Sans opérateur : scripts, crons, humains dans la boucle
Scripts bash
Cron jobs
Interventions humaines
État incohérent
Automatisation éclatée : la logique reste dans les têtes
Runbooks Confluence
Scripts bash « magiques »
Cron jobs oubliés
Interventions humaines
Drift, pas de reproductibilité, 3 h du matin
Drift de configuration
Actions non reproductibles
Dépendance aux humains
Incidents à 3 h du matin
Vous déclarez ; le système réconcilie
kind: Database
spec:
size: 3
Spec, opérateur, cluster : ça tourne en boucle
flowchart LR
A[Spec] --> B[Operator]
B --> C[Cluster]
C --> B
Dès que le réel diverge, la réconciliation repart.
Reflector & Reloader : la puissance des annotations
Outil
Idée
Déclencheur typique
Reflector (EmberStack)
Copie / réplique des Secrets et ConfigMaps (autres namespaces,
miroirs)
Annotations sur la ressource source ou la copie
Reloader (Stakater)
Redémarre les Pods quand un Secret / ConfigMap référencé change
Annotation sur le Deployment / StatefulSet / etc.
Reflector : annotations du type reflector.v1.k8s.emberstack.com/* pour autoriser la réflexion
et cibler où copier.
Reloader : reloader.stakater.com/auto: "true" (ou annotations plus fines par config) pour
lier redéploiement ↔ mise à jour de config.
Exemple Reflector : secret DB → namespace applicatif
Le secret « source de vérité » vit avec la base ; Reflector recrée un miroir là où l’app tourne.
# Namespace database — secret maître (ex. mot de passe géré par l’opérateur / la DBA)
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: database
annotations:
reflector.v1.k8s.emberstack.com/reflection-allowed: "true"
reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true"
reflector.v1.k8s.emberstack.com/reflection-auto-namespaces: "app-shop"
stringData:
DATABASE_URL: "postgres://…"
Reflector maintient un Secret du même nom dans app-shop, à jour quand la
source change.
Alternative : miroir manuel avec reflector.v1.k8s.emberstack.com/reflects: "database/db-credentials".
Exemple Reloader : rollout quand le secret miroir change
Le Deployment monte le secret copié ; Reloader redémarre les Pods si le Secret (ou ConfigMap) référencé
est mis à jour.
Même patterns que l’upstream (reconcile, owner refs). Sous pic
d’événements : goroutines + files + back-off → peu de RAM en plus —
le Pod opérateur ne part pas en limite quand le cluster s’emballe.
Operator SDK
CLI Red Hat · Go (souvent comme Kubebuilder) · Ansible / Helm
Go = solide ; Ansible / Helm = wrapper playbooks / charts — court si la logique métier épaissit.
Si équipe ou libs métier ; moins d’exemples tout faits côté core k8s.
Go + controller-runtime pour un opérateur maison sérieux ; sinon : celui que l’équipe
livre et maintient le mieux.
Charge ↑ : un runtime qui gonfle la RAM par événement mange votre quota ; Go + controller-runtime,
non.
Quand un opérateur est légitime
Système complexe
Règles claires et stables
État critique pour l’activité
Opérations répétitives
Keycloak Credential Manager — le même schéma, chez moi
Objectif : réconcilier des credentials Keycloak (clients, secrets,
rotation…) avec ce qui est déclaré dans le cluster (CRD, Secrets annotés, ou les deux —
selon ton design).
Intérêt conf : montrer le même schéma que les opérateurs « connus » : observe →
compare → corrige sur ton domaine métier.
Incident ou compromission de secrets clients : un reload Keycloak
émet des événements côté IdP ; l’opérateur les interroge régulièrement
(ex. toutes les minutes), repère l’écart et propage la correction vers
l’état attendu dans le cluster.
Extrait type ; en chart Helm, namespace et keycloakServer sont injectés via
include "common.namespace" …, {{ .Values.global.env }}-keycloak, etc.
Développer son opérateur : réutiliser les autres
Ne pas tout recoder : un opérateur maison peut s’appuyer sur des opérateurs / contrôleurs
existants pour ce qu’ils font déjà bien.
Ex. Secrets / ConfigMaps vers d’autres namespaces : plutôt que dupliquer la logique et une
deuxième config de ce qui est autorisé, brancher Reflector — une politique
de réflexion, un seul RBAC à raisonner (source vs cibles), moins de surface d’erreur.
Votre code se concentre sur le métier ; la diffusion transverse reste le problème de
l’outil prévu pour ça.
Ce qu’un opérateur bien choisi vous apporte
Automatiser du répétitif
Encapsuler de la complexité
Stabiliser un système
Quand ne pas écrire d’opérateur
Logique floue ou en perpétuelle négociation métier
Processus instables (la règle change toutes les semaines)
Dépendances externes imprévisibles
One-shot : automatisation ponctuelle sans cycle de vie
Cinq signaux qu’un opérateur part en vrille
Spec incompréhensible sans lire le code
Effets implicites (créations ailleurs sans que ce soit visible)
Couplage externe fort (APIs fragiles, ordre d’appels magique)
Debug impossible sans les mainteneurs à côté de toi
État non observable (pas de conditions / status utiles)
Débugger un opérateur : describe, events, status, logs
kubectl describe (resource + events)
Events du namespace
Status / conditions sur la CRD
Logs du contrôleur
Un opérateur, c’est un produit logiciel — pas un script
Code et revues
Tests (unitaires, intégration, e2e si possible)
Maintenance dans la durée
Compatibilité avec les versions Kubernetes
Migrations de CRD / spec
Quand l’opérateur devient l’enfer
Logique implicite
Effets de bord
Debugging difficile
Couplage fort
En pratique, ça donne souvent :
Tout mettre en opérateur
Logique métier floue
Effets cachés
Un opérateur mal conçu, c’est un microservice dont plus personne ne tient les règles du jeu.
Kubernetes, plateforme de programmation
Kubernetes vous évite l’enfer de l’infrastructure bas niveau.
Les bons opérateurs vous évitent l’enfer du manuel — les mauvais vous y renvoient
autrement.
Avant d’écrire un opérateur : la checklist
Répétitif
Règles claires
Déclaratif possible
Idempotent
Observable
Si une case importante manque, reculez : pas d’opérateur (ou pas tout de suite).
Votre logique, exécutée par le cluster au lieu d’un humain
Kubernetes simplifie l’infra — les opérateurs, ça peut simplifier ou empirer
Merci
Merci pour votre attention.
Des questions ?
Dépôt des sources et page d’accueil des slides — à scanner pour repartir avec les
liens.