Deployment operations lessons
The deployment operations lessons are for the reader who wants to see how the same tutorial platform behaves when the runtime boundary is the Tinys k3s cluster.
The operator shape is explicit: laptop as thin human surface, Spark as the Kubernetes The Hard Way style jumpbox and AI workstation, Tinys as the runtime, and Azure as the cloud migration target.
L01 - Tinys k3s operator loop
Section titled “L01 - Tinys k3s operator loop”The first lesson lives in
tutorial/deployment/lesson-01-k3s-local-cluster.md.
It covers:
- installing or attaching to the Tinys k3s cluster;
- choosing three k3s
servernodes with embedded etcd for quorum and failure learning; - configuring
kubectl; - creating the
fos-workbenchnamespace; - mapping Spark to the jumpbox role from Kubernetes The Hard Way;
- checking cluster defaults that matter to this repo, including DNS, HTTP exposure, and local persistent volume claims;
- mapping Kubernetes objects back to the tutorial vocabulary.
No fos-workbench workload is deployed in L01. That is deliberate: the first lesson establishes the operator boundary before adding manifests.
L01a - Kubernetes words with Tinys examples
Section titled “L01a - Kubernetes words with Tinys examples”The primer lives in
Kubernetes primer on the Tinys.
It covers:
- reading
kubectl get nodes,kubectl get pods, and Flux status output; - mapping containers, pods, Deployments, Services, Jobs, PVCs, and namespaces to the smoke workload that ran on the Tinys;
- explaining control plane, k3s server, etcd, HA, and quorum with the exact
three-node
eigil/ingvild/dictetopology; - explaining GitRepository, Kustomization, and reconciliation from the
fos-workbenchGitOps source; - connecting snapshots, Azure migration, and power posture back to the same cluster vocabulary.
L02 - App contract Deployment and Service
Section titled “L02 - App contract Deployment and Service”The second lesson lives in
tutorial/deployment/lesson-02-app-contract-service.md.
It covers:
- building the existing serving image from
deploy/azure/containers/serving.Dockerfile; - pushing that image through a local
localhost:5000registry so k3s can pull it without direct containerd access; - applying
tutorial/deployment/k8s/app-contract.yaml; - verifying the
/healthzprobe through a ClusterIP Service andkubectl port-forward; - naming why
/articlescan be empty until the next lesson mounts or seeds the raw Parquet data path.
L03 - DuckDB/Parquet data persistence
Section titled “L03 - DuckDB/Parquet data persistence”The host-data mount lesson lives in
tutorial/deployment/lesson-03-host-data-mount.md.
It covers:
- why
/healthzcan pass while/articlesreturns an empty list; - replacing the pod’s temporary
emptyDirwith a localhostPathmount; - showing the repo’s
data/raw/ebnerd/...Parquet files inside the pod at/app/data/raw/ebnerd/...; - calling
/articles?limit=3through the Kubernetes Service and seeing real rows.
The PVC persistence lesson lives in
tutorial/deployment/lesson-03-data-persistence.md.
It then covers:
- mounting the same DuckDB-readable
data/path at/app/data; - keeping
SERVING_RAW_ROOT=/app/data/rawfor the app contract; - using a local k3s
PersistentVolumeClaimbacked by the Local Path Provisioner; - distinguishing local rehearsal storage from the Azure ADLS Gen2 target;
- stopping routine verification at a client dry-run with no live persistent-volume mutation.
L04 - dlt/dbt batch Job
Section titled “L04 - dlt/dbt batch Job”The fourth lesson lives in
tutorial/deployment/lesson-04-dlt-dbt-batch-job.md.
It covers:
- running the existing
serving.dlt_pipelinepublisher loads as a Kubernetes Job; - running
dbt buildthroughpython -m dataset_layoutso the canonical raw Parquet globs still come from the storage module; - mounting the existing
app-contract-dataPVC at/app/data; - naming the Job’s inputs, outputs, persistence boundary, and failure inspection commands;
- keeping routine verification to a client dry-run with no live cluster mutation.
L05 - Flux GitOps portability
Section titled “L05 - Flux GitOps portability”The fifth lesson lives in
tutorial/deployment/lesson-05-flux-gitops-portability.md.
It covers:
- installing Flux only after k3s cluster smoke tests pass;
- moving durable Kubernetes changes from one-off
kubectl applyinto Git; - separating Ansible’s node/bootstrap responsibility from Flux’s in-cluster reconciliation responsibility;
- sketching the
clusters/tinys,clusters/azure-aks,infrastructure, andappsGitOps layout; - mapping the Tinys path to Azure reliability services, Container Apps, AKS + Flux, and optional Azure Arc;
- leaving real secret handling for a later explicit decision.
Planned follow-ups
Section titled “Planned follow-ups”L06 - Tinys power and idle posture
Section titled “L06 - Tinys power and idle posture”The sixth lesson lives in
tutorial/deployment/lesson-06-tinys-power-idle.md.
It covers:
- measuring wall power before tuning;
- keeping workloads scale-to-idle through GitOps;
- installing measurement tools without applying risky power changes;
- avoiding casual
powertop --auto-tune, NIC power saving, and suspend policies until recovery drills are understood; - using one-node tests before rolling host tuning across all three Tinys.
Planned follow-ups
Section titled “Planned follow-ups”- Compare the k3s mapping with the Azure target from ADR-0028 and the Flux portability layer from ADR-0034.