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:
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
@@ -10,11 +10,13 @@ trap 'printf "build failed at line %s\n" "$LINENO" >&2' ERR
|
|||||||
here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
cd -- "$here"
|
cd -- "$here"
|
||||||
|
|
||||||
for f in pilot/arc.tpl.html pilot/manifest-gitops.json pilot/manifest-supply.json \
|
for f in pilot/arc.tpl.html pilot/manifest-docker.json pilot/manifest-cluster.json \
|
||||||
|
pilot/manifest-gitops.json pilot/manifest-supply.json \
|
||||||
pilot/manifest-helm.json gsap.min.js st.min.js \
|
pilot/manifest-helm.json gsap.min.js st.min.js \
|
||||||
assets/dist/course-VI-gitops.webp assets/dist/course-VII-supply-chain.webp \
|
assets/dist/course-VI-gitops.webp assets/dist/course-VII-supply-chain.webp \
|
||||||
assets/dist/course-VIII-helm-press.webp assets/dist/appendix-dependency-ledger.webp \
|
assets/dist/course-VIII-helm-press.webp assets/dist/appendix-dependency-ledger.webp \
|
||||||
assets/dist/delivery-flow.svg; do
|
assets/dist/delivery-flow.svg assets/dist/docker-layers.webp \
|
||||||
|
assets/dist/course-II-cluster.webp; do
|
||||||
[[ -f "$f" ]] || { printf 'missing source: %s\n' "$f" >&2; exit 1; }
|
[[ -f "$f" ]] || { printf 'missing source: %s\n' "$f" >&2; exit 1; }
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
+155
-6
@@ -1,20 +1,114 @@
|
|||||||
<header class="masthead">
|
<header class="masthead">
|
||||||
<p class="eyebrow">The Exploded Cluster · The Delivery Arc</p>
|
<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>
|
<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
|
<p class="lede">The foundations first - what an image is, what a cluster is - then the toolchain
|
||||||
are named, and how one definition serves a fleet. Scroll, and each machine comes apart.</p>
|
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>
|
<p class="scrollcue" aria-hidden="true">scroll<span></span></p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<aside class="key" aria-label="reading key">
|
<aside class="key" aria-label="reading key">
|
||||||
<p class="kt">Reading key · this page stands alone - anything it leans on is listed here</p>
|
<p class="kt">Reading key · this page stands alone - anything it leans on is listed here</p>
|
||||||
<ul>
|
<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-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><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><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>
|
<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>
|
</ul>
|
||||||
</aside>
|
</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 ============ -->
|
<!-- ============ COURSE VI ============ -->
|
||||||
<section class="course" id="c6" data-scene="gitops" aria-labelledby="c6h">
|
<section class="course" id="c6" data-scene="gitops" aria-labelledby="c6h">
|
||||||
<header class="chead">
|
<header class="chead">
|
||||||
@@ -55,6 +149,15 @@
|
|||||||
<p class="fieldnote"><b>Field note.</b> Fighting the reconciler over SSH is arm-wrestling a
|
<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
|
machine that does not get tired - C-II's warning, now with a face. The fix is always
|
||||||
upstream, in git.</p>
|
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>
|
<h3>The one thing git never holds</h3>
|
||||||
<p>Git holds everything except secrets - a secret in git is published, forever, to everyone who
|
<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
|
ever clones. So the pattern splits the reference from the value: git carries an ExternalSecret
|
||||||
@@ -275,6 +378,40 @@
|
|||||||
</figure>
|
</figure>
|
||||||
</section>
|
</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">
|
<footer class="outro">
|
||||||
<p>The Exploded Cluster · the delivery arc · every scene is one generated hero,
|
<p>The Exploded Cluster · the delivery arc · every scene is one generated hero,
|
||||||
sliced and scrubbed - no diagrams were harmed.</p>
|
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}
|
.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}}
|
@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{max-width:1220px;margin:2vh auto 4vh;padding:0 24px}
|
||||||
.blueprint img{width:100%;height:auto;display:block;border-radius:12px}
|
.blueprint img{width:100%;height:auto;display:block;border-radius:12px}
|
||||||
|
|
||||||
@@ -384,7 +530,7 @@ body:not(.fx) .bench{display:none}
|
|||||||
<script>
|
<script>
|
||||||
(() => {
|
(() => {
|
||||||
"use strict";
|
"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 RM = matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||||
const units = [];
|
const units = [];
|
||||||
|
|
||||||
@@ -419,9 +565,12 @@ body:not(.fx) .bench{display:none}
|
|||||||
const lis = [...u.legend.children];
|
const lis = [...u.legend.children];
|
||||||
const n = lis.length;
|
const n = lis.length;
|
||||||
u.apply = t => {
|
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) => {
|
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 =
|
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) + ")";
|
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
|
// the side list lights in reading order, top to bottom, as the scrub advances
|
||||||
|
|||||||
@@ -19,12 +19,16 @@ const i = tpl.lastIndexOf("<script>");
|
|||||||
const j = tpl.lastIndexOf("</script>");
|
const j = tpl.lastIndexOf("</script>");
|
||||||
if (i < 0 || j < i) throw new Error("app script block not found");
|
if (i < 0 || j < i) throw new Error("app script block not found");
|
||||||
let app = tpl.slice(i + 8, j)
|
let app = tpl.slice(i + 8, j)
|
||||||
|
.replace("__MAN_DOCKER__", man("docker"))
|
||||||
|
.replace("__MAN_CLUSTER__", man("cluster"))
|
||||||
.replace("__MAN_GITOPS__", man("gitops"))
|
.replace("__MAN_GITOPS__", man("gitops"))
|
||||||
.replace("__MAN_SUPPLY__", man("supply"))
|
.replace("__MAN_SUPPLY__", man("supply"))
|
||||||
.replace("__MAN_HELM__", man("helm"));
|
.replace("__MAN_HELM__", man("helm"));
|
||||||
let body = tpl.slice(0, i) + tpl.slice(j + 9);
|
let body = tpl.slice(0, i) + tpl.slice(j + 9);
|
||||||
|
|
||||||
const heroes = {
|
const heroes = {
|
||||||
|
__HERO_DOCKER__: "docker-layers.webp",
|
||||||
|
__HERO_CLUSTER__: "course-II-cluster.webp",
|
||||||
__HERO_GITOPS__: "course-VI-gitops.webp",
|
__HERO_GITOPS__: "course-VI-gitops.webp",
|
||||||
__HERO_SUPPLY__: "course-VII-supply-chain.webp",
|
__HERO_SUPPLY__: "course-VII-supply-chain.webp",
|
||||||
__HERO_HELM__: "course-VIII-helm-press.webp",
|
__HERO_HELM__: "course-VIII-helm-press.webp",
|
||||||
|
|||||||
@@ -16,9 +16,13 @@ def man(tag):
|
|||||||
separators=(",", ":"))
|
separators=(",", ":"))
|
||||||
|
|
||||||
out = (ROOT / "pilot/arc.tpl.html").read_text() \
|
out = (ROOT / "pilot/arc.tpl.html").read_text() \
|
||||||
|
.replace("__MAN_DOCKER__", man("docker")) \
|
||||||
|
.replace("__MAN_CLUSTER__", man("cluster")) \
|
||||||
.replace("__MAN_GITOPS__", man("gitops")) \
|
.replace("__MAN_GITOPS__", man("gitops")) \
|
||||||
.replace("__MAN_SUPPLY__", man("supply")) \
|
.replace("__MAN_SUPPLY__", man("supply")) \
|
||||||
.replace("__MAN_HELM__", man("helm")) \
|
.replace("__MAN_HELM__", man("helm")) \
|
||||||
|
.replace("__HERO_DOCKER__", b64("docker-layers.webp")) \
|
||||||
|
.replace("__HERO_CLUSTER__", b64("course-II-cluster.webp")) \
|
||||||
.replace("__HERO_GITOPS__", b64("course-VI-gitops.webp")) \
|
.replace("__HERO_GITOPS__", b64("course-VI-gitops.webp")) \
|
||||||
.replace("__HERO_SUPPLY__", b64("course-VII-supply-chain.webp")) \
|
.replace("__HERO_SUPPLY__", b64("course-VII-supply-chain.webp")) \
|
||||||
.replace("__HERO_HELM__", b64("course-VIII-helm-press.webp")) \
|
.replace("__HERO_HELM__", b64("course-VIII-helm-press.webp")) \
|
||||||
|
|||||||
@@ -0,0 +1,439 @@
|
|||||||
|
{
|
||||||
|
"scene": "cluster",
|
||||||
|
"source": "assets/raw/course-II-cluster.jpeg",
|
||||||
|
"parts": [
|
||||||
|
{
|
||||||
|
"name": "command",
|
||||||
|
"title": "The control plane",
|
||||||
|
"blurb": "Decides - holds the truth, schedules, reconciles. The only authority in the room.",
|
||||||
|
"z": 0,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
70.1,
|
||||||
|
23.15
|
||||||
|
],
|
||||||
|
[
|
||||||
|
70.02,
|
||||||
|
28.95
|
||||||
|
],
|
||||||
|
[
|
||||||
|
68.86,
|
||||||
|
30.8
|
||||||
|
],
|
||||||
|
[
|
||||||
|
63.92,
|
||||||
|
34.78
|
||||||
|
],
|
||||||
|
[
|
||||||
|
55.0,
|
||||||
|
42.6
|
||||||
|
],
|
||||||
|
[
|
||||||
|
51.76,
|
||||||
|
44.47
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49.56,
|
||||||
|
45.13
|
||||||
|
],
|
||||||
|
[
|
||||||
|
47.87,
|
||||||
|
44.2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
43.26,
|
||||||
|
41.53
|
||||||
|
],
|
||||||
|
[
|
||||||
|
32.23,
|
||||||
|
31.68
|
||||||
|
],
|
||||||
|
[
|
||||||
|
30.85,
|
||||||
|
30.52
|
||||||
|
],
|
||||||
|
[
|
||||||
|
29.98,
|
||||||
|
29.1
|
||||||
|
],
|
||||||
|
[
|
||||||
|
29.83,
|
||||||
|
27.54
|
||||||
|
],
|
||||||
|
[
|
||||||
|
30.63,
|
||||||
|
21.55
|
||||||
|
],
|
||||||
|
[
|
||||||
|
47.43,
|
||||||
|
9.27
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49.73,
|
||||||
|
8.07
|
||||||
|
],
|
||||||
|
[
|
||||||
|
50.71,
|
||||||
|
8.2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
68.5,
|
||||||
|
20.84
|
||||||
|
],
|
||||||
|
[
|
||||||
|
69.3,
|
||||||
|
21.57
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"dx": 0.0,
|
||||||
|
"dy": 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "worker-1",
|
||||||
|
"title": "Worker node",
|
||||||
|
"blurb": "Runs pods. Identical and replaceable by design - cattle, not pets.",
|
||||||
|
"z": 0,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
32.59,
|
||||||
|
76.73
|
||||||
|
],
|
||||||
|
[
|
||||||
|
30.36,
|
||||||
|
76.47
|
||||||
|
],
|
||||||
|
[
|
||||||
|
23.8,
|
||||||
|
71.93
|
||||||
|
],
|
||||||
|
[
|
||||||
|
23.29,
|
||||||
|
71.16
|
||||||
|
],
|
||||||
|
[
|
||||||
|
22.71,
|
||||||
|
69.64
|
||||||
|
],
|
||||||
|
[
|
||||||
|
22.56,
|
||||||
|
68.69
|
||||||
|
],
|
||||||
|
[
|
||||||
|
22.35,
|
||||||
|
51.98
|
||||||
|
],
|
||||||
|
[
|
||||||
|
22.42,
|
||||||
|
49.83
|
||||||
|
],
|
||||||
|
[
|
||||||
|
22.56,
|
||||||
|
49.04
|
||||||
|
],
|
||||||
|
[
|
||||||
|
23.73,
|
||||||
|
46.8
|
||||||
|
],
|
||||||
|
[
|
||||||
|
30.59,
|
||||||
|
42.2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
31.6,
|
||||||
|
41.67
|
||||||
|
],
|
||||||
|
[
|
||||||
|
32.52,
|
||||||
|
41.8
|
||||||
|
],
|
||||||
|
[
|
||||||
|
33.62,
|
||||||
|
42.47
|
||||||
|
],
|
||||||
|
[
|
||||||
|
39.21,
|
||||||
|
46.19
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.01,
|
||||||
|
47.18
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.88,
|
||||||
|
49.36
|
||||||
|
],
|
||||||
|
[
|
||||||
|
41.03,
|
||||||
|
52.59
|
||||||
|
],
|
||||||
|
[
|
||||||
|
41.1,
|
||||||
|
68.65
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.95,
|
||||||
|
69.59
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.37,
|
||||||
|
71.1
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.01,
|
||||||
|
71.66
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"dx": 11.06,
|
||||||
|
"dy": -19.39
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "worker-2",
|
||||||
|
"title": "Worker node",
|
||||||
|
"blurb": "Interchangeable with its neighbours - lose one and the promise re-lands its pods elsewhere.",
|
||||||
|
"z": 0,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
59.78,
|
||||||
|
62.5
|
||||||
|
],
|
||||||
|
[
|
||||||
|
59.78,
|
||||||
|
74.77
|
||||||
|
],
|
||||||
|
[
|
||||||
|
59.7,
|
||||||
|
81.75
|
||||||
|
],
|
||||||
|
[
|
||||||
|
59.63,
|
||||||
|
82.22
|
||||||
|
],
|
||||||
|
[
|
||||||
|
59.56,
|
||||||
|
82.53
|
||||||
|
],
|
||||||
|
[
|
||||||
|
58.83,
|
||||||
|
84.19
|
||||||
|
],
|
||||||
|
[
|
||||||
|
58.54,
|
||||||
|
84.56
|
||||||
|
],
|
||||||
|
[
|
||||||
|
52.87,
|
||||||
|
88.87
|
||||||
|
],
|
||||||
|
[
|
||||||
|
51.1,
|
||||||
|
90.07
|
||||||
|
],
|
||||||
|
[
|
||||||
|
50.85,
|
||||||
|
90.2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49.12,
|
||||||
|
90.2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48.87,
|
||||||
|
90.07
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48.29,
|
||||||
|
89.67
|
||||||
|
],
|
||||||
|
[
|
||||||
|
41.39,
|
||||||
|
84.65
|
||||||
|
],
|
||||||
|
[
|
||||||
|
41.1,
|
||||||
|
84.27
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.44,
|
||||||
|
82.76
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.3,
|
||||||
|
82.12
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.22,
|
||||||
|
81.65
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.15,
|
||||||
|
72.96
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.15,
|
||||||
|
62.14
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.22,
|
||||||
|
61.52
|
||||||
|
],
|
||||||
|
[
|
||||||
|
40.37,
|
||||||
|
60.89
|
||||||
|
],
|
||||||
|
[
|
||||||
|
41.02,
|
||||||
|
59.38
|
||||||
|
],
|
||||||
|
[
|
||||||
|
41.24,
|
||||||
|
59.02
|
||||||
|
],
|
||||||
|
[
|
||||||
|
41.53,
|
||||||
|
58.63
|
||||||
|
],
|
||||||
|
[
|
||||||
|
45.13,
|
||||||
|
56.07
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49.37,
|
||||||
|
53.4
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49.7,
|
||||||
|
53.27
|
||||||
|
],
|
||||||
|
[
|
||||||
|
50.28,
|
||||||
|
53.27
|
||||||
|
],
|
||||||
|
[
|
||||||
|
50.61,
|
||||||
|
53.4
|
||||||
|
],
|
||||||
|
[
|
||||||
|
54.86,
|
||||||
|
56.07
|
||||||
|
],
|
||||||
|
[
|
||||||
|
58.54,
|
||||||
|
58.92
|
||||||
|
],
|
||||||
|
[
|
||||||
|
58.76,
|
||||||
|
59.27
|
||||||
|
],
|
||||||
|
[
|
||||||
|
59.63,
|
||||||
|
61.26
|
||||||
|
],
|
||||||
|
[
|
||||||
|
59.7,
|
||||||
|
61.73
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"dx": 0.28,
|
||||||
|
"dy": -26.71
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "worker-3",
|
||||||
|
"title": "Worker node",
|
||||||
|
"blurb": "The fleet grows by adding more of exactly this.",
|
||||||
|
"z": 0,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
58.9,
|
||||||
|
69.09
|
||||||
|
],
|
||||||
|
[
|
||||||
|
58.97,
|
||||||
|
50.11
|
||||||
|
],
|
||||||
|
[
|
||||||
|
59.19,
|
||||||
|
49.18
|
||||||
|
],
|
||||||
|
[
|
||||||
|
60.21,
|
||||||
|
47.22
|
||||||
|
],
|
||||||
|
[
|
||||||
|
66.51,
|
||||||
|
42.2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
67.2,
|
||||||
|
41.8
|
||||||
|
],
|
||||||
|
[
|
||||||
|
68.05,
|
||||||
|
41.67
|
||||||
|
],
|
||||||
|
[
|
||||||
|
68.73,
|
||||||
|
41.93
|
||||||
|
],
|
||||||
|
[
|
||||||
|
73.28,
|
||||||
|
44.6
|
||||||
|
],
|
||||||
|
[
|
||||||
|
76.2,
|
||||||
|
46.67
|
||||||
|
],
|
||||||
|
[
|
||||||
|
77.22,
|
||||||
|
48.69
|
||||||
|
],
|
||||||
|
[
|
||||||
|
77.44,
|
||||||
|
49.51
|
||||||
|
],
|
||||||
|
[
|
||||||
|
77.58,
|
||||||
|
50.94
|
||||||
|
],
|
||||||
|
[
|
||||||
|
77.51,
|
||||||
|
58.91
|
||||||
|
],
|
||||||
|
[
|
||||||
|
77.36,
|
||||||
|
69.28
|
||||||
|
],
|
||||||
|
[
|
||||||
|
77.14,
|
||||||
|
70.44
|
||||||
|
],
|
||||||
|
[
|
||||||
|
75.71,
|
||||||
|
72.2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
70.98,
|
||||||
|
75.4
|
||||||
|
],
|
||||||
|
[
|
||||||
|
69.0,
|
||||||
|
76.73
|
||||||
|
],
|
||||||
|
[
|
||||||
|
67.19,
|
||||||
|
76.73
|
||||||
|
],
|
||||||
|
[
|
||||||
|
59.92,
|
||||||
|
71.54
|
||||||
|
],
|
||||||
|
[
|
||||||
|
59.05,
|
||||||
|
69.71
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"dx": -11.12,
|
||||||
|
"dy": -19.34
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stagger": 0.28
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
{
|
||||||
|
"scene": "docker",
|
||||||
|
"source": "assets/dist/docker-layers.webp",
|
||||||
|
"stagger": 0.35,
|
||||||
|
"parts": [
|
||||||
|
{
|
||||||
|
"name": "base",
|
||||||
|
"title": "Base image",
|
||||||
|
"blurb": "The OS layer everything else is stacked on. Pull it once, share it across every image on the host.",
|
||||||
|
"z": 0,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
55.66
|
||||||
|
],
|
||||||
|
[
|
||||||
|
27.88,
|
||||||
|
55.66
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49.48,
|
||||||
|
75.51
|
||||||
|
],
|
||||||
|
[
|
||||||
|
71.88,
|
||||||
|
55.6
|
||||||
|
],
|
||||||
|
[
|
||||||
|
100,
|
||||||
|
55.6
|
||||||
|
],
|
||||||
|
[
|
||||||
|
100,
|
||||||
|
100
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
100
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"dx": 0.0,
|
||||||
|
"dy": 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "deps",
|
||||||
|
"title": "Dependencies",
|
||||||
|
"blurb": "Your packages and runtime. Change these and every layer above has to be rebuilt.",
|
||||||
|
"z": 0,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
40.64
|
||||||
|
],
|
||||||
|
[
|
||||||
|
28.38,
|
||||||
|
40.64
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49.76,
|
||||||
|
55.45
|
||||||
|
],
|
||||||
|
[
|
||||||
|
71.5,
|
||||||
|
40.32
|
||||||
|
],
|
||||||
|
[
|
||||||
|
100,
|
||||||
|
40.32
|
||||||
|
],
|
||||||
|
[
|
||||||
|
100,
|
||||||
|
56.3
|
||||||
|
],
|
||||||
|
[
|
||||||
|
71.88,
|
||||||
|
56.3
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49.48,
|
||||||
|
76.21
|
||||||
|
],
|
||||||
|
[
|
||||||
|
27.88,
|
||||||
|
56.36
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
56.36
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"dx": 0.0,
|
||||||
|
"dy": 13.49
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "code",
|
||||||
|
"title": "Application code",
|
||||||
|
"blurb": "Your actual program \u2014 usually the smallest layer, and the one that changes every single build.",
|
||||||
|
"z": 0,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
26.54
|
||||||
|
],
|
||||||
|
[
|
||||||
|
27.75,
|
||||||
|
26.54
|
||||||
|
],
|
||||||
|
[
|
||||||
|
50.77,
|
||||||
|
40.03
|
||||||
|
],
|
||||||
|
[
|
||||||
|
72.0,
|
||||||
|
26.17
|
||||||
|
],
|
||||||
|
[
|
||||||
|
100,
|
||||||
|
26.17
|
||||||
|
],
|
||||||
|
[
|
||||||
|
100,
|
||||||
|
41.02
|
||||||
|
],
|
||||||
|
[
|
||||||
|
71.5,
|
||||||
|
41.02
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49.76,
|
||||||
|
56.15
|
||||||
|
],
|
||||||
|
[
|
||||||
|
28.38,
|
||||||
|
41.34
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
41.34
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"dx": 0.0,
|
||||||
|
"dy": 28.54
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "writable",
|
||||||
|
"title": "Writable layer",
|
||||||
|
"blurb": "Created fresh per container, thrown away when it dies. Anything written here is not in the image.",
|
||||||
|
"z": 0,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
100,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
100,
|
||||||
|
26.87
|
||||||
|
],
|
||||||
|
[
|
||||||
|
72.0,
|
||||||
|
26.87
|
||||||
|
],
|
||||||
|
[
|
||||||
|
50.77,
|
||||||
|
40.73
|
||||||
|
],
|
||||||
|
[
|
||||||
|
27.75,
|
||||||
|
27.24
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
27.24
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"dx": 0.0,
|
||||||
|
"dy": 40.1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
{
|
{
|
||||||
"name": "image",
|
"name": "image",
|
||||||
"title": "The image",
|
"title": "The image",
|
||||||
"blurb": "C-I's layers, shipped as one unit - the thing whose name we must say precisely.",
|
"blurb": "Course I's layers, shipped as one unit - the thing whose name we must say precisely.",
|
||||||
"z": 0,
|
"z": 0,
|
||||||
"points": [
|
"points": [
|
||||||
[
|
[
|
||||||
|
|||||||
+4
-1
@@ -84,6 +84,8 @@ def emit(name, tag, spec):
|
|||||||
p["dy"] = round((ay - p["cy"]) * nest, 2)
|
p["dy"] = round((ay - p["cy"]) * nest, 2)
|
||||||
del p["cx"]; del p["cy"]
|
del p["cx"]; del p["cy"]
|
||||||
man = dict(scene=tag, source=f"assets/raw/{name}", parts=parts)
|
man = dict(scene=tag, source=f"assets/raw/{name}", parts=parts)
|
||||||
|
if "stagger" in spec:
|
||||||
|
man["stagger"] = spec["stagger"]
|
||||||
out = ROOT / "pilot" / f"manifest-{tag}.json"
|
out = ROOT / "pilot" / f"manifest-{tag}.json"
|
||||||
out.write_text(json.dumps(man, indent=1))
|
out.write_text(json.dumps(man, indent=1))
|
||||||
print(f"manifest-{tag}.json: {len(parts)} parts ->",
|
print(f"manifest-{tag}.json: {len(parts)} parts ->",
|
||||||
@@ -92,7 +94,8 @@ def emit(name, tag, spec):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
mode, scenes_json, outdir = sys.argv[1], sys.argv[2], sys.argv[3]
|
mode, scenes_json, outdir = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||||
scenes = json.loads(pathlib.Path(scenes_json).read_text()) if mode == "emit" else None
|
scenes = json.loads(pathlib.Path(scenes_json).read_text()) if mode == "emit" else None
|
||||||
heroes = [("course-VI-gitops.jpeg", "gitops"),
|
heroes = [("course-II-cluster.jpeg", "cluster"),
|
||||||
|
("course-VI-gitops.jpeg", "gitops"),
|
||||||
("course-VII-supply-chain.jpeg", "supply"),
|
("course-VII-supply-chain.jpeg", "supply"),
|
||||||
("course-VIII-helm-press.jpeg", "helm")]
|
("course-VIII-helm-press.jpeg", "helm")]
|
||||||
for name, tag in heroes:
|
for name, tag in heroes:
|
||||||
|
|||||||
Reference in New Issue
Block a user