Files
Talks/kubernetes-hell-to-heaven/index.html
T

804 lines
36 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Kubernetes Operators — De l’enfer au paradis</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.6.1/reveal.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.6.1/theme/moon.min.css" />
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.6.1/plugin/highlight/monokai.min.css" />
<link rel="stylesheet" href="css/custom.css" />
<!--
Reveal.js 4.6.1 — plugins : Notes orateur + surlignage code.
S : fenêtre notes (même origine, servir en HTTP, pas file://).
? : aide raccourcis · Échap : aperçu · F : plein écran
-->
</head>
<body>
<div class="reveal">
<div class="slides">
<section>
<h1>Kubernetes Operators — De l’enfer… au paradis</h1>
<blockquote>
<p>Kubernetes n’est <strong>pas</strong> compliqué.<br />
Mais <strong>mal compris</strong>… c’est un enfer.</p>
</blockquote>
<aside class="notes">
<p>On va parler de Kubernetes… mais surtout de pourquoi les opérateurs changent complètement la manière de
travailler — et pourquoi c’est <strong>puissant</strong> mais <strong>dangereux</strong> si on se plante sur
le mental model.</p>
<p>Illustration suggérée : diagramme moderne et minimal, flat design, lisible (éviter le poster CNCF
illisible).</p>
</aside>
</section>
<section>
<h1>Kubernetes : délire d’Ops ?</h1>
<blockquote>
<p>On vous l’a vendu comme un truc d’infra</p>
</blockquote>
<ul>
<li>Complexe</li>
<li>Réservé aux DevOps</li>
<li>Magie noire</li>
</ul>
<aside class="notes">
<p>On vous a vendu Kubernetes comme un délire d’infra, réservé à des gens chelous.<br />
En réalité… c’est beaucoup plus proche de ce que vous faites déjà.</p>
<p><em>Chaos scripts</em> — dev entouré de scripts, crons, alertes, terminal chargé, ambiance tendue
(contraste avec la suite).</p>
</aside>
</section>
<section>
<h1>Kubernetes = API + état + boucle</h1>
<ul>
<li>API REST</li>
<li>Base d’état</li>
<li>Boucle de réconciliation</li>
<li>Pas de mystère : des mécanismes explicites</li>
</ul>
<aside class="notes">
<p>Kubernetes, c’est juste une API, une base d’état, et une boucle qui corrige la réalité.<br />
Pas de magie — des patterns qu’on retrouve ailleurs en dev.</p>
</aside>
</section>
<section>
<h1>De vos outils au noyau Linux</h1>
<pre><code class="language-mermaid">flowchart LR
A[Clients] --&gt; B[kube-api]
B --&gt; C[etcd]
B --&gt; D[Controllers]
D --&gt; E[kubelet]
E --&gt; F[containerd]
F --&gt; G[Kernel Linux]
</code></pre>
<ul>
<li><strong>kube-api</strong> — point d’entrée</li>
<li><strong>etcd</strong> — source de vérité</li>
<li><strong>kubelet</strong> — agent sur chaque nœud</li>
<li><strong>container runtime</strong> (ex. containerd) — exécution des conteneurs</li>
</ul>
<aside class="notes">
<p>Tout ça, ce sont des clients : Helm, kubectl, Flux… ils appellent l’API.<br />
L’API parle à etcd, les controllers poussent le travail vers les kubelets, et le runtime lance les
conteneurs.</p>
<p>Schéma ASCII de secours :</p>
<pre><code>Clients → kube-api → etcd
↓
controllers → kubelet → containerd → kernel
</code></pre>
</aside>
</section>
<section>
<h1>Kubernetes ne fait pas tourner vos apps</h1>
<ul>
<li>Il <strong>orchestre</strong></li>
<li>Il <strong>délègue</strong></li>
<li>Il <strong>observe</strong></li>
</ul>
<aside class="notes">
<p>Ce n’est pas Kubernetes qui « exécute votre métier » comme un process magique : il coordonne, délègue aux
agents, et observe l’état.<br />
La suite : le cœur, c’est la boucle de réconciliation.</p>
<p>(Cette slide remplace l’ancien zoom « kube-api → etcd » seul : le détail etcd est déjà dans la vue
d’ensemble slide 4.)</p>
</aside>
</section>
<section class="slide-boucle">
<div class="slide-boucle-inner">
<h1 class="slide-boucle-title">État voulu → réel : la boucle</h1>
<pre><code class="language-mermaid">flowchart LR
A[Désiré] --&gt; B[API] --&gt; C[Controllers] --&gt; D[Réel]
</code></pre>
<p class="slide-boucle-foot">
<strong>↻</strong> réinjecte vers les contrôleurs · <em>chaque tour : observer, décider, agir</em>
</p>
</div>
<aside class="notes">
<p>Des agents exécutent les instructions côté nœud (kubelet, runtime), mais le fil conducteur, c’est : état
voulu, comparaison, correction en boucle.</p>
</aside>
</section>
<section>
<h1>Sous containerd : cgroups et namespaces</h1>
<pre><code class="language-mermaid">flowchart TD
F[containerd] --> K[Linux Kernel]
</code></pre>
<ul>
<li><strong>cgroups</strong></li>
<li><strong>namespaces</strong></li>
</ul>
<aside class="notes">
<p>Et derrière containerd… le noyau Linux. Cgroups, namespaces…</p>
<p><em>(pause)</em></p>
<p>Et ça… vous n’avez pas envie d’y manipuler les fichiers à la main au quotidien.</p>
<p><em>Kernel danger zone</em> — mécanique dense, style industriel sombre (le « trou du souffleur » sous
Kubernetes).</p>
</aside>
</section>
<section>
<h1>Kubernetes vous évite de vivre dans le noyau</h1>
<ul>
<li>Manipuler les <strong>cgroups</strong> à la main</li>
<li>Gérer <strong>isolation</strong>, <strong>CPU</strong>, <strong>mémoire</strong> au niveau noyau</li>
</ul>
<blockquote>
<p>À côté de ça, un cluster qui râle, c’est presque du repos.</p>
</blockquote>
<aside class="notes">
<p>Si Kubernetes existe, c’est pour vous éviter de vivre dans ces couches-là.<br />
À côté du noyau, Kubernetes est presque rassurant.</p>
</aside>
</section>
<section>
<h1>CNI, CSI, CRI : de l’infra branchable</h1>
<ul>
<li><strong>CNI</strong> → réseau (ex : Istio, Cilium ,Calico, Flannel, etc.)</li>
<li><strong>CSI</strong> → stockage (ex : Ceph, Longhorn , Amazon,etc.)</li>
<li><strong>CRI</strong> → runtime (ex : containerd, CRI-O,etc.)</li>
<li>Ici : brancher l’infra — pas encoder la logique métier (<em>≠</em> opérateur)</li>
</ul>
<aside class="notes">
<p>On peut brancher réseau, stockage, runtime… mais ce n’est pas notre sujet du jour — et surtout, ne les
confondez pas avec la logique métier.</p>
</aside>
</section>
<section>
<h1>Brancher l’infra ou coder le métier ?</h1>
<table>
<thead>
<tr>
<th>Type</th>
<th>Rôle</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>CNI / CSI / CRI</strong></td>
<td>Infrastructure</td>
</tr>
<tr>
<td><strong>Opérateur</strong></td>
<td>Logique métier</td>
</tr>
</tbody>
</table>
<aside class="notes">
<p>D’un côté : des briques d’infra branchables.<br />
De l’autre : du <strong>code</strong> qui encode <strong>comment</strong> votre système doit se comporter
dans le cluster.</p>
<p>Icônes utiles :</p>
<ul>
<li><a href="https://github.com/kubernetes/community/tree/master/icons">Icônes Kubernetes (officiel)</a>
</li>
<li><a href="https://landscape.cncf.io/">CNCF Landscape</a></li>
<li><a href="https://simpleicons.org/">Simple Icons (SVG)</a></li>
</ul>
</aside>
</section>
<section>
<h1>Qui écrit la logique du cluster ?</h1>
<aside class="notes">
<p>Qui décide de ce qu’il faut faire quand l’état réel diverge de ce qu’on veut ?</p>
<p><em>(pause)</em></p>
</aside>
</section>
<section>
<h1>La logique vit dans les controllers</h1>
<p><em>Ils ferment la boucle : observation, décision, action.</em></p>
<aside class="notes">
<p>La vraie intelligence de Kubernetes… elle est dans les controllers.<br />
Ce sont eux qui ferment la boucle : observation, décision, action.</p>
</aside>
</section>
<section>
<h1>Les opérateurs : des controllers spécialisés</h1>
<aside class="notes">
<p>Les opérateurs, ce sont des controllers… mais focalisés sur un domaine métier ou un logiciel précis (base,
queue, etc.).<br />
Même mécanisme, autre niveau d’abstraction.</p>
</aside>
</section>
<section>
<h1>Automatiser le métier… en code dans le cluster</h1>
<aside class="notes">
<p>Un opérateur, c’est du code qui dit au cluster comment faire vivre un service au-delà du simple « Pod +
Service » : upgrades, backup, réplication, etc.</p>
</aside>
</section>
<section>
<h1>Sans opérateur : scripts, crons, humains dans la boucle</h1>
<ul>
<li>Scripts bash</li>
<li>Cron jobs</li>
<li>Interventions humaines</li>
<li>État incohérent</li>
</ul>
<aside class="notes">
<p>Avant : bricolage, des humains dans la boucle, et un état qui part dans tous les sens dès qu’il y a une
alerte à 3 h du matin.</p>
</aside>
</section>
<section>
<h1>Automatisation éclatée : la logique reste dans les têtes</h1>
<ul>
<li>Runbooks Confluence</li>
<li>Scripts bash « magiques »</li>
<li>Cron jobs oubliés</li>
<li>Interventions humaines</li>
</ul>
<aside class="notes">
<p>Avant les opérateurs, l’automatisation existe… mais elle est <strong>fragmentée</strong>.<br />
Chaque problème a sa solution bricolée.</p>
<p>Et surtout :<br />
<strong>la logique est dans la tête des gens, pas dans le système.</strong>
</p>
</aside>
</section>
<section>
<h1>Drift, pas de reproductibilité, 3 h du matin</h1>
<ul>
<li>Drift de configuration</li>
<li>Actions non reproductibles</li>
<li>Dépendance aux humains</li>
<li>Incidents à 3 h du matin</li>
</ul>
<aside class="notes">
<p>Le problème, ce n’est pas juste « moins automatisé ».</p>
<p>C’est :</p>
<ul>
<li>pas <strong>reproductible</strong></li>
<li>pas <strong>observable</strong> au sens cluster</li>
<li>pas <strong>fiable</strong> dans le temps</li>
</ul>
<p>Surtout : impossible de <strong>raisonner globalement</strong> sur l’état du système.</p>
</aside>
</section>
<section>
<h1>Vous déclarez ; le système réconcilie</h1>
<pre><code class="language-yaml">kind: Database
spec:
size: 3
</code></pre>
<aside class="notes">
<p>Après : on décrit l’état voulu… et le contrôleur / l’opérateur fait le travail répétitif et les
transitions.</p>
<p><em>Paradis opérateur</em> — ingénieur calme, infra automatisée, UI claire, atmosphère sereine (contraste
avec slide enfer).</p>
</aside>
</section>
<section>
<h1>Spec, opérateur, cluster : ça tourne en boucle</h1>
<pre><code class="language-mermaid">flowchart LR
A[Spec] --&gt; B[Operator]
B --&gt; C[Cluster]
C --&gt; B
</code></pre>
<p><em>Dès que le réel diverge, la réconciliation repart.</em></p>
<aside class="notes">
<p>Le système réobserve, recompare, recorrige. C’est la même philosophie que le reste de Kubernetes, appliquée
à votre domaine.</p>
<p>Quelques <strong>exemples réels</strong> : d’abord des outils qui <strong>étendent</strong> des ressources
natives avec des <strong>annotations</strong>, puis des opérateurs qui posent de <strong>vrais CRD</strong>
— et un projet maison pour ancrer le discours.</p>
</aside>
</section>
<section>
<h1>Reflector &amp; Reloader : la puissance des annotations</h1>
<table>
<thead>
<tr>
<th>Outil</th>
<th>Idée</th>
<th>Déclencheur typique</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Reflector</strong> (EmberStack)</td>
<td>Copie / réplique des <strong>Secrets</strong> et <strong>ConfigMaps</strong> (autres namespaces,
miroirs)</td>
<td>Annotations sur la ressource source ou la copie</td>
</tr>
<tr>
<td><strong>Reloader</strong> (Stakater)</td>
<td><strong>Redémarre</strong> les Pods quand un Secret / ConfigMap référencé change</td>
<td>Annotation sur le <strong>Deployment</strong> / <strong>StatefulSet</strong> / etc.</td>
</tr>
</tbody>
</table>
<ul>
<li>Reflector : annotations du type <code>reflector.v1.k8s.emberstack.com/*</code> pour autoriser la réflexion
et cibler où copier.</li>
<li>Reloader : <code>reloader.stakater.com/auto: "true"</code> (ou annotations plus fines par config) pour
lier redéploiement ↔ mise à jour de config.</li>
</ul>
<aside class="notes">
<p>Ce n’est pas toujours un « gros » opérateur avec CRD : parfois c’est un <strong>contrôleur</strong> qui
<strong>regarde</strong> des objets standards et réagit à des <strong>annotations</strong>.<br />
Très utile pour TLS partagé, configs dupliquées, ou « je change un secret → je veux un rollout » sans
pipeline ad hoc.
</p>
<ul>
<li><a href="https://github.com/emberstack/kubernetes-reflector">kubernetes-reflector</a></li>
<li><a href="https://github.com/stakater/Reloader">Reloader</a></li>
</ul>
</aside>
</section>
<section>
<h1>Cert-manager, Postgres, Keycloak : la spec devient une entité</h1>
<table>
<thead>
<tr>
<th>Opérateur</th>
<th>CRD (exemples)</th>
<th>Ce que ça <strong>matérialise</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>cert-manager</strong></td>
<td><code>Certificate</code>, <code>Issuer</code>, <code>ClusterIssuer</code></td>
<td>Secrets TLS, chaînes ACME / PKI</td>
</tr>
<tr>
<td><strong>Postgres Operator</strong> (Zalando)</td>
<td><code>postgresql</code> (<code>acid.zalan.do</code>)</td>
<td>Cluster PostgreSQL (Patroni, volumes, users…)</td>
</tr>
<tr>
<td><strong>Keycloak Operator</strong></td>
<td><code>Keycloak</code>, <code>KeycloakRealm</code>, <code>KeycloakClient</code>, …</td>
<td>Instances Keycloak, royaumes, clients</td>
</tr>
</tbody>
</table>
<p>La spec décrit l’état métier (cert, base, realm) ; le contrôleur crée / met à jour Deployments, Services,
Secrets, etc.</p>
<aside class="notes">
<p>On est dans le pattern « Database, size: 3 » mais en prod : certificats, bases gérées, IdP.<br />
Trois domaines, <strong>même mécanisme</strong> : CRD + boucle de réconciliation.</p>
<p>Liens :</p>
<ul>
<li><a href="https://cert-manager.io/">cert-manager</a></li>
<li><a href="https://github.com/zalando/postgres-operator">Zalando Postgres operator</a></li>
<li><a href="https://www.keycloak.org/operator/installation">Keycloak Operator</a></li>
</ul>
</aside>
</section>
<section>
<h1>Un certificat déclaratif, tout le cycle TLS en dessous</h1>
<pre><code class="language-yaml">apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: api-example
spec:
dnsNames:
- api.example.com
</code></pre>
<p>La suite est dérivée automatiquement :</p>
<ul>
<li><code>CertificateRequest</code></li>
<li>Challenge ACME</li>
<li>Secret TLS</li>
</ul>
<aside class="notes">
<p>Tu déclares <strong>juste</strong> un certificat.</p>
<p>Et derrière : génération de clé, challenge ACME, renouvellement automatique.</p>
<p><strong>Aucun humain dans la boucle</strong> pour le cycle de vie du cert.</p>
</aside>
</section>
<section>
<h1>Certificats : du Certbot au renouvellement automatique</h1>
<p><em>Avant — sans opérateur</em></p>
<ul>
<li>Certbot manuel</li>
<li>Scripts cron</li>
<li>Cert expiré = prod down</li>
</ul>
<p><em>Avec cert-manager</em></p>
<ul>
<li>Déclaratif</li>
<li>Auto-renouvellement</li>
<li>Observabilité native (<code>Certificate</code>, conditions, events)</li>
</ul>
<aside class="notes">
<p>cert-manager, c’est typiquement : <strong>une complexité énorme</strong> encapsulée dans un opérateur.</p>
<p><strong>Là, ça vaut clairement le coup</strong> — parce que le domaine est clair, répétitif et critique.
</p>
</aside>
</section>
<section>
<h1>Postgres répliqué sans bricoler le failover à la main</h1>
<pre><code class="language-yaml">apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
name: my-cluster
spec:
numberOfInstances: 3
</code></pre>
<p>Derrière cette spec, le contrôleur s’occupe de :</p>
<ul>
<li>Réplication</li>
<li>Failover</li>
<li>Backup</li>
<li>Users / rôles</li>
</ul>
<aside class="notes">
<p>Ça, c’est intéressant parce que ce n’est <strong>pas</strong> trivial : ce n’est <strong>pas</strong>
stateless, ce n’est <strong>pas</strong> safe à bricoler à la main sur du long terme.</p>
<p>Un opérateur mature sur ce terrain, c’est du <strong>métier base de données</strong> version déclarative.
</p>
</aside>
</section>
<section>
<h1>Quand un opérateur est légitime</h1>
<ul>
<li>Système <strong>complexe</strong></li>
<li><strong>Règles</strong> claires et stables</li>
<li><strong>État</strong> critique pour l’activité</li>
<li>Opérations <strong>répétitives</strong></li>
</ul>
<aside class="notes">
<p>Un opérateur est pertinent quand tu peux formaliser des règles du type <strong>« si X alors Y »</strong>,
que tu dois les appliquer <strong>souvent</strong>, et que tu veux <strong>réduire l’erreur
humaine</strong>.</p>
<p>Si tu ne peux pas écrire ça proprement… <strong>pause</strong>… <strong>ça, c’est une très mauvaise
idée</strong> en opérateur.</p>
</aside>
</section>
<section>
<h1>Keycloak Credential Manager — le même schéma, chez moi</h1>
<ul>
<li>Objectif : <strong>réconcilier</strong> des <strong>credentials Keycloak</strong> (clients, secrets,
rotation…) avec ce qui est <strong>déclaré dans le cluster</strong> (CRD, Secrets annotés, ou les deux —
selon ton design).</li>
<li>Intérêt conf : montrer le <strong>même schéma</strong> que les opérateurs « connus » : <em>observe →
compare → corrige</em> sur <strong>ton</strong> domaine métier.</li>
</ul>
<aside class="notes">
<p>Je vous montre ça pas pour vendre un produit, mais pour prouver que <strong>la logique</strong> est la même
: une API Kubernetes, un état désiré, et du code qui parle à Keycloak à la place d’un humain et d’un
runbook.</p>
<ul>
<li><strong>Lien repo</strong> : <code>https://…</code> (README, une spec d’exemple, capture d’un
<code>kubectl get</code>).
</li>
<li><strong>Une phrase</strong> sur le périmètre exact : ex. clients OAuth uniquement, rotation de secrets,
sync depuis External Secrets, etc.</li>
</ul>
<p>Schéma minimal : <code>CRD / Secret</code> → <strong>Keycloak Credential Manager</strong> → API Keycloak,
style diagramme propre (flat design).</p>
</aside>
</section>
<section>
<h1>Ce qu’un opérateur bien choisi vous apporte</h1>
<ul>
<li>Automatiser du <strong>répétitif</strong></li>
<li><strong>Encapsuler</strong> de la complexité</li>
<li><strong>Stabiliser</strong> un système</li>
</ul>
<aside class="notes">
<p>On remplace des opérations manuelles par du code — quand c’est le bon outil pour du vrai répétitif et des
règles claires.</p>
<p>On vient de voir du <strong>concret</strong> (annotations, CRD, projet maison). La question :
<strong>quand</strong> est-ce un bon outil ?
</p>
<p>…<strong>mais</strong> les opérateurs peuvent aussi devenir un enfer.</p>
</aside>
</section>
<section>
<h1>Quand ne pas écrire d’opérateur</h1>
<ul>
<li>Logique <strong>floue</strong> ou en perpétuelle négociation métier</li>
<li>Processus <strong>instables</strong> (la règle change toutes les semaines)</li>
<li>Dépendances <strong>externes</strong> imprévisibles</li>
<li><strong>One-shot</strong> : automatisation ponctuelle sans cycle de vie</li>
</ul>
<aside class="notes">
<p>Si tu ne peux pas écrire clairement <strong>« si X alors Y »</strong> avec des conditions observables… tu
n’as <strong>rien à faire</strong> dans un opérateur.</p>
<p>Sinon tu recodes un outil métier opaque <strong>dans</strong> le cluster.</p>
</aside>
</section>
<section>
<h1>Cinq signaux qu’un opérateur part en vrille</h1>
<ul>
<li>Spec <strong>incompréhensible</strong> sans lire le code</li>
<li>Effets <strong>implicites</strong> (créations ailleurs sans que ce soit visible)</li>
<li><strong>Couplage</strong> externe fort (APIs fragiles, ordre d’appels magique)</li>
<li><strong>Debug</strong> impossible sans les mainteneurs à côté de toi</li>
<li><strong>État</strong> non observable (pas de conditions / status utiles)</li>
</ul>
<aside class="notes">
<p>Un opérateur doit être <strong>lisible</strong>, <strong>prévisible</strong>, <strong>observable</strong>.
</p>
<p>Sinon, ce n’est pas de la plateforme : c’est une <strong>bombe à retardement</strong> avec un
<code>kubectl</code> dessus.
</p>
</aside>
</section>
<section>
<h1>Débugger un opérateur : describe, events, status, logs</h1>
<ul>
<li><code>kubectl describe</code> (resource + events)</li>
<li><strong>Events</strong> du namespace</li>
<li><strong>Status / conditions</strong> sur la CRD</li>
<li><strong>Logs</strong> du contrôleur</li>
</ul>
<aside class="notes">
<p>Le debug devient <strong>indirect</strong>, <strong>distribué</strong>, parfois <strong>opaque</strong>.
</p>
<p>Si ton opérateur est mal conçu — pas de status clair, pas d’events utiles — <strong>bonne chance</strong>
pour le post-mortem à 3 h du matin.</p>
</aside>
</section>
<section>
<h1>Un opérateur, c’est un produit logiciel — pas un script</h1>
<ul>
<li><strong>Code</strong> et revues</li>
<li><strong>Tests</strong> (unitaires, intégration, e2e si possible)</li>
<li><strong>Maintenance</strong> dans la durée</li>
<li><strong>Compatibilité</strong> avec les versions Kubernetes</li>
<li><strong>Migrations</strong> de CRD / spec</li>
</ul>
<aside class="notes">
<p>Un opérateur, ce n’est <strong>pas</strong> un script du vendredi.</p>
<p>C’est un <strong>produit logiciel</strong> avec un <strong>cycle de vie</strong> — et une dette si tu
l’abandonnes.</p>
<p>Ensuite : la synthèse <strong>« ça part en vrille »</strong> — smells qu’on a déjà nommés, mais côté vécu
d’équipe.</p>
</aside>
</section>
<section>
<h1>Quand l’opérateur devient l’enfer</h1>
<ul>
<li>Logique <strong>implicite</strong></li>
<li><strong>Effets de bord</strong></li>
<li><strong>Debugging</strong> difficile</li>
<li><strong>Couplage</strong> fort</li>
</ul>
<p>En pratique, ça donne souvent :</p>
<ul>
<li>Tout mettre en opérateur</li>
<li>Logique métier <strong>floue</strong></li>
<li>Effets <strong>cachés</strong></li>
</ul>
<blockquote>
<p>Un opérateur mal conçu, c’est un microservice sous LSD.</p>
</blockquote>
<aside class="notes">
<p>Un mauvais opérateur, c’est de la complexité distribuée avec un debugger qui vous hait.<br />
Le piège classique : « formation Kubernetes par la trappe » parce qu’on empile des comportements opaques.
</p>
</aside>
</section>
<section>
<h1>Kubernetes, plateforme de programmation</h1>
<ul>
<li>Kubernetes vous évite <strong>l’enfer de l’infrastructure</strong> bas niveau.</li>
<li>Les bons opérateurs vous évitent <strong>l’enfer du manuel</strong> — les mauvais vous y renvoient
autrement.</li>
</ul>
<aside class="notes">
<p>Kubernetes n’est pas qu’un outil d’infra : c’est une surface où l’on encode du
<strong>comportement</strong>.<br />
Les opérateurs sont l’extension naturelle… à condition de rester honnêtes sur la complexité.
</p>
<p>Fil de la conf (rappel) :</p>
<ol>
<li>Kubernetes n’est pas magique </li>
<li>API + état + boucle ; kernel = vrai « enfer » bas niveau </li>
<li>Controllers / opérateurs : où vit la logique </li>
<li><strong>Avant</strong> : vécu (runbooks, scripts, drift) → <strong>pourquoi</strong> ça casse </li>
<li><strong>Après</strong> + boucle ; exemples Reflector/Reloader, panorama CRD, creux cert-manager &amp;
Postgres </li>
<li>Projet perso ; <strong>bon usage</strong> puis <strong>quand ne pas</strong>, smells, debug,
<strong>coût</strong>
</li>
<li>Pièges ; plateforme de programmation ; <strong>checklist</strong> ; dernière phrase + conclusion brutale
</li>
</ol>
</aside>
</section>
<section>
<h1>Avant d’écrire un opérateur : la checklist</h1>
<ul>
<li>Répétitif</li>
<li>Règles claires</li>
<li>Déclaratif possible</li>
<li>Idempotent</li>
<li>Observable</li>
</ul>
<p>Si une case importante manque, reculez : pas d’opérateur (ou pas tout de suite).</p>
<aside class="notes">
<p>C’est probablement <strong>la</strong> slide la plus importante pour une salle de devs.</p>
<p>Kubernetes donne <strong>envie</strong> d’en mettre partout. <strong>Mauvaise idée</strong> par défaut.</p>
<p>👉 Tu valides la checklist, ou tu <strong>recules</strong>.</p>
<p>Les <strong>pauses</strong> et les <strong>regards</strong> sur la salle après « Mauvaise idée » font
autant le travail qu’un diagramme.</p>
</aside>
</section>
<section>
<h1>Votre logique, exécutée par le cluster au lieu d’un humain</h1>
<aside class="notes">
<p>Tu vises deux réactions :</p>
<ul>
<li>« OK, Kubernetes je comprends enfin le cadre. »</li>
<li>« OK, les opérateurs : puissants, mais à manier avec précision. »</li>
</ul>
<p><strong>Éviter le piège</strong> : faire une « formation Kubernetes » sans le vouloir — rester sur le
message <em>comportement / automatisation / risques</em>.</p>
<p>Un opérateur, c’est votre logique métier… mais exécutée par le cluster au lieu d’un humain.<br />
À vous de garder cette logique <strong>lisible</strong>, <strong>testable</strong>, et
<strong>bornée</strong>.
</p>
</aside>
</section>
<section>
<h1>Kubernetes simplifie l’infra — les opérateurs, ça peut simplifier ou empirer</h1>
<aside class="notes">
<p>Kubernetes vous évite une partie du chaos du bas niveau.</p>
<p>Mais les opérateurs : soit ils <strong>encapsulent</strong> intelligemment une complexité maîtrisée, soit
ils recréent un enfer <strong>plus sophistiqué</strong>.</p>
<p>Ce que la salle retient : les <strong>pauses</strong>, les <strong>« très mauvaise idée »</strong>
honnêtes, plus que chaque ligne du deck.</p>
</aside>
</section>
</div>
<footer class="deck-footer">
<a href="https://specificat.io/" rel="noopener noreferrer" target="_blank">Specificat.io</a>
<span class="deck-footer-sep" aria-hidden="true">·</span>
<span class="deck-footer-rights">All rights reserved</span>
</footer>
</div>
<!-- Ordre : cœur Reveal → plugins officiels (notes, highlight) → Mermaid -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.6.1/reveal.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.6.1/plugin/notes/notes.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/4.6.1/plugin/highlight/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
<script>
(function () {
if (typeof RevealNotes === "undefined" || typeof RevealHighlight === "undefined") {
console.error("Reveal : plugins Notes ou Highlight introuvables (vérifiez le réseau / CDN).");
}
/* Thème Mermaid aligné sur https://specificat.io/ (variables en hex — requis par Mermaid) */
mermaid.initialize({
startOnLoad: false,
theme: "base",
securityLevel: "loose",
/* foreignObject + thème base = libellés vides chez certains navigateurs ; SVG text est fiable */
htmlLabels: false,
fontFamily:
'system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif',
themeVariables: {
darkMode: true,
background: "#050709",
fontSize: "15px",
primaryColor: "#0d1115",
primaryTextColor: "#f5f5f5",
primaryBorderColor: "#2bc9c6",
secondaryColor: "#1b1f22",
secondaryTextColor: "#f5f5f5",
secondaryBorderColor: "#3d6d6a",
tertiaryColor: "#050709",
tertiaryTextColor: "#c8d0d4",
tertiaryBorderColor: "#2bc9c6",
lineColor: "#4dbfb8",
textColor: "#f5f5f5",
mainBkg: "#0d1115",
nodeBorder: "#2bc9c6",
nodeTextColor: "#f5f5f5",
clusterBkg: "#0a1014",
clusterBorder: "#2bc9c6",
defaultLinkColor: "#4dbfb8",
edgeLabelBackground: "#0d1115",
titleColor: "#f5f5f5",
nodeSpacing: 10,
rankSpacing: 10
},
flowchart: {
useMaxWidth: true,
curve: "natural",
},
});
function upgradeMermaidBlocks(container) {
container.querySelectorAll("pre code.language-mermaid").forEach(function (code) {
var pre = code.parentElement;
var div = document.createElement("div");
div.className = "mermaid";
var raw = code.textContent.replace(/\r\n/g, "\n");
div.setAttribute("data-mermaid-src", raw);
div.textContent = raw;
pre.parentNode.replaceChild(div, pre);
});
}
/** Mermaid dans une slide display:none a souvent largeur 0 → SVG vide ou taille absurde : on ne rend que la slide visible. */
function resetMermaidIfBroken(el) {
if (!el.getAttribute("data-processed")) return;
var svg = el.querySelector("svg");
if (svg && svg.getBoundingClientRect().height > 8) return;
var src = el.getAttribute("data-mermaid-src");
if (!src) return;
el.removeAttribute("data-processed");
el.innerHTML = "";
el.textContent = src;
}
function runMermaidInSlide(slide) {
if (!slide || typeof mermaid.run !== "function") return;
slide.querySelectorAll(".mermaid").forEach(resetMermaidIfBroken);
var pending = slide.querySelectorAll(".mermaid:not([data-processed])");
if (!pending.length) return;
requestAnimationFrame(function () {
requestAnimationFrame(function () {
mermaid.run({ nodes: pending, suppressErrors: true }).catch(function () { });
});
});
}
function applyHighlight() {
if (typeof hljs === "undefined") return;
document.querySelectorAll(".reveal .slides pre code").forEach(function (el) {
if (!el.classList.contains("language-mermaid")) hljs.highlightElement(el);
});
}
Reveal.initialize({
hash: true,
slideNumber: "c/t",
progress: true,
center: true,
transition: "slide",
backgroundTransition: "fade",
// Notes orateur en premier (touche S) ; puis coloration des blocs de code
plugins: [RevealNotes, RevealHighlight],
}).then(function () {
applyHighlight();
document.querySelectorAll(".reveal .slides section").forEach(function (sec) {
upgradeMermaidBlocks(sec);
});
runMermaidInSlide(Reveal.getCurrentSlide());
});
Reveal.on("slidechanged", function (event) {
runMermaidInSlide(event.currentSlide);
/* Slide « boucle » : sans ça, flex + centrage Reveal donnent souvent une 1ʳᵉ mesure de largeur fausse ; resize appelle déjà layout() — on le déclenche ici. */
if (event.currentSlide && event.currentSlide.classList.contains("slide-boucle")) {
requestAnimationFrame(function () {
requestAnimationFrame(function () {
if (typeof Reveal.layout === "function") Reveal.layout();
});
});
}
});
})();
</script>
</body>
</html>