Files
vps-k8s/docs/02-talos-bootstrap.md
T
Maxwell MorrisandClaude Sonnet 5 110bd97cca Move to a three-node cluster with a Talos VIP; add Longhorn
Every node is now both control plane and worker, giving real tolerance
for losing any single machine (etcd quorum needs 3, not 1). A Talos VIP
gives the cluster one stable address regardless of which node answers.

Also pre-installs Longhorn for replicated storage, backed by a Talos
UserVolume on each node's second disk (>=900GB, non-system) rather than
the default path — mirroring the pattern from the homescale repo.

Docs renumbered to make room for 06-longhorn.md; checklist, glossary,
network map, and CLAUDE.md's guided-bootstrap instructions updated to
match, including asking the user for node IPs, VIP, and disk info before
generating any Talos config.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 18:25:38 +01:00

125 lines
6.3 KiB
Markdown

# 02 — Bootstrapping Talos
At the end of this doc, you'll have a three-node Kubernetes cluster,
running nothing but Kubernetes itself (plus the storage prep Longhorn
needs later). No Argo CD yet — that's next.
## Why Talos, specifically
Most guides have you install a general-purpose Linux distribution, then
install Kubernetes on top by hand or with a tool like kubeadm — meaning
there's a whole normal OS underneath, with a package manager, an SSH
daemon, and every other thing that can drift out of a known-good state or
need patching. Talos throws that away: it's an OS whose only job is running
Kubernetes. There's no SSH, no shell to log into, no packages to manage —
you configure it entirely by sending it a **machine config** file over the
network (the `talosctl` commands below), and it applies that config the
same declarative way Kubernetes applies your app manifests. Less surface
area, less to secure, less to keep in sync by hand — the same philosophy as
the rest of this stack, one layer lower.
## Three nodes, each both control plane and worker
A "real" Kubernetes cluster has multiple nodes — some running the
**control plane** (the brain: API server, scheduler, the cluster's own
database, called etcd) and others as **workers** (running your actual
apps) — so that losing one machine doesn't take the whole cluster down.
This repo starts with three nodes, each acting as *both* control plane and
worker, which gets you real resilience — survive losing any one machine —
without needing more than three.
Why three specifically, and not two: etcd (the control plane's database)
uses **quorum** — a majority vote among its members — to agree on cluster
state, and a majority only means something with an odd number of voters.
With one control-plane node, losing it loses the cluster outright. Two
doesn't help: losing either one leaves one survivor, which is not a
majority of two. Three is the smallest number where losing any *one*
still leaves two — a majority — able to keep agreeing on state. This is
also why [`docs/09-growing-up.md`](09-growing-up.md) — once you're past
this doc — specifically warns against going to four control-plane nodes
later: four doesn't improve on three's fault tolerance, it just makes
quorum math worse.
## The VIP: one address for a cluster that moves
With three control-plane nodes, "where's the API server?" no longer has
one fixed answer — it's whichever nodes happen to be up right now. Talos's
**VIP** (Virtual IP) feature solves this: the three nodes continuously
elect one of themselves to answer on one shared IP address you choose, and
re-elect within seconds if that node goes down. Everything that talks to
this cluster — `talosctl`, `kubectl`, the Application manifests in this
repo — points at the VIP, never at one node's own address. See the
`vip` block in
[`talos/patch-controlplane.yaml`](../talos/patch-controlplane.yaml).
> **Ask Claude:** "help me pick the VIP address for my cluster" before you
> generate any config — same kind of decision as the MetalLB pool in
> [`docs/05-metallb.md`](05-metallb.md): a real, unused address on your
> LAN, outside your DHCP range, that Claude can't guess on its own.
## Preparing storage for Longhorn
Each node also gets a second disk reserved for
[Longhorn](06-longhorn.md), this repo's pre-installed storage layer —
handled by [`talos/patch-longhorn.yaml`](../talos/patch-longhorn.yaml),
applied at the same time as the control-plane patch below. Read that
file's comments; the short version is that Talos finds, formats, and
mounts a disk for Longhorn on its own, using rules you set (not the system
disk, at least 900GB), so there's no manual partitioning step here.
## Steps
Full exact commands live in [`talos/README.md`](../talos/README.md) — read
that alongside this doc; this section explains what's happening at each
stage, that one has copy-paste blocks. You'll repeat most of these steps
**three times**, once per node — `talosctl README.md` calls out exactly
which commands are "once" vs. "once per node."
1. **Generate machine configuration.** `talosctl gen config` produces
Talos's config files for your cluster, with both
[`talos/patch-controlplane.yaml`](../talos/patch-controlplane.yaml)
(scheduling + the VIP) and
[`talos/patch-longhorn.yaml`](../talos/patch-longhorn.yaml) (storage
prep) baked in — read both files, they explain themselves. This
produces one `controlplane.yaml`, applied identically to all three
nodes; there's no separate worker config in this setup, since every
node is both.
2. **Apply the config to each node.** Each node, freshly booted from your
custom Talos ISO, is sitting in "maintenance mode" with no
configuration. `talosctl apply-config` sends it the config from step 1.
Run this once per node, pointed at that node's own address. Each node
reboots, installs Talos to its system disk, formats and mounts its
second disk for Longhorn, and comes up running for real.
3. **Bootstrap etcd.** `talosctl bootstrap` initializes the cluster's data
store. This is a one-time, one-node-ever operation, run against exactly
one of your three nodes — even though all three will end up running
etcd, only one of them needs telling to *start* the cluster; the other
two join the existing one once it exists.
4. **Retrieve your kubeconfig.** `talosctl kubeconfig` downloads the
credentials you'll use to talk to Kubernetes with `kubectl` — the
Kubernetes equivalent of the `talosconfig` you got in step 1. Point it
at the VIP, not at any one node.
5. **Verify.** `kubectl get nodes` should show all three nodes, `Ready`.
> **Ask Claude:** "what's the difference between a kubeconfig and a
> talosconfig, and why are they separate files?" is a good one to ask once
> you've got both — the split trips people up the first time.
## Checklist
- [ ] VIP address chosen, with Claude's help, for your LAN
- [ ] `_out/controlplane.yaml` and `talosconfig` generated, with both
patches applied
- [ ] Config applied to **all three** nodes; each has rebooted, installed
Talos, and mounted its Longhorn disk
- [ ] `talosctl bootstrap` run exactly once, against exactly one node
- [ ] kubeconfig retrieved, pointed at the VIP
- [ ] `kubectl get nodes` shows all three nodes `Ready`
Once all three nodes are `Ready`, continue to
[`docs/03-installing-argocd.md`](03-installing-argocd.md).