- Enabled recording feature in slides for Kubernetes Operators and Spring AI presentations. - Updated slides.md files to include 'record: true' for both presentations, allowing for camera and recording options during development.
580 lines
14 KiB
Markdown
580 lines
14 KiB
Markdown
---
|
||
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.
|
||
|
||
<!--
|
||
On parle surtout de pourquoi les opérateurs changent la manière de travailler — puissants mais dangereux si le mental model est faux.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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
|
||
|
||
<!--
|
||
Souvent présenté comme réservé aux spécialistes — en réalité proche de ce qu'on fait déjà en dev.
|
||
-->
|
||
|
||
---
|
||
|
||
## Kubernetes c'est un délire de dev
|
||
|
||
> Kubernetes c'est un délire de dev
|
||
|
||
<!--
|
||
Transition : contraste avec la slide précédente.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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.)
|
||
|
||
<!--
|
||
Pas de magie — des patterns qu'on retrouve ailleurs en dev.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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
|
||
|
||
<!--
|
||
Helm, kubectl, Flux… appellent l'API. L'API parle à etcd, les controllers poussent vers les kubelets, le runtime lance les 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.
|
||
|
||
<!--
|
||
Aligné sur la doc Kubernetes : observer, rapprocher le courant du spec ; informers / watches → files d'événements. etcd derrière l'API.
|
||
-->
|
||
|
||
---
|
||
|
||
## Sous containerd : cgroups et namespaces
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
F[containerd] --> K[Linux Kernel]
|
||
```
|
||
|
||
- **cgroups**
|
||
- **namespaces**
|
||
|
||
<!--
|
||
Derrière containerd, le noyau — vous ne voulez pas manipuler les fichiers à la main au quotidien.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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 |
|
||
|
||
<!--
|
||
D'un côté briques branchables ; de l'autre du code qui encode le comportement dans le cluster. Icônes : icônes K8s officielles, CNCF Landscape, Simple Icons.
|
||
-->
|
||
|
||
---
|
||
|
||
## Qui écrit la logique du cluster ?
|
||
|
||
<!--
|
||
Qui décide quand l'état réel diverge ? (pause)
|
||
-->
|
||
|
||
---
|
||
|
||
## La logique vit dans les controllers
|
||
|
||
*Ils ferment la boucle : observation, décision, action.*
|
||
|
||
<!--
|
||
L'intelligence du cluster est dans les controllers.
|
||
-->
|
||
|
||
---
|
||
|
||
## Les opérateurs : des controllers spécialisés
|
||
|
||
<!--
|
||
Même mécanisme, focalisé sur un domaine métier ou un logiciel (base, queue…).
|
||
-->
|
||
|
||
---
|
||
|
||
## 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
|
||
```
|
||
|
||
<!--
|
||
État voulu → le contrôleur fait les transitions répétitives.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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.*
|
||
|
||
<!--
|
||
Exemples : annotations sur ressources natives, puis vrais CRD ; projet maison pour ancrer le discours.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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"`
|
||
|
||
<!--
|
||
Pas toujours un gros opérateur CRD — parfois contrôleur sur objets standards + annotations. Liens : kubernetes-reflector, Reloader sur GitHub.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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…
|
||
|
||
<!--
|
||
Trois domaines, même mécanisme CRD + réconciliation. Liens cert-manager.io, Zalando postgres-operator, Keycloak operator.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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)
|
||
|
||
<!--
|
||
Complexité énorme encapsulée — pertinent quand le domaine est clair, répétitif et critique.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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**
|
||
|
||
<!--
|
||
Si tu ne peux pas formaliser « si X alors Y » proprement — très mauvaise idée en opérateur.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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.
|
||
|
||
<!--
|
||
Pas pour vendre un produit — prouver que la logique est la même : API K8s, état désiré, code à la place du runbook.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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.
|
||
|
||
<!--
|
||
Fil : pas magique → API + boucle → controllers / opérateurs → avant/après → exemples → bon usage / pièges → conclusion.
|
||
-->
|
||
|
||
---
|
||
|
||
## 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 ?**
|
||
|
||
<ClosingQr />
|
||
|
||
- 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)
|
||
|
||
<!--
|
||
Laisser le temps pour les questions ; rappeler l'URL du dépôt à l'oral.
|
||
-->
|