fresh-eyes pass: the Linux bridge (container is a process, namespaces/cgroups, overlayfs upper dir, rootless podman), control plane internals + Deployment->ReplicaSet->Pod lineage, oc-vs-kubectl note, registry-qualified push, de-slop (VIII title formula, flowery headings)
This commit is contained in:
+49
-11
@@ -55,9 +55,9 @@
|
||||
<div class="crt" role="img" aria-label="A terminal transcript: podman build, podman push,
|
||||
oc apply, oc get pods showing Running - then the question: but what
|
||||
actually just happened?">
|
||||
<p class="line">$ podman build -t shop/api:1.4.2 .</p>
|
||||
<p class="line">$ podman build -t ghcr.io/shop/api:1.4.2 .</p>
|
||||
<p class="line ok">=> exported image sha256:9f2c41...</p>
|
||||
<p class="line">$ podman push shop/api:1.4.2</p>
|
||||
<p class="line">$ podman push ghcr.io/shop/api:1.4.2</p>
|
||||
<p class="line">$ oc apply -f deploy.yaml</p>
|
||||
<p class="line ok">deployment.apps/api created</p>
|
||||
<p class="line">$ oc get pods</p>
|
||||
@@ -68,6 +68,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="prose">
|
||||
<p class="aside">A note on the commands: this site uses <span class="mono">oc</span>,
|
||||
OpenShift's CLI. Everything shown works identically with <span class="mono">kubectl</span> -
|
||||
<span class="mono">oc</span> is a superset that adds OpenShift's own verbs. Read
|
||||
<span class="mono">kubectl</span> in the docs, type whichever your cluster gives you.</p>
|
||||
<p>An image got <b>built</b> - of what, exactly? <b>Pushed</b> - to where, and what travelled?
|
||||
<b>Applied</b> - which is not the same as launched. <b>Running</b> - according to whom?
|
||||
Every course below takes one of those words apart. The armour is already loose.</p>
|
||||
@@ -95,15 +99,32 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="prose">
|
||||
<h3>A stack of diffs wearing a name</h3>
|
||||
<h3>A container is a process, not a machine</h3>
|
||||
<p>If you arrived here from Linux, take this translation first: a running container is an
|
||||
<b>ordinary process on your kernel</b>. No guest OS, no hypervisor. The kernel gives it
|
||||
<b>namespaces</b> so it sees its own PID tree, mounts, network and hostname, and
|
||||
<b>cgroups</b> so its CPU and memory can be capped. <span class="mono">ps</span> on the host
|
||||
lists it. <span class="mono">kill</span> on the host kills it. Podman leans into that: no
|
||||
daemon sits in the middle - the container is a child of your own shell - and rootless mode
|
||||
maps your user onto root <i>inside</i> the container through a user namespace, so root in
|
||||
there is an unprivileged UID out here.</p>
|
||||
<p>So what does the image provide? The <b>filesystem that process sees</b>. That is the whole
|
||||
job, and it is why an image is a stack of layers rather than a disk image.</p>
|
||||
|
||||
<h3>What the image actually is</h3>
|
||||
<p>An image is not a copy of a machine. It is a stack of <b>read-only</b> layers, each
|
||||
recording only what changed from the one beneath. Layers are content-addressed, so an
|
||||
identical layer is stored once and reused by every image that references it, so a pull only
|
||||
fetches the layers you do not already have. A base sits at the bottom, your dependencies on it,
|
||||
your code - usually the smallest layer, always the most volatile - above that.</p>
|
||||
<p>The top slab in the scene is the odd one out: the <b>writable layer is not part of the
|
||||
image</b>. The runtime adds it when it creates the <i>container</i>, and discards it when
|
||||
that container dies. Nothing written there ships, and nothing written there survives.</p>
|
||||
<p>Those layers become one filesystem through a <b>union mount</b> - overlayfs, the same
|
||||
kernel feature you can mount by hand. The read-only image layers are the lower dirs; the
|
||||
container gets a fresh <b>upper dir</b> of its own. Writes land in the upper, and editing an
|
||||
existing file copies it up there first, leaving the image layer untouched underneath.</p>
|
||||
<p>That upper dir is the top slab in the scene, and it is the odd one out: the <b>writable
|
||||
layer is not part of the image</b>. The runtime creates it with the <i>container</i> and
|
||||
discards it when that container dies. Nothing written there ships, and nothing written there
|
||||
survives - which is the entire reason volumes exist.</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
|
||||
@@ -159,8 +180,23 @@
|
||||
<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.
|
||||
<h3>What the control plane is made of</h3>
|
||||
<p>"Control plane" is four processes and a database, and naming them makes every later error
|
||||
message readable. The <b>api-server</b> is the only door: everything authenticates, is
|
||||
authorised and is admitted there, and it is the only component allowed to touch <b>etcd</b> -
|
||||
the key-value store holding the entire cluster state. Lose etcd and you have lost the
|
||||
cluster, which is why backing it up is the homework nobody should skip. The <b>scheduler</b>
|
||||
decides which node a new pod belongs on and writes that decision down; it never starts
|
||||
anything. The <b>controller-manager</b> runs the reconciliation loops. Nothing talks
|
||||
sideways - every component watches the api-server.</p>
|
||||
<p>One of those loops is the chain you will debug most: a <b>Deployment</b> creates a
|
||||
<b>ReplicaSet</b>, and the ReplicaSet creates <b>pods</b>. That is why the deploy in the
|
||||
aperitif printed <span class="mono">deployment.apps/api created</span> and you then went
|
||||
looking for a pod - and why, when a rollout is stuck with no pod at all, the answer is
|
||||
upstream in that chain rather than on any node.</p>
|
||||
<p class="sayit">etcd is the truth. Everything else is a cache of it.</p>
|
||||
<p class="closeline">Everything the delivery arc teaches from Course VI onward is the same
|
||||
loop at a bigger scale - 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> ·
|
||||
@@ -496,7 +532,7 @@
|
||||
<section class="course" id="c8" data-scene="helm" aria-labelledby="c8h">
|
||||
<header class="chead">
|
||||
<p class="eyebrow">Course VIII · Helm</p>
|
||||
<h2 id="c8h" class="thesis">A chart is a function.<br><em>The values file is the cluster speaking.</em></h2>
|
||||
<h2 id="c8h" class="thesis">Stop copying YAML between clusters.<br><em>Ship the function instead.</em></h2>
|
||||
<p class="lede">The default way to run one app on five clusters is five copies of the YAML, and
|
||||
the default result is five slightly different apps. The inversion: stop copying outputs and
|
||||
ship the function. A chart is a template with holes; each cluster supplies one small values
|
||||
@@ -514,7 +550,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="prose">
|
||||
<h3>It's Go under the braces</h3>
|
||||
<h3>You are writing a program, not YAML</h3>
|
||||
<p>Helm templates are Go text/template: <span class="mono">{{ .Values.device.address }}</span>
|
||||
is a pipeline walking a values object, <span class="mono">_helpers.tpl</span> holds the
|
||||
<b>named templates</b> - the partials every manifest <span class="mono">include</span>s, not
|
||||
@@ -648,7 +684,7 @@
|
||||
</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>
|
||||
state, the reconciliation loop, pods, services, and the controllers that keep the 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>
|
||||
@@ -764,6 +800,8 @@ body.fx .crt .line.on{opacity:1;transform:none}
|
||||
|
||||
|
||||
.prose{max-width:66ch;margin:0 auto;padding:5vh 24px 9vh}
|
||||
.prose .aside{font-size:.88rem !important;border-left:2px solid var(--line);padding-left:18px;
|
||||
color:var(--dim) !important}
|
||||
.prose h3{font-size:1.18rem;font-weight:600;letter-spacing:-.01em;margin:2.6rem 0 .8rem;color:var(--ink)}
|
||||
.prose p{font-size:.98rem;line-height:1.75;color:var(--dim);margin:0 0 1.1rem}
|
||||
.sayit{font-weight:600;color:var(--cyan) !important;border-left:2px solid var(--cyan);padding-left:18px}
|
||||
|
||||
Reference in New Issue
Block a user