Enhance index.html and custom.css for improved content presentation and new assets
- Updated index.html to refine content structure, including enhanced blockquotes and lists for better readability. - Added a new section to emphasize Kubernetes as a developer-centric tool. - Improved CSS styles in custom.css for blockquote presentation and slide layout adjustments. - Introduced new SVG and WEBP images for better visual representation of tools used in Kubernetes.
This commit is contained in:
@@ -182,6 +182,13 @@
|
|||||||
padding: 0.45em 0.85em;
|
padding: 0.45em 0.85em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reveal blockquote .blockquote-sub {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0.0em;
|
||||||
|
font-size: 0.88em;
|
||||||
|
color: var(--text-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
.reveal aside.notes {
|
.reveal aside.notes {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
@@ -238,7 +245,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Slide « boucle » (n° 6) : graphe cyclique Mermaid = très haut ; graphe linéaire + libellés courts.
|
* Slide « boucle » (API / événements / controllers / réconciliation) : graphe Mermaid assez large — hauteur plafonnée.
|
||||||
* Pas de scale() : ça ne réduit pas la hauteur de layout (Reveal mesure scrollHeight).
|
* Pas de scale() : ça ne réduit pas la hauteur de layout (Reveal mesure scrollHeight).
|
||||||
*/
|
*/
|
||||||
.reveal .slides section.slide-boucle {
|
.reveal .slides section.slide-boucle {
|
||||||
@@ -260,7 +267,16 @@
|
|||||||
line-height: 1.15;
|
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 .slide-boucle-lead {
|
||||||
|
margin: 0 0 0.45em;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
font-size: clamp(0.82rem, 2.6vw, 0.95rem);
|
||||||
|
color: var(--text-muted);
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mermaid sur la slide « boucle » : graphe fusionné (outils + boucle) — un peu plus de hauteur qu’avant */
|
||||||
.reveal section.slide-boucle .mermaid {
|
.reveal section.slide-boucle .mermaid {
|
||||||
margin: 0.25em auto 0.15em;
|
margin: 0.25em auto 0.15em;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
@@ -269,7 +285,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.reveal section.slide-boucle .mermaid svg {
|
.reveal section.slide-boucle .mermaid svg {
|
||||||
max-height: min(34vh, 220px) !important;
|
max-height: min(42vh, 300px) !important;
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
116
content/kubernetes-hell-to-heaven/img/helmsh-icon.svg
Normal file
116
content/kubernetes-hell-to-heaven/img/helmsh-icon.svg
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
height="64"
|
||||||
|
viewBox="0 0 64 64"
|
||||||
|
width="64"
|
||||||
|
version="1.1"
|
||||||
|
id="svg12"
|
||||||
|
sodipodi:docname="helmsh-icon.svg"
|
||||||
|
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs12" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview12"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:zoom="2.2302632"
|
||||||
|
inkscape:cx="152"
|
||||||
|
inkscape:cy="175.31563"
|
||||||
|
inkscape:window-width="1392"
|
||||||
|
inkscape:window-height="997"
|
||||||
|
inkscape:window-x="1802"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="svg12" />
|
||||||
|
<mask
|
||||||
|
id="a"
|
||||||
|
fill="#ffffff">
|
||||||
|
<path
|
||||||
|
d="M 0,0 H 313.30315 V 159.86486 H 0 Z"
|
||||||
|
fill="#ffffff"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path1" />
|
||||||
|
</mask>
|
||||||
|
<mask
|
||||||
|
id="b"
|
||||||
|
fill="#ffffff">
|
||||||
|
<path
|
||||||
|
d="M 0,0 H 313.30315 V 159.86486 H 0 Z"
|
||||||
|
fill="#ffffff"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
id="path2" />
|
||||||
|
</mask>
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
transform="matrix(0.1829216,0,0,0.1829216,1.8637371,-9.3897197)"
|
||||||
|
id="g12">
|
||||||
|
<path
|
||||||
|
d="m 11.678571,189 h 19.785836 v 26.789 h 23.9037 V 189 h 19.785836 v 75.25 H 55.368107 v -28.69533 h -23.9037 V 264.25 H 11.678571 Z m 86.173843,75.25 V 189 h 46.803046 v 16.35433 H 117.63825 V 217.595 h 23.9037 v 16.65533 h -23.9037 v 13.846 h 27.01721 V 264.25 Z m 68.497156,0 V 189 h 19.78584 v 55.384 h 27.11764 V 264.25 Z M 243.88594,189 274.61927,216.89267 305.25216,189 h 8.93878 v 75.25 H 294.30467 V 225.62167 L 274.61927,243.58133 254.83344,225.722 V 264.25 H 234.94716 V 189 Z"
|
||||||
|
fill="#0f1689"
|
||||||
|
id="path3" />
|
||||||
|
<g
|
||||||
|
transform="matrix(1,0,0,-1,11.958136,455)"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
fill="#0f1689"
|
||||||
|
mask="url(#a)"
|
||||||
|
id="g6">
|
||||||
|
<path
|
||||||
|
d="m 203.46068,95.687543 c 6.93631,0 12.5593,-14.80922 12.5593,-33.077318 0,-18.268097 -5.62299,-33.077317 -12.5593,-33.077317 -6.93631,0 -12.55931,14.80922 -12.55931,33.077317 0,18.268098 5.62299,33.077318 12.55931,33.077318 z"
|
||||||
|
transform="rotate(35,137.93117,151.55002)"
|
||||||
|
id="path4" />
|
||||||
|
<path
|
||||||
|
d="m 30.142322,95.687543 c 6.936311,0 12.559301,-14.80922 12.559301,-33.077318 0,-18.268097 -5.62299,-33.077317 -12.559301,-33.077317 -6.93631,0 -12.559301,14.80922 -12.559301,33.077317 0,18.268098 5.622991,33.077318 12.559301,33.077318 z"
|
||||||
|
transform="matrix(-0.81915204,0.57357644,0.57357644,0.81915204,58.084611,47.704768)"
|
||||||
|
id="path5" />
|
||||||
|
<path
|
||||||
|
d="m 116.73282,66.275268 c 6.93631,0 12.5593,-14.80922 12.5593,-33.077318 0,-18.268097 -5.62299,-33.07731673 -12.55931,-33.07731673 -6.93631,0 -12.5593,14.80921973 -12.5593,33.07731673 0,18.268098 5.62299,33.077318 12.55931,33.077318 z"
|
||||||
|
transform="matrix(-1,0,0,1,272.62852,53.670762)"
|
||||||
|
id="path6" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="m 251.46701,173.09985 c -20.23008,-33.60997 -56.88957,-56.06791 -98.75578,-56.06791 -40.7208,0 -76.515877,21.2459 -97.058696,53.33459 m 2.198111,129.16953 c 20.840303,30.2327 55.555905,50.02659 94.860585,50.02659 39.3761,0 74.14642,-19.86588 94.97405,-50.19149"
|
||||||
|
mask="url(#a)"
|
||||||
|
stroke="#0f1689"
|
||||||
|
stroke-width="20"
|
||||||
|
id="path7" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(11.958136)"
|
||||||
|
id="g11">
|
||||||
|
<g
|
||||||
|
fill="#0f1689"
|
||||||
|
mask="url(#b)"
|
||||||
|
id="g10">
|
||||||
|
<path
|
||||||
|
d="m 203.46068,95.687543 c 6.93631,0 12.5593,-14.80922 12.5593,-33.077318 0,-18.268097 -5.62299,-33.077317 -12.5593,-33.077317 -6.93631,0 -12.55931,14.80922 -12.55931,33.077317 0,18.268098 5.62299,33.077318 12.55931,33.077318 z"
|
||||||
|
transform="rotate(35,141.83137,150.32029)"
|
||||||
|
id="path8" />
|
||||||
|
<path
|
||||||
|
d="m 30.142322,95.687543 c 6.936311,0 12.559301,-14.80922 12.559301,-33.077318 0,-18.268097 -5.62299,-33.077317 -12.559301,-33.077317 -6.93631,0 -12.559301,14.80922 -12.559301,33.077317 0,18.268098 5.622991,33.077318 12.559301,33.077318 z"
|
||||||
|
transform="matrix(-0.81915204,0.57357644,0.57357644,0.81915204,58.084611,45.245308)"
|
||||||
|
id="path9" />
|
||||||
|
<path
|
||||||
|
d="m 116.73282,66.275268 c 6.93631,0 12.5593,-14.80922 12.5593,-33.077318 0,-18.268097 -5.62299,-33.07731673 -12.55931,-33.07731673 -6.93631,0 -12.5593,14.80921973 -12.5593,33.07731673 0,18.268098 5.62299,33.077318 12.55931,33.077318 z"
|
||||||
|
transform="matrix(-1,0,0,1,272.62852,51.211302)"
|
||||||
|
id="path10" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="m 251.46701,170.64039 c -20.23008,-33.60997 -56.88957,-56.06791 -98.75578,-56.06791 -40.7208,0 -76.515877,21.2459 -97.058696,53.33459 m 2.198111,129.16953 c 20.840303,30.2327 55.555905,50.02659 94.860585,50.02659 39.3761,0 74.14642,-19.86588 94.97405,-50.19149"
|
||||||
|
mask="url(#b)"
|
||||||
|
stroke="#0f1689"
|
||||||
|
stroke-width="20"
|
||||||
|
id="path11" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -37,27 +37,37 @@
|
|||||||
<section>
|
<section>
|
||||||
<h1>Kubernetes : délire d’Ops ?</h1>
|
<h1>Kubernetes : délire d’Ops ?</h1>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p>On vous l’a vendu comme un truc d’infra</p>
|
<p>On nous l’a surtout vendu comme un truc d’infra.<br />
|
||||||
|
<span class="blockquote-sub">Moi aussi, je suis passé par ce réflexe.</span>
|
||||||
|
</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Complexe</li>
|
<li>« Trop complexe »</li>
|
||||||
<li>Réservé aux DevOps</li>
|
<li>Réservé aux spécialistes plateforme</li>
|
||||||
<li>Magie noire</li>
|
<li>Magie noire / pas « mon » métier</li>
|
||||||
</ul>
|
</ul>
|
||||||
<aside class="notes">
|
<aside class="notes">
|
||||||
<p>On vous a vendu Kubernetes comme un délire d’infra, réservé à des gens chelous.<br />
|
<p>On nous présente souvent Kubernetes comme un délire d’infra, réservé à une poignée de spécialistes — j’ai
|
||||||
En réalité… c’est beaucoup plus proche de ce que vous faites déjà.</p>
|
eu
|
||||||
|
ce réflexe aussi, et je m’y retrouve quand j’entends encore ce discours.<br />
|
||||||
|
En réalité… c’est beaucoup plus proche de ce qu’on fait déjà en dev.</p>
|
||||||
<p><em>Chaos scripts</em> — dev entouré de scripts, crons, alertes, terminal chargé, ambiance tendue
|
<p><em>Chaos scripts</em> — dev entouré de scripts, crons, alertes, terminal chargé, ambiance tendue
|
||||||
(contraste avec la suite).</p>
|
(contraste avec la suite).</p>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
<section>
|
||||||
|
<blockquote>
|
||||||
|
<p>Kubernetes c'est un délire de dev</span>
|
||||||
|
</p>
|
||||||
|
</blockquote>
|
||||||
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h1>Kubernetes = API + état + boucle</h1>
|
<h1>Kubernetes = API + état + boucle</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li>API REST</li>
|
<li>API REST</li>
|
||||||
<li>Base d’état</li>
|
<li>Base d’état (etcd)</li>
|
||||||
<li>Boucle de réconciliation</li>
|
<li>Boucle de réconciliation → events</li>
|
||||||
<li>Pas de mystère : des mécanismes explicites</li>
|
<li>Pas de mystère → des mécanismes explicites (controllers, kubelet, containerd, etc.)</li>
|
||||||
</ul>
|
</ul>
|
||||||
<aside class="notes">
|
<aside class="notes">
|
||||||
<p>Kubernetes, c’est juste une API, une base d’état, et une boucle qui corrige la réalité.<br />
|
<p>Kubernetes, c’est juste une API, une base d’état, et une boucle qui corrige la réalité.<br />
|
||||||
@@ -66,13 +76,25 @@
|
|||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h1>De vos outils au noyau Linux</h1>
|
<h1>De vos outils au noyau Linux</h1>
|
||||||
<pre><code class="language-mermaid">flowchart LR
|
<pre><code class="language-mermaid">
|
||||||
A[Clients] --> B[kube-api]
|
flowchart LR
|
||||||
|
B[kube-api]
|
||||||
|
subgraph Clients
|
||||||
|
A01[kubectl]
|
||||||
|
A02@{ img: "img/helmsh-icon.svg", pos: "b", h: 44, constraint: "on" }
|
||||||
|
A03@{ img: "img/logo-kustomize.B5HO6GxI_1tyDev.webp", pos: "b", h: 36, constraint: "on" }
|
||||||
|
end
|
||||||
|
A01 --> B
|
||||||
|
A02 --> B
|
||||||
|
A03 --> B
|
||||||
|
D --> B
|
||||||
B --> C[etcd]
|
B --> C[etcd]
|
||||||
B --> D[Controllers]
|
B --> D[Controllers]
|
||||||
D --> E[kubelet]
|
D --> E[kubelet]
|
||||||
E --> F[containerd]
|
E --> F[containerd]
|
||||||
F --> G[Kernel Linux]
|
F --> G[Kernel Linux]
|
||||||
|
classDef logoBare fill:transparent,stroke:transparent,stroke-width:0px;
|
||||||
|
class A02,A03 logoBare;
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>kube-api</strong> — point d’entrée</li>
|
<li><strong>kube-api</strong> — point d’entrée</li>
|
||||||
@@ -91,34 +113,39 @@
|
|||||||
</code></pre>
|
</code></pre>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</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">
|
<section class="slide-boucle">
|
||||||
<div class="slide-boucle-inner">
|
<div class="slide-boucle-inner">
|
||||||
<h1 class="slide-boucle-title">État voulu → réel : la boucle</h1>
|
<h1 class="slide-boucle-title">Kubernetes ne fait pas tourner vos apps : la boucle de contrôle</h1>
|
||||||
|
<ul class="slide-boucle-lead">
|
||||||
|
<li>Il <strong>orchestre</strong> · il <strong>délègue</strong> · il <strong>observe</strong></li>
|
||||||
|
</ul>
|
||||||
<pre><code class="language-mermaid">flowchart LR
|
<pre><code class="language-mermaid">flowchart LR
|
||||||
A[Désiré] --> B[API] --> C[Controllers] --> D[Réel]
|
DS[Spec · désir] --> AP[kube-apiserver]
|
||||||
|
AP <-->|objets cluster| ET[(etcd)]
|
||||||
|
AP --> EV[Watch · événements]
|
||||||
|
EV --> CTRL[Controllers]
|
||||||
|
CTRL -->|réconciliation · requêtes API| AP
|
||||||
|
subgraph NODE[Nœud]
|
||||||
|
K[kubelet] -->|CRI| CD[containerd]
|
||||||
|
end
|
||||||
|
AP -->|spec Pods · synchro| K
|
||||||
|
K -->|status| AP
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p class="slide-boucle-foot">
|
<p class="slide-boucle-foot">
|
||||||
<strong>↻</strong> réinjecte vers les contrôleurs · <em>chaque tour : observer, décider, agir</em>
|
<strong>↻</strong> Les changements d’objets (Pods, etc.) vivent dans l’API ; le <strong>kubelet</strong> les
|
||||||
|
<strong>synchronise</strong> depuis l’API, puis traduit en appels <strong>CRI</strong> vers
|
||||||
|
<strong>containerd</strong>. Le <strong>status</strong> remonte par le même kubelet vers l’API.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<aside class="notes">
|
<aside class="notes">
|
||||||
<p>Des agents exécutent les instructions côté nœud (kubelet, runtime), mais le fil conducteur, c’est : état
|
<p>Aligné sur la doc Kubernetes (<em>Controllers</em>) : boucle de contrôle = observer l’état du cluster,
|
||||||
voulu, comparaison, correction en boucle.</p>
|
rapprocher le courant du <strong>spec</strong> désiré ; le plus souvent le controller envoie des messages au
|
||||||
|
<strong>kube-apiserver</strong> (création / mise à jour d’objets), d’où des effets de bord (Pods à planifier,
|
||||||
|
etc.). Les <strong>informers</strong> / watches alimentent des files d’<strong>événements</strong>.</p>
|
||||||
|
<p>etcd est la persistance derrière l’API ; le <strong>status</strong> reflète ce qui tourne vraiment et
|
||||||
|
repart dans la boucle. Le <strong>chemin des « commandes » vers containerd</strong> : mises à jour du
|
||||||
|
<strong>spec</strong> dans l’API → le kubelet (watch / liste des Pods du nœud) → <strong>CRI</strong> →
|
||||||
|
containerd. Le schéma du plan de contrôle côté masters reste API ↔ controllers ↔ API.</p>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
@@ -142,10 +169,10 @@
|
|||||||
<h1>Kubernetes vous évite de vivre dans le noyau</h1>
|
<h1>Kubernetes vous évite de vivre dans le noyau</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Manipuler les <strong>cgroups</strong> à la main</li>
|
<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>
|
<li>Gérer <strong>isolation</strong>, <strong>CPU</strong>, <strong>mémoire</strong> au niveau des noyaux de vos differents noeuds</li>
|
||||||
</ul>
|
</ul>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p>À côté de ça, un cluster qui râle, c’est presque du repos.</p>
|
<p>À côté de ça, un cluster qui râle, c'est presque du repos.</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<aside class="notes">
|
<aside class="notes">
|
||||||
<p>Si Kubernetes existe, c’est pour vous éviter de vivre dans ces couches-là.<br />
|
<p>Si Kubernetes existe, c’est pour vous éviter de vivre dans ces couches-là.<br />
|
||||||
@@ -221,13 +248,6 @@
|
|||||||
Même mécanisme, autre niveau d’abstraction.</p>
|
Même mécanisme, autre niveau d’abstraction.</p>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</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>
|
<section>
|
||||||
<h1>Sans opérateur : scripts, crons, humains dans la boucle</h1>
|
<h1>Sans opérateur : scripts, crons, humains dans la boucle</h1>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -347,6 +367,65 @@ spec:
|
|||||||
</ul>
|
</ul>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
<section>
|
||||||
|
<h1>Exemple Reflector : secret DB → namespace applicatif</h1>
|
||||||
|
<p><em>Le secret « source de vérité » vit avec la base ; Reflector recrée un miroir là où l’app tourne.</em></p>
|
||||||
|
<pre><code class="language-yaml"># Namespace database — secret maître (ex. mot de passe géré par l’opérateur / la DBA)
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: db-credentials
|
||||||
|
namespace: database
|
||||||
|
annotations:
|
||||||
|
reflector.v1.k8s.emberstack.com/reflection-allowed: "true"
|
||||||
|
reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true"
|
||||||
|
reflector.v1.k8s.emberstack.com/reflection-auto-namespaces: "app-shop"
|
||||||
|
stringData:
|
||||||
|
DATABASE_URL: "postgres://…"
|
||||||
|
</code></pre>
|
||||||
|
<ul>
|
||||||
|
<li>Reflector maintient un <strong>Secret du même nom</strong> dans <code>app-shop</code>, à jour quand la
|
||||||
|
source change.</li>
|
||||||
|
<li>Alternative : miroir manuel avec <code>reflector.v1.k8s.emberstack.com/reflects: "database/db-credentials"</code>.</li>
|
||||||
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<p>Scénario concret : l’équipe data garde le secret dans <code>database</code> ; l’app en
|
||||||
|
<code>app-shop</code> n’a pas besoin d’accès RBAC au namespace DB — elle consomme la <strong>copie</strong>
|
||||||
|
synchronisée.</p>
|
||||||
|
<p>Insister sur <code>reflection-auto-*</code> vs miroir explicite selon votre politique (Helm, GitOps…).</p>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h1>Exemple Reloader : rollout quand le secret miroir change</h1>
|
||||||
|
<p><em>Le Deployment monte le secret copié ; Reloader redémarre les Pods si le Secret (ou ConfigMap) référencé
|
||||||
|
est mis à jour.</em></p>
|
||||||
|
<pre><code class="language-yaml">apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: shop-api
|
||||||
|
namespace: app-shop
|
||||||
|
annotations:
|
||||||
|
reloader.stakater.com/auto: "true"
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: api
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: db-credentials # copie Reflector depuis database/
|
||||||
|
</code></pre>
|
||||||
|
<ul>
|
||||||
|
<li>Rotation du mot de passe dans <code>database/db-credentials</code> → Reflector met à jour
|
||||||
|
<code>app-shop/db-credentials</code> → Reloader déclenche un <strong>nouveau rollout</strong>.</li>
|
||||||
|
<li>Sans Reloader, les Pods gardent l’ancienne valeur en mémoire jusqu’à un redémarrage manuel.</li>
|
||||||
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<p>Enchaînement oral : (1) changement côté source, (2) miroir à jour, (3) Reloader voit la révision du Secret,
|
||||||
|
(4) rolling update. Mentionner les annotations plus fines (<code>secret.reloader.stakater.com/reload</code>,
|
||||||
|
etc.) si vous voulez limiter à un seul secret.</p>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h1>Cert-manager, Postgres, Keycloak : la spec devient une entité</h1>
|
<h1>Cert-manager, Postgres, Keycloak : la spec devient une entité</h1>
|
||||||
<table>
|
<table>
|
||||||
@@ -453,6 +532,51 @@ spec:
|
|||||||
</p>
|
</p>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
<section>
|
||||||
|
<h1>Quelle techno pour développer votre opérateur ?</h1>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Piste</th>
|
||||||
|
<th>Outils typiques</th>
|
||||||
|
<th>À retenir</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Go</strong> (référence)</td>
|
||||||
|
<td><strong>Kubebuilder</strong>, <code>controller-runtime</code>, client-go</td>
|
||||||
|
<td>Même patterns que l’upstream (<strong>reconcile</strong>, <strong>owner refs</strong>). Sous pic
|
||||||
|
d’événements : <strong>goroutines</strong> + files + <strong>back-off</strong> → peu de RAM en plus —
|
||||||
|
le Pod opérateur ne part pas en <strong>limite</strong> quand le cluster s’emballe.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Operator SDK</strong></td>
|
||||||
|
<td>CLI Red Hat · Go (souvent comme Kubebuilder) · <strong>Ansible</strong> / <strong>Helm</strong></td>
|
||||||
|
<td>Go = solide ; Ansible / Helm = wrapper playbooks / charts — court si la logique métier épaissit.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>Autres langages</strong></td>
|
||||||
|
<td>Python <strong>Kopf</strong>, Rust <strong>kube-rs</strong>, Java (Fabric8, Quarkus)…</td>
|
||||||
|
<td>Si <strong>équipe</strong> ou libs métier ; moins d’exemples tout faits côté core k8s.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p><em><strong>Go + controller-runtime</strong> pour un opérateur maison sérieux ; sinon : celui que l’équipe
|
||||||
|
<strong>livre et maintient</strong> le mieux.</em></p>
|
||||||
|
<p><em>Charge ↑ : un runtime qui gonfle la RAM par événement mange votre quota ; Go + <code>controller-runtime</code>,
|
||||||
|
non.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<p>Carte rapide : Kubebuilder / Operator SDK (Go) = même famille <code>controller-runtime</code>.</p>
|
||||||
|
<p><strong>Go & charge</strong> : goroutines, workqueues, back-off — pics d’événements sans RAM qui explose ;
|
||||||
|
éviter le Pod opérateur en <strong>OOM</strong> / limits alors que le cluster est juste chargé ailleurs.</p>
|
||||||
|
<p>Liens utiles : <a href="https://kubebuilder.io/">kubebuilder.io</a>,
|
||||||
|
<a href="https://sdk.operatorframework.io/">Operator SDK</a>,
|
||||||
|
<a href="https://kopf.readthedocs.io/">Kopf</a> (Python),
|
||||||
|
<a href="https://kube.rs/">kube-rs</a> (Rust).</p>
|
||||||
|
<p>Helm / Ansible : OK pour emballer du déploiement ; logique riche → souvent retour au <strong>Go</strong>.</p>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h1>Quand un opérateur est légitime</h1>
|
<h1>Quand un opérateur est légitime</h1>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -477,11 +601,22 @@ spec:
|
|||||||
selon ton design).</li>
|
selon ton design).</li>
|
||||||
<li>Intérêt conf : montrer le <strong>même schéma</strong> que les opérateurs « connus » : <em>observe →
|
<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>
|
compare → corrige</em> sur <strong>ton</strong> domaine métier.</li>
|
||||||
|
<li><strong>Incident ou compromission</strong> de secrets clients : un <strong>reload</strong> Keycloak
|
||||||
|
<strong>émet des événements</strong> côté IdP ; l’opérateur les <strong>interroge régulièrement</strong>
|
||||||
|
(ex. <strong>toutes les minutes</strong>), repère l’écart et <strong>propage</strong> la correction vers
|
||||||
|
l’état attendu dans le cluster.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<p><em>Exemple concret de CRD à la slide suivante.</em></p>
|
||||||
<aside class="notes">
|
<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
|
<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
|
: une API Kubernetes, un état désiré, et du code qui parle à Keycloak à la place d’un humain et d’un
|
||||||
runbook.</p>
|
runbook.</p>
|
||||||
|
<p><strong>Scénario oral</strong> : en cas d’<strong>incident</strong> ou de <strong>compromission</strong> de
|
||||||
|
credentials clients, tu forces un <strong>reload</strong> Keycloak — ça <strong>déclenche des événements</strong>
|
||||||
|
côté serveur. L’opérateur ne repose pas sur un webhook magique seul : il <strong>scrape / poll</strong> (dans
|
||||||
|
ton design : <strong>toutes les minutes</strong>) pour lire ces signaux ou resynchroniser, puis
|
||||||
|
<strong>réconcilie</strong> : Secrets / CRD à jour, rotation propagée, pas de dérive longue entre l’IdP et le
|
||||||
|
déclaratif cluster.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Lien repo</strong> : <code>https://…</code> (README, une spec d’exemple, capture d’un
|
<li><strong>Lien repo</strong> : <code>https://…</code> (README, une spec d’exemple, capture d’un
|
||||||
<code>kubectl get</code>).
|
<code>kubectl get</code>).
|
||||||
@@ -493,6 +628,57 @@ spec:
|
|||||||
style diagramme propre (flat design).</p>
|
style diagramme propre (flat design).</p>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
<section>
|
||||||
|
<h1>Exemple — <code>KeycloakClientCredential</code></h1>
|
||||||
|
<p><em>CRD</em> <code>secrets.carbogame.com/v1alpha1</code> — <code>spec.source</code> (realm, client) →
|
||||||
|
<code>spec.target</code> (Secret, rafraîchissement, mapping des clés).</p>
|
||||||
|
<pre><code class="language-yaml">apiVersion: secrets.carbogame.com/v1alpha1
|
||||||
|
kind: KeycloakClientCredential
|
||||||
|
metadata:
|
||||||
|
name: gitea-creds
|
||||||
|
namespace: keycloak
|
||||||
|
spec:
|
||||||
|
source:
|
||||||
|
keycloakServer: prod-keycloak
|
||||||
|
realm: operator
|
||||||
|
clientId: gitea
|
||||||
|
target:
|
||||||
|
secretName: gitea-secret-creds
|
||||||
|
namespace:
|
||||||
|
- gitea
|
||||||
|
refreshPeriod: 10m
|
||||||
|
fields:
|
||||||
|
clientId: key
|
||||||
|
clientSecret: secret
|
||||||
|
</code></pre>
|
||||||
|
<p><em>Extrait type ; en chart Helm, <code>namespace</code> et <code>keycloakServer</code> sont injectés via
|
||||||
|
<code>include "common.namespace" …</code>, <code>{{ .Values.global.env }}-keycloak</code>, etc.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<p>Parcourir le YAML : <code>source</code> = où lire côté Keycloak ; <code>target</code> = où écrire le Secret
|
||||||
|
et sous quelles clés.</p>
|
||||||
|
<p><code>spec.target.refreshPeriod</code> (ex. <code>10m</code>) cadence la resync vers le Secret ; la boucle
|
||||||
|
événements / poll (ex. minute) peut être un autre réglage — à distinguer en live si on pose la question.</p>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h1>Développer son opérateur : réutiliser les autres</h1>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Ne pas tout recoder</strong> : un opérateur maison peut s’appuyer sur des opérateurs / contrôleurs
|
||||||
|
existants pour ce qu’ils font déjà bien.</li>
|
||||||
|
<li>Ex. <strong>Secrets / ConfigMaps</strong> vers d’autres namespaces : plutôt que dupliquer la logique et une
|
||||||
|
<strong>deuxième config</strong> de ce qui est autorisé, brancher <strong>Reflector</strong> — une politique
|
||||||
|
de réflexion, <strong>un seul RBAC</strong> à raisonner (source vs cibles), moins de surface d’erreur.</li>
|
||||||
|
<li>Votre code se concentre sur <strong>le métier</strong> ; la diffusion transverse reste le problème de
|
||||||
|
l’outil prévu pour ça.</li>
|
||||||
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<p>Message : <strong>composition</strong> plutôt que réinventer Reflector dans votre binaire. Même idée pour
|
||||||
|
Reloader, External Secrets, etc. : chaque brique a déjà ses garde-fous et sa doc.</p>
|
||||||
|
<p>RBAC : si vous copiez vous-même des secrets entre namespaces, vous maintenez souvent <strong>deux</strong>
|
||||||
|
chemins (votre opérateur + la vérité côté Reflector). Avec Reflector seul pour la copie, une seule matrice
|
||||||
|
« qui peut refléter où ».</p>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h1>Ce qu’un opérateur bien choisi vous apporte</h1>
|
<h1>Ce qu’un opérateur bien choisi vous apporte</h1>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -587,11 +773,14 @@ spec:
|
|||||||
<li>Effets <strong>cachés</strong></li>
|
<li>Effets <strong>cachés</strong></li>
|
||||||
</ul>
|
</ul>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<p>Un opérateur mal conçu, c’est un microservice sous LSD.</p>
|
<p>Un opérateur mal conçu, c’est un microservice dont plus personne ne tient les règles du jeu.</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<aside class="notes">
|
<aside class="notes">
|
||||||
<p>Un mauvais opérateur, c’est de la complexité distribuée avec un debugger qui vous hait.<br />
|
<p>Un mauvais opérateur, c’est de la complexité distribuée et le genre de session debug qui nous tient
|
||||||
Le piège classique : « formation Kubernetes par la trappe » parce qu’on empile des comportements opaques.
|
éveillés à
|
||||||
|
3 h du matin — j’en ai croisé de très pénibles, et je ne suis pas toujours innocent de l’empilement.<br />
|
||||||
|
Le piège classique : « formation Kubernetes par la trappe » parce qu’on empile des comportements opaques —
|
||||||
|
souvent avec les meilleures intentions.
|
||||||
</p>
|
</p>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
@@ -701,7 +890,8 @@ spec:
|
|||||||
<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/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/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://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>
|
<!-- Image shape (flowchart) : Mermaid 11.3+ — https://mermaid.ai/open-source/syntax/flowchart.html#image-shape -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
if (typeof RevealNotes === "undefined" || typeof RevealHighlight === "undefined") {
|
if (typeof RevealNotes === "undefined" || typeof RevealHighlight === "undefined") {
|
||||||
@@ -745,7 +935,7 @@ spec:
|
|||||||
},
|
},
|
||||||
flowchart: {
|
flowchart: {
|
||||||
useMaxWidth: true,
|
useMaxWidth: true,
|
||||||
curve: "natural",
|
curve: "basis",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -773,6 +963,31 @@ spec:
|
|||||||
el.textContent = src;
|
el.textContent = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Chemins relatifs → absolus pour <image> SVG (forme « image » v11) et foreignObject le cas échéant. */
|
||||||
|
function fixMermaidImageUrls(slide) {
|
||||||
|
if (!slide) return;
|
||||||
|
var base = window.location.href;
|
||||||
|
function toAbs(url) {
|
||||||
|
if (!url || url.indexOf("data:") === 0) return null;
|
||||||
|
if (/^https?:\/\//i.test(url)) return null;
|
||||||
|
try {
|
||||||
|
return new URL(url, base).href;
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slide.querySelectorAll(".mermaid svg image").forEach(function (el) {
|
||||||
|
var u = el.getAttribute("href") || el.getAttribute("xlink:href");
|
||||||
|
var abs = toAbs(u);
|
||||||
|
if (abs) el.setAttribute("href", abs);
|
||||||
|
});
|
||||||
|
slide.querySelectorAll(".mermaid svg foreignObject img").forEach(function (img) {
|
||||||
|
var u = img.getAttribute("src");
|
||||||
|
var abs = toAbs(u);
|
||||||
|
if (abs) img.setAttribute("src", abs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function runMermaidInSlide(slide) {
|
function runMermaidInSlide(slide) {
|
||||||
if (!slide || typeof mermaid.run !== "function") return;
|
if (!slide || typeof mermaid.run !== "function") return;
|
||||||
slide.querySelectorAll(".mermaid").forEach(resetMermaidIfBroken);
|
slide.querySelectorAll(".mermaid").forEach(resetMermaidIfBroken);
|
||||||
@@ -780,7 +995,12 @@ spec:
|
|||||||
if (!pending.length) return;
|
if (!pending.length) return;
|
||||||
requestAnimationFrame(function () {
|
requestAnimationFrame(function () {
|
||||||
requestAnimationFrame(function () {
|
requestAnimationFrame(function () {
|
||||||
mermaid.run({ nodes: pending, suppressErrors: true }).catch(function () { });
|
var p = mermaid.run({ nodes: pending, suppressErrors: true });
|
||||||
|
var after = function () {
|
||||||
|
fixMermaidImageUrls(slide);
|
||||||
|
};
|
||||||
|
if (p && typeof p.then === "function") p.then(after).catch(after);
|
||||||
|
else after();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user