4.7 KiB
rig
A runnable local model of a large, regulated estate — legacy and new side by side. Its job is onboarding and exploration, not a production replica: most services are deliberately mocked, because what has to be faithful is the topology, not the workloads.
Prerequisite
Docker. Nothing else — no curl, no jq, no python, no apt repositories.
Read the docs first
make docs # serves on localhost, prints the URL
They run before anything is installed, which matters because they are the instructions for everything else. No cluster and no toolchain required.
Then
make station # report host and config problems; changes nothing
make deps # install the toolchain (add `core` on a managed machine)
make cluster up # build the cluster for the active profile
make help lists every target.
On a machine where Docker really is the only thing installed, make deps has
nothing to download with — see BOOTSTRAP.md, which runs the
toolchain through the wizard container and carries on to scaffolding and running
a new project.
One directory is one environment
Copy this directory, rename it, run it. Cluster name, kubectl context, image tags and the host port block all derive from the directory name, so copies never collide and neither one's teardown can touch the other.
rig lives inside soleprint, at spr/rig — it is soleprint's cluster half, and a
copy is a sibling: spr/acme-rig. That is why the ignore rules for client
rigs sit in spr/.gitignore rather than here; a rule in this directory cannot
see a directory beside it.
Profiles
A profile is the shape of the cluster: how many nodes, which addons, whether the
apiserver audits. They live in ctrl/env.d/, and the active one is PROFILE.
| Profile | For |
|---|---|
minimal |
the default. One node, no addons, boots fast. |
client |
the regulated-estate shape — multi-node, audit on, registry mirror. |
offline |
air-gapped: everything from a preloaded local registry. |
data |
the dependency containers a soleprint room asks for. |
PROFILE=data make cluster up
PROFILE=data make addons install
make addons # what the active profile wants, and what exists
A profile names a cluster shape — a file in ctrl/k8s/ — rather than
restating node count and audit as variables:
| shape | nodes | audit | used by |
|---|---|---|---|
kind-config.yaml.tpl |
1 | off | minimal, data |
kind-config.audit.yaml.tpl |
1 | on | offline |
kind-config.client.yaml.tpl |
3 | on | client |
Both numbers are read back out of the chosen file, so the YAML is the only place
that decides and there is nothing to drift. The layout under ctrl/k8s/ is the
same as every other project here — a kind config, a kustomize base/, an
overlays/dev/ — see ctrl/k8s/README.md.
Addons
Each addon is its own idempotent script in ctrl/addons/, and a profile names
the ones it wants in ADDONS. Adding one is adding a file — there is no
dispatcher to edit.
There is no ingress controller, deliberately. They pin a narrow window of
Kubernetes versions, so depending on one would constrain which k8s a rig can be
built with — and running a trailing-edge control plane to model a legacy estate
is the whole point. Services are reached through MetalLB and
type: LoadBalancer, which carries no such constraint and is also what a real
cluster does.
| Addon | Does |
|---|---|
metallb |
gives type: LoadBalancer an address it can actually reach |
cert-manager |
a local CA, so TLS works offline |
metrics-server |
makes kubectl top work on kind |
postgres |
database, in the data namespace |
redis |
cache and broker |
airflow |
scheduled pipelines; needs postgres and redis |
The last three are the cluster half of soleprint's cabinets. A room declares
what it needs once, in cfg/<room>/data/cabinets.json; soleprint's build.py
composes those services into docker-compose.yml for a laptop, and these
install the same ones here. The names match on purpose — each cabinet carries a
rig_addon field pointing at ctrl/addons/<name>.sh.
Plain manifests rather than helm charts, like every other addon: a chart repo is
a network dependency, and the offline profile exists precisely so there is a
path with none. Images are pinned in ctrl/versions.env and can be preloaded.
Passwords are generated on first install and kept across re-runs, so re-running an addon never rotates a credential out from under something already connected:
kubectl -n data get secret postgres -o jsonpath='{.data.POSTGRES_PASSWORD}' | base64 -d
kubectl -n data port-forward svc/airflow 8080:8080