--- theme: default # Par défaut Slidev : record: "dev" → pas d’UI caméra/enregistrement dans `slidev build`. true = inclus en prod. record: true title: Kubernetes Operators — De l'enfer au paradis info: | Mental model Kubernetes, boucles de contrôle, opérateurs, exemples et pièges. highlighter: shiki # Comme Reveal « center: true » : planche centrée (layout intégré Slidev). defaults: layout: center --- # Kubernetes Operators — De l'enfer… au paradis > 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 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 → mécanismes explicites (controllers, kubelet, containerd, etc.) --- ## De vos outils au noyau Linux ```mermaid flowchart LR B[kube-api] subgraph Clients["Clients"] A01[kubectl] A02[Helm] A03[Kustomize] end A01 --> B A02 --> B A03 --> B B --> C[(etcd)] B --> D[Controllers] D --> B D --> E[kubelet] E --> F[containerd] F --> G[Kernel Linux] ``` - **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** ```mermaid 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 vivent dans l'API ; le **kubelet** **synchronise** depuis l'API, traduit en **CRI** vers **containerd**. Le **status** remonte par le kubelet. --- ## Sous containerd : cgroups et namespaces ```mermaid 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** sur vos nœuds > À 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…) - **CSI** → stockage (ex. Ceph, Longhorn, Amazon…) - **CRI** → runtime (ex. containerd, CRI-O…) - 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 **La logique est dans la tête des gens, pas dans le système.** --- ## Drift, pas de reproductibilité, 3 h du matin - Drift de configuration - Actions non reproductibles - Dépendance aux humains - Incidents à 3 h du matin - Pas **reproductible** / pas **observable** cluster / pas **fiable** dans le temps - Impossible de **raisonner globalement** sur l'état --- ## Vous déclarez ; le système réconcilie ```yaml kind: Database spec: size: 3 ``` --- ## Spec, opérateur, cluster : ça tourne en boucle ```mermaid 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 **Secrets** et **ConfigMaps** (autres ns, miroirs) | Annotations sur la ressource source ou la copie | | **Reloader** (Stakater) | **Redémarre** les Pods si Secret / ConfigMap référencé change | Annotation sur **Deployment** / **StatefulSet** / etc. | - Reflector : `reflector.v1.k8s.emberstack.com/*` - Reloader : `reloader.stakater.com/auto: "true"` --- ## 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.* ```yaml # Namespace database — secret maître 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 : `reflector.v1.k8s.emberstack.com/reflects: "database/db-credentials"` --- ## Exemple Reloader : rollout quand le secret miroir change ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: shop-api namespace: app-shop annotations: reloader.stakater.com/auto: "true" spec: template: spec: containers: - name: api envFrom: - secretRef: name: db-credentials ``` - Rotation dans `database/db-credentials` → Reflector met à jour `app-shop/db-credentials` → Reloader déclenche un **rollout**. - Sans Reloader, les Pods gardent l'ancienne valeur en mémoire. --- ## Cert-manager, Postgres, Keycloak : la spec devient une entité | Opérateur | CRD (exemples) | Ce que ça matérialise | |-----------|----------------|----------------------| | **cert-manager** | `Certificate`, `Issuer`, `ClusterIssuer` | Secrets TLS, ACME / PKI | | **Postgres Operator** (Zalando) | `postgresql` (`acid.zalan.do`) | Cluster PG (Patroni, volumes, users…) | | **Keycloak Operator** | `Keycloak`, `KeycloakRealm`, `KeycloakClient`… | Instances, royaumes, clients | La spec décrit l'état métier ; le contrôleur crée / met à jour Deployments, Services, Secrets… --- ## Un certificat déclaratif, tout le cycle TLS en dessous ```yaml apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: api-example spec: dnsNames: - api.example.com ``` Suite dérivée automatiquement : `CertificateRequest`, challenge ACME, Secret TLS. --- ## Certificats : du Certbot au renouvellement automatique *Avant — sans opérateur* - Certbot manuel - Scripts cron - Cert expiré = prod down *Avec cert-manager* - Déclaratif - Auto-renouvellement - Observabilité native (`Certificate`, conditions, events) --- ## Postgres répliqué sans bricoler le failover à la main ```yaml apiVersion: acid.zalan.do/v1 kind: postgresql metadata: name: my-cluster spec: numberOfInstances: 3 ``` Derrière : réplication, failover, backup, users / rôles. --- ## Quelle techno pour développer votre opérateur ? | Piste | Outils typiques | À retenir | |-------|-----------------|-----------| | **Go** (référence) | **Kubebuilder**, `controller-runtime`, client-go | Même patterns upstream ; sous charge : goroutines, files, back-off — peu de RAM en plus. | | **Operator SDK** | CLI Red Hat · Go · Ansible / Helm | Go solide ; Ansible/Helm = wrapper — court si la logique métier épaissit. | | **Autres** | Python **Kopf**, Rust **kube-rs**, Java… | Selon l'équipe ; moins d'exemples core. | **Go + controller-runtime** pour un opérateur maison sérieux ; sinon celui que l'équipe **maintient** le mieux. *Charge ↑ : un runtime qui gonfle la RAM par événement mange votre quota.* --- ## 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** avec ce qui est **déclaré dans le cluster** (CRD, Secrets annotés…). - Montrer le **même schéma** : *observe → compare → corrige* sur **ton** domaine. - **Incident / compromission** : reload Keycloak → événements côté IdP ; l'opérateur **interroge** (ex. toutes les minutes), repère l'écart et **propage** la correction. --- ## Exemple — `KeycloakClientCredential` *CRD* `secrets.carbogame.com/v1alpha1` — `spec.source` (realm, client) → `spec.target` (Secret, rafraîchissement, mapping). ```yaml apiVersion: secrets.carbogame.com/v1alpha1 kind: KeycloakClientCredential metadata: name: gitea-creds namespace: keycloak spec: source: keycloakServer: prod-keycloak realm: operator clientId: gitea target: secretName: gitea-secret-creds namespace: - gitea refreshPeriod: 10m fields: clientId: key clientSecret: secret ``` *En chart Helm, `namespace` et `keycloakServer` sont souvent injectés via templates.* --- ## Développer son opérateur : réutiliser les autres - **Ne pas tout recoder** : s'appuyer sur des contrôleurs existants pour ce qu'ils font déjà bien. - Ex. **Secrets / ConfigMaps** vers d'autres namespaces : brancher **Reflector** plutôt que dupliquer la logique — une politique de réflexion, **un RBAC** à raisonner. - Votre code se concentre sur **le métier**. --- ## Ce qu'un opérateur bien choisi vous apporte - Automatiser du **répétitif** - **Encapsuler** de la complexité - **Stabiliser** un système … mais les opérateurs peuvent aussi devenir un enfer. --- ## Quand ne pas écrire d'opérateur - Logique **floue** ou en perpétuelle négociation métier - Processus **instables** - Dépendances **externes** imprévisibles - **One-shot** 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 invisibles) - **Couplage** externe fort - **Debug** impossible sans les mainteneurs - **État** non observable (pas de conditions / status utiles) > Un opérateur mal conçu, c'est un microservice dont plus personne ne tient les règles du jeu. --- ## 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 : - Tout mettre en opérateur - Logique métier **floue** - Effets **cachés** --- ## Kubernetes, plateforme de programmation - Kubernetes évite **l'enfer de l'infrastructure** bas niveau. - Les **bons** opérateurs évitent **l'enfer du manuel** — les **mauvais** 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**. --- ## Votre logique, exécutée par le cluster au lieu d'un humain - « OK, je comprends enfin le cadre Kubernetes. » - « Les opérateurs : puissants, à manier avec précision. » Un opérateur, c'est votre logique métier — **lisible**, **testable**, **bornée**. --- ## Kubernetes simplifie l'infra — les opérateurs, ça peut simplifier ou empirer Les **bons** **encapsulent** une complexité maîtrisée ; les **mauvais** recréent un enfer **plus sophistiqué**. --- ## Merci Merci pour votre attention. **Des questions ?** - Dépôt : [git.specificat.io/arnault/Talks](https://git.specificat.io/arnault/Talks) - Site : [specificat.io](https://specificat.io/) - Index des slides : portail talks (page d'accueil des présentations)