ci
This commit is contained in:
286
content/kubernetes-hell-to-heaven/css/custom.css
Normal file
286
content/kubernetes-hell-to-heaven/css/custom.css
Normal 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). D’où des SVG à ~1000px de haut.
|
||||
* On borne par la hauteur et on laisse width: auto recalculer la largeur au ratio du viewBox.
|
||||
*/
|
||||
.reveal .mermaid svg {
|
||||
width: auto !important;
|
||||
max-width: 100% !important;
|
||||
max-height: min(52vh, 520px) !important;
|
||||
height: auto !important;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* Libellés Mermaid (SVG) : couleur explicite — le thème base ne peint pas toujours les <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;
|
||||
}
|
||||
804
content/kubernetes-hell-to-heaven/index.html
Normal file
804
content/kubernetes-hell-to-heaven/index.html
Normal 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 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>
|
||||
Reference in New Issue
Block a user