courses I and II wired: cascade + up-down split animations, fleet-labels move in VI, the library, C-II hero + manifest
This commit is contained in:
+155
-6
@@ -1,20 +1,114 @@
|
||||
<header class="masthead">
|
||||
<p class="eyebrow">The Exploded Cluster · The Delivery Arc</p>
|
||||
<h1>The machine was the easy part.<br><em>Now watch how software reaches it.</em></h1>
|
||||
<p class="lede">Three courses on the toolchain around the cluster: how change travels, how images
|
||||
are named, and how one definition serves a fleet. Scroll, and each machine comes apart.</p>
|
||||
<p class="lede">The foundations first - what an image is, what a cluster is - then the toolchain
|
||||
that delivers to them: how change travels, how images are named, and how one definition serves
|
||||
a fleet. Scroll, and each machine comes apart.</p>
|
||||
<p class="scrollcue" aria-hidden="true">scroll<span></span></p>
|
||||
</header>
|
||||
|
||||
<aside class="key" aria-label="reading key">
|
||||
<p class="kt">Reading key · this page stands alone - anything it leans on is listed here</p>
|
||||
<ul>
|
||||
<li><b>C-I</b><span>Docker: an image is a stack of frozen diffs. Referenced by Course VII.</span><i>course lands here soon</i></li>
|
||||
<li><b>C-II</b><span>Kubernetes: a cluster is a promise - the reconciliation loop. Referenced by Courses VI and VIII.</span><i>course lands here soon</i></li>
|
||||
<li><b>C-I</b><span>Docker: an image is a stack of frozen diffs. Referenced by Course VII.</span><a href="#c1">on this page</a></li>
|
||||
<li><b>C-II</b><span>Kubernetes: a cluster is a promise - the reconciliation loop. Referenced by Courses VI and VIII.</span><a href="#c2">on this page</a></li>
|
||||
<li><b>BLOG</b><span>Consistency by construction beats consistency by discipline - Course VIII's contexts move, lived on a real fleet.</span><a href="https://www.bztmon.com/blog/one-value-many-enforcers/">read it on bztmon.com</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<!-- ============ COURSE I ============ -->
|
||||
<section class="course" id="c1" data-scene="docker" aria-labelledby="c1h">
|
||||
<header class="chead">
|
||||
<p class="eyebrow">Course I · Docker</p>
|
||||
<h2 id="c1h" class="thesis">An image is not a box.<br><em>It is a stack of frozen diffs.</em></h2>
|
||||
<p class="lede">Scroll, and the thing you keep calling "a container image" comes apart in your
|
||||
hands. Four layers. Each one only stores what changed from the layer under it - and here they
|
||||
rise one at a time, bottom up.</p>
|
||||
</header>
|
||||
<div class="pin">
|
||||
<div class="viewport">
|
||||
<figure class="scene">
|
||||
<img class="flat" src="__HERO_DOCKER__"
|
||||
alt="An exploded isometric view of a container image: four stacked slabs floating
|
||||
apart - a heavy metal base, a circuit-etched dependency layer, a magenta-traced
|
||||
code layer, and a thin frosted-glass writable layer on top.">
|
||||
</figure>
|
||||
<ol class="legend"></ol>
|
||||
</div>
|
||||
<div class="bench"></div>
|
||||
</div>
|
||||
<div class="prose">
|
||||
<h3>A stack of diffs wearing a name</h3>
|
||||
<p>An image is not a copy of a machine. It is a stack of read-only layers, each recording only
|
||||
what changed from the one beneath. The base OS is pulled once and shared by every image on
|
||||
the host; your dependencies sit on it; your code - usually the smallest layer, always the
|
||||
most volatile - sits above that; and the writable layer is created fresh per container and
|
||||
thrown away when it dies. Nothing you write there survives.</p>
|
||||
<p class="sayit">Change a layer and every layer above it must be rebuilt.</p>
|
||||
<h3>The order is a caching decision</h3>
|
||||
<p>The builder caches layer by layer, and a cached layer survives only while everything
|
||||
beneath it is unchanged. Put <span class="mono">COPY . .</span> above your dependency
|
||||
install and you have told the builder to throw the dependency cache away every time one
|
||||
line of code changes. Dependencies first, code last - a Dockerfile is a cache policy that
|
||||
happens to build software.</p>
|
||||
<p class="fieldnote"><b>Field note.</b> A rebuild that takes twenty minutes and one that
|
||||
takes twenty seconds are usually the same Dockerfile with the lines swapped.</p>
|
||||
<p class="closeline">This stack explains the whole ecosystem above it: sharing explains why
|
||||
pulls are fast, immutability explains why a digest can name the exact bytes (Course VII),
|
||||
and the throwaway top layer explains why state needs volumes. One idea - frozen diffs -
|
||||
all the way down.</p>
|
||||
<p class="reads"><b>Pre-reads:</b> none - start here
|
||||
 <b>Further:</b> <a href="https://docs.docker.com/get-started/">Docker get-started</a> ·
|
||||
<a href="https://docs.docker.com/build/">Docker build + layers</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============ COURSE II ============ -->
|
||||
<section class="course" id="c2" data-scene="cluster" aria-labelledby="c2h">
|
||||
<header class="chead">
|
||||
<p class="eyebrow">Course II · Kubernetes</p>
|
||||
<h2 id="c2h" class="thesis">A cluster is a promise,<br><em>not a place.</em></h2>
|
||||
<p class="lede">You never tell Kubernetes <i>how</i> to run your app. You describe what you
|
||||
want - declarative intent - and the cluster works, forever, to make it true. Scroll, and
|
||||
the formation splits: the half that decides rises, the halves that run spread below.</p>
|
||||
</header>
|
||||
<div class="pin">
|
||||
<div class="viewport">
|
||||
<figure class="scene">
|
||||
<img class="flat" src="__HERO_CLUSTER__"
|
||||
alt="A small fleet mid-explosion: one wide command slab with a strong cyan seam
|
||||
hovering above a row of three identical worker blocks, all floating apart in
|
||||
the void.">
|
||||
</figure>
|
||||
<ol class="legend"></ol>
|
||||
</div>
|
||||
<div class="bench"></div>
|
||||
</div>
|
||||
<div class="prose">
|
||||
<h3>Desired against actual, forever</h3>
|
||||
<p>The habit underneath everything: the <b>reconciliation loop</b> - compare desired state
|
||||
against actual state, fix the difference, repeat. That <span class="mono">kubectl apply</span>
|
||||
didn't launch anything; it filed paperwork. The machine took it from there, and it never
|
||||
stops taking it from there: kill a pod and it returns, not because something noticed the
|
||||
crash but because the loop noticed the difference.</p>
|
||||
<p class="sayit">Kubernetes doesn't run your app - it reconciles it.</p>
|
||||
<h3>One half decides, one half runs</h3>
|
||||
<p>The split in the scene is the split that makes everything else possible: a <b>control
|
||||
plane</b> that decides - holds the truth, schedules, reconciles - and <b>worker nodes</b>
|
||||
that run pods. The workers are deliberately interchangeable: identical, replaceable, cattle
|
||||
from day one. Authority does not live where the work happens.</p>
|
||||
<p class="fieldnote"><b>Field note.</b> If you are SSHing into nodes to "fix" things, you
|
||||
are arm-wrestling the reconciler - and it does not get tired. Change the desired state
|
||||
instead.</p>
|
||||
<p class="closeline">Everything the delivery arc teaches from Course VI onward is this loop
|
||||
wearing bigger clothes - git as the desired state, whole fleets as reconciled objects.
|
||||
Learn the promise once; it repeats all the way up.</p>
|
||||
<p class="reads"><b>Pre-reads:</b> C-I
|
||||
 <b>Further:</b> <a href="https://kubernetes.io/docs/concepts/overview/">Kubernetes overview</a> ·
|
||||
<a href="https://kubernetes.io/docs/concepts/architecture/">cluster architecture</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ============ COURSE VI ============ -->
|
||||
<section class="course" id="c6" data-scene="gitops" aria-labelledby="c6h">
|
||||
<header class="chead">
|
||||
@@ -55,6 +149,15 @@
|
||||
<p class="fieldnote"><b>Field note.</b> Fighting the reconciler over SSH is arm-wrestling a
|
||||
machine that does not get tired - C-II's warning, now with a face. The fix is always
|
||||
upstream, in git.</p>
|
||||
<h3>At fleet scale, the label is the deploy button</h3>
|
||||
<p>Run many OpenShift clusters under a hub - RHACM, with edge clusters arriving through
|
||||
zero-touch provisioning - and nobody applies apps to clusters by hand. Each app carries a
|
||||
<b>Placement</b> that selects cluster <b>labels</b>; the hub matches placements against the
|
||||
labels a cluster wears, and the chosen cluster pulls its assignment. Labelling the cluster
|
||||
<i>is</i> the deploy action: attach the label and the app follows, remove it and the app
|
||||
leaves. The same pull model as above, one level bigger - a cluster's labels are its
|
||||
entitlements, reconciled like everything else.</p>
|
||||
<p class="sayit">Label the cluster; the app follows.</p>
|
||||
<h3>The one thing git never holds</h3>
|
||||
<p>Git holds everything except secrets - a secret in git is published, forever, to everyone who
|
||||
ever clones. So the pattern splits the reference from the value: git carries an ExternalSecret
|
||||
@@ -275,6 +378,40 @@
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<section class="course" id="library" aria-labelledby="libh">
|
||||
<header class="chead">
|
||||
<p class="eyebrow">The library</p>
|
||||
<h2 id="libh" class="thesis">Go to <em>the sources.</em></h2>
|
||||
<p class="lede">Every technology this site teaches, one sentence each, official documentation
|
||||
only.</p>
|
||||
</header>
|
||||
<ul class="library">
|
||||
<li><b>Kubernetes</b><span>The container orchestrator underneath everything here: declarative
|
||||
state, the reconciliation loop, pods, services and the machinery that keeps promises.</span>
|
||||
<a href="https://kubernetes.io/docs/">kubernetes.io/docs</a></li>
|
||||
<li><b>Helm</b><span>The package manager and Go-template engine that turns one chart plus a
|
||||
per-cluster values file into the rendered manifests a cluster actually runs.</span>
|
||||
<a href="https://helm.sh/docs/">helm.sh/docs</a></li>
|
||||
<li><b>Red Hat OpenShift</b><span>Kubernetes as an opinionated, supported distribution -
|
||||
SCCs, Routes and Operators, plus the fleet tooling (RHACM, zero-touch provisioning) that
|
||||
assigns apps to clusters by label.</span>
|
||||
<a href="https://docs.redhat.com/en/documentation/openshift_container_platform">docs.redhat.com</a></li>
|
||||
<li><b>Argo CD</b><span>The GitOps reconciler that watches a repository and keeps the cluster
|
||||
converged on what it finds there - the pull model that means nothing ever pushes to
|
||||
production.</span>
|
||||
<a href="https://argo-cd.readthedocs.io/">argo-cd.readthedocs.io</a></li>
|
||||
<li><b>GitHub Actions</b><span>CI that builds and tests on every push - in this flow it makes
|
||||
artefacts and proposes changes, and never holds a credential that can touch a cluster.</span>
|
||||
<a href="https://docs.github.com/en/actions">docs.github.com/actions</a></li>
|
||||
<li><b>Azure Key Vault</b><span>The managed vault holding secret values, so git carries only
|
||||
references and rotation never has to become a commit.</span>
|
||||
<a href="https://learn.microsoft.com/azure/key-vault/">learn.microsoft.com/key-vault</a></li>
|
||||
<li><b>Azure Container Registry</b><span>The managed upstream registry CI pushes to - where
|
||||
the digest is minted that everything downstream pins and promotes.</span>
|
||||
<a href="https://learn.microsoft.com/azure/container-registry/">learn.microsoft.com/container-registry</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<footer class="outro">
|
||||
<p>The Exploded Cluster · the delivery arc · every scene is one generated hero,
|
||||
sliced and scrubbed - no diagrams were harmed.</p>
|
||||
@@ -318,6 +455,15 @@ b{color:var(--ink)} .mono{font-family:var(--mono);font-size:.9em;color:var(--cya
|
||||
.key a{font-family:var(--mono);font-size:.66rem;letter-spacing:.06em;white-space:nowrap}
|
||||
@media (max-width:700px){.key li{grid-template-columns:44px 1fr}.key i,.key a{grid-column:2}}
|
||||
|
||||
.library{max-width:860px;margin:2vh auto 6vh;padding:0 24px;list-style:none;
|
||||
display:flex;flex-direction:column;gap:12px}
|
||||
.library li{display:grid;grid-template-columns:190px 1fr auto;gap:16px;align-items:baseline;
|
||||
border:1px solid var(--line);border-radius:8px;padding:14px 18px;background:#0a1020}
|
||||
.library b{font-family:var(--mono);font-size:.78rem;letter-spacing:.06em;color:var(--cyan)}
|
||||
.library span{font-size:.85rem;line-height:1.6;color:var(--dim)}
|
||||
.library a{font-family:var(--mono);font-size:.68rem;letter-spacing:.04em;white-space:nowrap}
|
||||
@media (max-width:760px){.library li{grid-template-columns:1fr}.library a{justify-self:start}}
|
||||
|
||||
.blueprint{max-width:1220px;margin:2vh auto 4vh;padding:0 24px}
|
||||
.blueprint img{width:100%;height:auto;display:block;border-radius:12px}
|
||||
|
||||
@@ -384,7 +530,7 @@ body:not(.fx) .bench{display:none}
|
||||
<script>
|
||||
(() => {
|
||||
"use strict";
|
||||
const SCENES = { gitops: __MAN_GITOPS__, supply: __MAN_SUPPLY__, helm: __MAN_HELM__ };
|
||||
const SCENES = { docker: __MAN_DOCKER__, cluster: __MAN_CLUSTER__, gitops: __MAN_GITOPS__, supply: __MAN_SUPPLY__, helm: __MAN_HELM__ };
|
||||
const RM = matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
const units = [];
|
||||
|
||||
@@ -419,9 +565,12 @@ body:not(.fx) .bench{display:none}
|
||||
const lis = [...u.legend.children];
|
||||
const n = lis.length;
|
||||
u.apply = t => {
|
||||
// stagger > 0 = cascade: parts complete their travel one after another in list order
|
||||
const sg = u.man.stagger || 0;
|
||||
u.man.parts.forEach((p, i) => {
|
||||
const pt = sg ? Math.min(1, Math.max(0, t * (1 + sg * (n - 1)) - sg * i)) : t;
|
||||
u.slabs[i].style.transform =
|
||||
"translate3d(" + (p.dx * (1 - t)).toFixed(3) + "%," + (p.dy * (1 - t)).toFixed(3) + "%,0)";
|
||||
"translate3d(" + (p.dx * (1 - pt)).toFixed(3) + "%," + (p.dy * (1 - pt)).toFixed(3) + "%,0)";
|
||||
});
|
||||
u.scene.style.transform = "scale(" + (0.95 + 0.05 * t).toFixed(4) + ")";
|
||||
// the side list lights in reading order, top to bottom, as the scrub advances
|
||||
|
||||
Reference in New Issue
Block a user