ci
Some checks failed
Talks slides — image & chart / vars (push) Successful in 2s
Talks slides — image & chart / Build container image (push) Failing after 41s
Talks slides — image & chart / Helm chart (push) Failing after 43s

This commit is contained in:
Le Prévost-Corvellec Arnault
2026-04-08 20:13:50 +02:00
parent 4c921843a6
commit 4ffed1b5fa
19 changed files with 695 additions and 0 deletions

View File

@@ -0,0 +1,286 @@
/* 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). Doù 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 <text> */
.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;
}
/* Mermaid sur la slide « boucle » : hauteur mini correcte (éviter lécrasement à ~78px qui tuait les libellés) */
.reveal section.slide-boucle .mermaid {
margin: 0.25em auto 0.15em;
line-height: normal;
padding: 0.45em 0.65em;
min-width: min(100%, 320px);
}
.reveal section.slide-boucle .mermaid svg {
max-height: min(34vh, 220px) !important;
overflow: visible !important;
}
.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;
}

View File

@@ -0,0 +1,804 @@
<!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 lenfer 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 lenfer… au paradis</h1>
<blockquote>
<p>Kubernetes nest <strong>pas</strong> compliqué.<br />
Mais <strong>mal compris</strong>… cest 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 cest <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 dOps ?</h1>
<blockquote>
<p>On vous la vendu comme un truc dinfra</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 dinfra, réservé à des gens chelous.<br />
En réalité… cest 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, cest juste une API, une base détat, et une boucle qui corrige la réalité.<br />
Pas de magie — des patterns quon 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 dentré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 lAPI.<br />
LAPI 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 nest 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, cest la boucle de réconciliation.</p>
<p>(Cette slide remplace lancien zoom « kube-api → etcd » seul : le détail etcd est déjà dans la vue
densemble 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, cest : é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 navez pas envie dy 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, cest presque du repos.</p>
</blockquote>
<aside class="notes">
<p>Si Kubernetes existe, cest 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 linfra 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 linfra — 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 nest pas notre sujet du jour — et surtout, ne les
confondez pas avec la logique métier.</p>
</aside>
</section>
<section>
<h1>Brancher linfra 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>Dun côté : des briques dinfra branchables.<br />
De lautre : 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 quil faut faire quand létat réel diverge de ce quon 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 dabstraction.</p>
</aside>
</section>
<section>
<h1>Automatiser le métier… en code dans le cluster</h1>
<aside class="notes">
<p>Un opérateur, cest 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 quil 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, lautomatisation 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 nest pas juste « moins automatisé ».</p>
<p>Cest :</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 / lopé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. Cest la même philosophie que le reste de Kubernetes, appliquée
à votre domaine.</p>
<p>Quelques <strong>exemples réels</strong> : dabord 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 nest pas toujours un « gros » opérateur avec CRD : parfois cest 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, cest 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 soccupe de :</p>
<ul>
<li>Réplication</li>
<li>Failover</li>
<li>Backup</li>
<li>Users / rôles</li>
</ul>
<aside class="notes">
<p>Ça, cest intéressant parce que ce nest <strong>pas</strong> trivial : ce nest <strong>pas</strong>
stateless, ce nest <strong>pas</strong> safe à bricoler à la main sur du long terme.</p>
<p>Un opérateur mature sur ce terrain, cest 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 lactivité</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 lerreur
humaine</strong>.</p>
<p>Si tu ne peux pas écrire ça proprement… <strong>pause</strong><strong>ça, cest 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 dun humain et dun
runbook.</p>
<ul>
<li><strong>Lien repo</strong> : <code>https://…</code> (README, une spec dexemple, capture dun
<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 quun 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 cest 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 dopé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
nas <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 quun 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 dappels 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 nest pas de la plateforme : cest 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 devents utiles — <strong>bonne chance</strong>
pour le post-mortem à 3 h du matin.</p>
</aside>
</section>
<section>
<h1>Un opérateur, cest 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 nest <strong>pas</strong> un script du vendredi.</p>
<p>Cest un <strong>produit logiciel</strong> avec un <strong>cycle de vie</strong> — et une dette si tu
labandonnes.</p>
<p>Ensuite : la synthèse <strong>« ça part en vrille »</strong> — smells quon a déjà nommés, mais côté vécu
déquipe.</p>
</aside>
</section>
<section>
<h1>Quand lopérateur devient lenfer</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, cest un microservice sous LSD.</p>
</blockquote>
<aside class="notes">
<p>Un mauvais opérateur, cest de la complexité distribuée avec un debugger qui vous hait.<br />
Le piège classique : « formation Kubernetes par la trappe » parce quon empile des comportements opaques.
</p>
</aside>
</section>
<section>
<h1>Kubernetes, plateforme de programmation</h1>
<ul>
<li>Kubernetes vous évite <strong>lenfer de linfrastructure</strong> bas niveau.</li>
<li>Les bons opérateurs vous évitent <strong>lenfer du manuel</strong> — les mauvais vous y renvoient
autrement.</li>
</ul>
<aside class="notes">
<p>Kubernetes nest pas quun outil dinfra : cest une surface où lon encode du
<strong>comportement</strong>.<br />
Les opérateurs sont lextension naturelle… à condition de rester honnêtes sur la complexité.
</p>
<p>Fil de la conf (rappel) :</p>
<ol>
<li>Kubernetes nest 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 dopérateur (ou pas tout de suite).</p>
<aside class="notes">
<p>Cest probablement <strong>la</strong> slide la plus importante pour une salle de devs.</p>
<p>Kubernetes donne <strong>envie</strong> den 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 quun diagramme.</p>
</aside>
</section>
<section>
<h1>Votre logique, exécutée par le cluster au lieu dun 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, cest votre logique métier… mais exécutée par le cluster au lieu dun 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 linfra — 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>