804 lines
36 KiB
HTML
804 lines
36 KiB
HTML
<!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] --> B[kube-api]
|
||
B --> C[etcd]
|
||
B --> D[Controllers]
|
||
D --> E[kubelet]
|
||
E --> F[containerd]
|
||
F --> 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é] --> B[API] --> C[Controllers] --> 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] --> B[Operator]
|
||
B --> C[Cluster]
|
||
C --> 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 & 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 &
|
||
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> |