From c468fc8061eb2bd8fae10f104be5d2fe15b88d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=20Pr=C3=A9vost-Corvellec=20Arnault?= Date: Wed, 8 Apr 2026 19:21:21 +0200 Subject: [PATCH] Add initial presentation files and .gitignore - Created a new .gitignore file to exclude .DS_Store files. - Added index.html for a presentation on Kubernetes Operators, including structured content and notes. - Introduced custom CSS for styling the presentation, aligning with the design specifications of specificat.io. --- .gitignore | 1 + reveal/css/custom.css | 321 +++++++++++++++++ reveal/index.html | 804 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1126 insertions(+) create mode 100644 .gitignore create mode 100644 reveal/css/custom.css create mode 100644 reveal/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/reveal/css/custom.css b/reveal/css/custom.css new file mode 100644 index 0000000..6191df1 --- /dev/null +++ b/reveal/css/custom.css @@ -0,0 +1,321 @@ +/* Thème aligné sur https://specificat.io/ (variables officielles du site) */ +:root { + --bg-dark: #050709; + --bg-medium: #0d1115; + --bg-light: #1b1f22; + --bg-gradient-start: #10151a; + --text-primary: #f5f5f5; + --text-muted: rgba(245, 245, 245, 0.9); + --text-subtle: rgba(245, 245, 245, 0.75); + --accent-cyan: #2bc9c6; + --accent-cyan-light: #e9fefd; + --accent-cyan-bg: rgba(43, 201, 198, 0.08); + --accent-cyan-border: rgba(43, 201, 198, 0.55); + --accent-copper: #d19c5a; + --border-subtle: rgba(255, 255, 255, 0.12); + --border-very-subtle: rgba(255, 255, 255, 0.07); + --panel-glow: 0 0 0 1px rgba(43, 201, 198, 0.12); +} + +/* Fond slide = même logique que le body du site (dégradé radial + profondeur) */ +.reveal .slide-background-content { + background-color: var(--bg-dark); + background-image: radial-gradient( + circle at 10% 0%, + var(--bg-gradient-start) 0%, + var(--bg-dark) 52%, + var(--bg-light) 100% + ), + radial-gradient( + ellipse 100% 80% at 50% -20%, + rgba(43, 201, 198, 0.12), + transparent 55% + ); +} + +.reveal .slide-background-content::after { + content: ""; + position: absolute; + inset: 0; + pointer-events: none; + background: radial-gradient( + ellipse 95% 75% at 50% 50%, + transparent 35%, + rgba(5, 7, 9, 0.35) 100% + ); +} + +/* Canvas Reveal : typo et couleur de texte comme le site */ +.reveal { + font-size: 32px; + font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif; + color: var(--text-primary); + position: relative; +} + +/* Réserve le bas du cadre pour le footer (évite de marcher sur le dernier texte / la progress bar) */ +.reveal .slides { + box-sizing: border-box; + padding-bottom: 2.25rem !important; +} + +/* Pied de page deck — sous la barre de progression Reveal (souvent z-index 10) */ +.deck-footer { + position: absolute; + left: 0; + right: 0; + bottom: 0; + z-index: 8; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 0.35em 0.5em; + padding: 0.35rem 2.5rem 0.55rem; + font-size: clamp(10px, 1.5vw, 12px); + line-height: 1.3; + color: var(--text-subtle); + pointer-events: none; + border-top: 1px solid var(--border-very-subtle); + background: linear-gradient(to top, rgba(5, 7, 9, 0.65), transparent); +} + +.deck-footer a { + pointer-events: auto; + color: var(--accent-cyan); + text-decoration: none; + font-weight: 500; +} + +.deck-footer a:hover, +.deck-footer a:focus-visible { + text-decoration: underline; + text-underline-offset: 2px; +} + +.deck-footer-sep { + opacity: 0.65; +} + +.deck-footer-rights { + opacity: 0.85; + letter-spacing: 0.02em; +} + +.reveal.overview .deck-footer { + display: none; +} + +.reveal .slides section { + color: var(--text-primary); +} + +.reveal h1 { + font-size: 1.6em; + text-transform: none; + font-weight: 700; + color: var(--text-primary); +} + +.reveal h2 { + font-size: 1.15em; + margin-top: 0.6em; + color: var(--accent-cyan); + text-transform: none; + font-weight: 600; +} + +.reveal h3 { + font-size: 0.95em; + color: var(--text-subtle); + text-transform: none; +} + +.reveal a { + color: var(--accent-cyan); +} + +.reveal ul, +.reveal ol { + display: block; + text-align: left; + color: var(--text-muted); +} + +.reveal table { + margin: 0.5em auto; + font-size: 0.75em; + border-color: var(--border-subtle); +} + +.reveal table th, +.reveal table td { + padding: 0.35em 0.6em; +} + +.reveal pre { + width: 100%; + box-shadow: var(--panel-glow), 0 1px 0 0 rgba(255, 255, 255, 0.03) inset; + border: 1px solid var(--border-subtle); + border-radius: 12px; + font-size: 0.45em; + max-height: 480px; + overflow: auto; +} + +.reveal pre code { + max-height: none; +} + +/* Citations : bordure cyan + fond comme les panneaux du site */ +.reveal blockquote { + width: 90%; + margin: 0.5em auto; + font-family: Georgia, "Times New Roman", serif; + font-style: italic; + color: var(--text-muted); + background: var(--accent-cyan-bg); + border: 1px solid var(--border-subtle); + border-left: 3px solid var(--accent-cyan-border); + border-radius: 12px; + box-shadow: var(--panel-glow); + padding: 0.45em 0.85em; +} + +.reveal aside.notes { + display: none !important; +} + +/* Bloc diagramme : même esprit « panel » que specificat.io (bordure discrète, fond légèrement relevé) */ +.reveal .mermaid { + margin: 0.5em auto; + padding: 0.55em 0.85em; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + max-width: 100%; + box-sizing: border-box; + border-radius: 12px; + border: 1px solid var(--border-subtle); + background: rgba(13, 17, 21, 0.55); + box-shadow: var(--panel-glow), inset 0 1px 0 0 rgba(255, 255, 255, 0.03); +} + +/* Évite les libellés écrasés quand le conteneur est encore étroit au 1er rendu */ +.reveal .mermaid { + min-width: min(100%, 240px); +} + +/* + * Mermaid met souvent width="100%" sur le SVG → il prend toute la largeur du slide, puis + * height: auto recalcule une hauteur énorme (viewBox étroit mais très haut). D’où des SVG à ~1000px de haut. + * On borne par la hauteur et on laisse width: auto recalculer la largeur au ratio du viewBox. + */ +.reveal .mermaid svg { + width: auto !important; + max-width: 100% !important; + max-height: min(52vh, 520px) !important; + height: auto !important; + display: block; + margin-left: auto; + margin-right: auto; +} + +/* Libellés Mermaid (SVG) : couleur explicite — le thème base ne peint pas toujours les */ +.reveal .mermaid svg text { + fill: var(--text-primary) !important; +} + +/* Barre de progression et numéros de slide : accent cyan */ +.reveal .progress span { + background: var(--accent-cyan); +} + +.reveal .slide-number { + color: var(--text-subtle); + font-size: 0.65em; +} + +/* + * Slide « boucle » (n° 6) : graphe cyclique Mermaid = très haut ; graphe linéaire + libellés courts. + * Pas de scale() : ça ne réduit pas la hauteur de layout (Reveal mesure scrollHeight). + */ +.reveal .slides section.slide-boucle { + padding-top: 0.5em !important; + padding-bottom: 0.5em !important; +} + +.reveal section.slide-boucle .slide-boucle-inner { + width: 100%; + max-width: 960px; + margin: 0 auto; + min-width: 0; + box-sizing: border-box; +} + +.reveal section.slide-boucle .slide-boucle-title { + font-size: clamp(1rem, 4.2vw, 1.35rem); + margin: 0 0 0.35em; + line-height: 1.15; +} + +/* Schéma « chaîne » (remplace Mermaid sur cette slide uniquement) */ +.reveal section.slide-boucle .boucle-flow { + width: 100%; + margin: 0.35em auto 0.25em; + padding: 0.5em 0.65em; + border-radius: 12px; + border: 1px solid var(--border-subtle); + background: rgba(13, 17, 21, 0.55); + box-shadow: var(--panel-glow), inset 0 1px 0 0 rgba(255, 255, 255, 0.03); + max-width: 100%; + min-width: 0; + box-sizing: border-box; +} + +.reveal section.slide-boucle .boucle-flow-row { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 0.35em 0.2em; + width: 100%; + min-width: 0; +} + +.reveal section.slide-boucle .boucle-flow-node { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.32em 0.55em; + font-size: 0.48em; + font-weight: 600; + line-height: 1.2; + color: var(--text-primary); + background: var(--bg-medium); + border: 1px solid var(--accent-cyan); + border-radius: 8px; + box-shadow: 0 0 0 1px rgba(43, 201, 198, 0.12); +} + +.reveal section.slide-boucle .boucle-flow-arr { + color: var(--accent-cyan); + font-size: 0.52em; + font-weight: 600; + line-height: 1; + opacity: 0.95; + user-select: none; +} + +.reveal section.slide-boucle .slide-boucle-foot { + margin: 0.4em 0 0; + font-size: clamp(0.7rem, 2.8vw, 0.88rem); + line-height: 1.35; + color: var(--text-muted); +} + +.reveal section.slide-boucle .slide-boucle-foot strong { + color: var(--accent-cyan); + font-weight: 600; +} diff --git a/reveal/index.html b/reveal/index.html new file mode 100644 index 0000000..2efd8bb --- /dev/null +++ b/reveal/index.html @@ -0,0 +1,804 @@ + + + + + + + Kubernetes Operators — De l’enfer au paradis + + + + + + + + +
+
+
+

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 vous l’a vendu comme un truc d’infra

+
+
    +
  • Complexe
  • +
  • Réservé aux DevOps
  • +
  • Magie noire
  • +
+ +
+
+

Kubernetes = API + état + boucle

+
    +
  • API REST
  • +
  • Base d’état
  • +
  • Boucle de réconciliation
  • +
  • Pas de mystère : des mécanismes explicites
  • +
+ +
+
+

De vos outils au noyau Linux

+
flowchart LR
+    A[Clients] --> B[kube-api]
+    B --> C[etcd]
+    B --> D[Controllers]
+    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

+
    +
  • Il orchestre
  • +
  • Il délègue
  • +
  • Il observe
  • +
+ +
+
+
+

État voulu → réel : la boucle

+
flowchart LR
+    A[Désiré] --> B[API] --> C[Controllers] --> D[Réel]
+
+

+ réinjecte vers les contrôleurs · chaque tour : observer, décider, agir +

+
+ +
+
+

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 noyau
  • +
+
+

À 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 ?

+ + + + + + + + + + + + + + + + + +
TypeRôle
CNI / CSI / CRIInfrastructure
OpérateurLogique 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

+ +
+
+

Automatiser le métier… en code dans le cluster

+ +
+
+

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

+ + + + + + + + + + + + + + + + + + + + +
OutilIdéeDé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é changeAnnotation 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.
  • +
+ +
+
+

Cert-manager, Postgres, Keycloak : la spec devient une entité

+ + + + + + + + + + + + + + + + + + + + + + + + + +
OpérateurCRD (exemples)Ce que ça matérialise
cert-managerCertificate, Issuer, ClusterIssuerSecrets TLS, chaînes ACME / PKI
Postgres Operator (Zalando)postgresql (acid.zalan.do)Cluster PostgreSQL (Patroni, volumes, users…)
Keycloak OperatorKeycloak, KeycloakRealm, KeycloakClient, …Instances Keycloak, royaumes, clients
+

La spec décrit l’état métier (cert, base, realm) ; le contrôleur crée / met à jour Deployments, Services, + Secrets, etc.

+ +
+
+

Un certificat déclaratif, tout le cycle TLS en dessous

+
apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+  name: api-example
+spec:
+  dnsNames:
+    - api.example.com
+
+

La suite est 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

+
apiVersion: acid.zalan.do/v1
+kind: postgresql
+metadata:
+  name: my-cluster
+spec:
+  numberOfInstances: 3
+
+

Derrière cette spec, le contrôleur s’occupe de :

+
    +
  • Réplication
  • +
  • Failover
  • +
  • Backup
  • +
  • Users / rôles
  • +
+ +
+
+

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.
  • +
+ +
+
+

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 sous LSD.

+
+ +
+
+

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

+ +
+
+ +
+ + + + + + + + + \ No newline at end of file