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>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
92c3ad3191
commit
110bd97cca
+68
-44
@@ -5,64 +5,81 @@ This is the detailed, copy-pasteable companion to
|
||||
first for the explanations — this one is here so you have every exact
|
||||
command in one place once you understand what they do.
|
||||
|
||||
Throughout, replace `<NODE_IP>` with your machine's real IP address. If it's
|
||||
a VM, give it a DHCP reservation (a fixed IP tied to its MAC address) in your
|
||||
router or hypervisor before you start, so the address never changes under
|
||||
the cluster. Talos bakes the IP into certificates during setup, and changing
|
||||
it later means starting over.
|
||||
Throughout, replace `<NODE1_IP>`, `<NODE2_IP>`, `<NODE3_IP>` with your three
|
||||
machines' real IP addresses, and `<VIP_IP>` with the shared address you
|
||||
picked (with Claude's help) for the cluster's VIP — see
|
||||
[`docs/02-talos-bootstrap.md`](../docs/02-talos-bootstrap.md#the-vip-one-address-for-a-cluster-that-moves).
|
||||
Give each machine a DHCP reservation (a fixed IP tied to its MAC address)
|
||||
in your router or hypervisor before you start, so none of these addresses
|
||||
change under the cluster. Talos bakes them into certificates during setup,
|
||||
and changing one later means starting over for that node.
|
||||
|
||||
Steps 1–2 below run **once, total**. Steps 3 runs **once per node** (three
|
||||
times). Steps 4–6 run **once, total**, against whichever node you like
|
||||
(or, once it exists, the VIP).
|
||||
|
||||
All commands assume you're running them from the root of this repo.
|
||||
|
||||
## 1. Generate machine configuration
|
||||
|
||||
```sh
|
||||
talosctl gen config gitops-starter https://<NODE_IP>:6443 \
|
||||
talosctl gen config gitops-starter https://<VIP_IP>:6443 \
|
||||
--output-dir _out \
|
||||
--config-patch-control-plane @talos/patch-controlplane.yaml
|
||||
--config-patch-control-plane @talos/patch-controlplane.yaml \
|
||||
--config-patch-control-plane @talos/patch-longhorn.yaml
|
||||
```
|
||||
|
||||
Note the cluster endpoint here is the **VIP**, not any one node's address
|
||||
— every node needs to agree in advance on where the cluster "lives," and
|
||||
that address has to be the one that keeps working no matter which node is
|
||||
currently answering.
|
||||
|
||||
This writes three files into `_out/` (already covered by this repo's
|
||||
`.gitignore` — they contain private keys and must never be committed):
|
||||
|
||||
- `controlplane.yaml` — the full machine configuration for a control-plane
|
||||
node, with our single-node patch baked in.
|
||||
- `worker.yaml` — a worker machine configuration. Unused for now (single
|
||||
node), kept for when you follow docs/08-growing-up.md.
|
||||
- `controlplane.yaml` — the machine configuration for a control-plane
|
||||
node, with both patches baked in. Applied identically to all three
|
||||
nodes — there's no separate worker config in this setup, since every
|
||||
node is both.
|
||||
- `worker.yaml` — a plain worker config, generated automatically but
|
||||
unused here since every node in this repo's setup is both control plane
|
||||
and worker. Kept around for [`docs/09-growing-up.md`](../docs/09-growing-up.md),
|
||||
where a fourth-and-beyond node uses exactly this file.
|
||||
- `talosconfig` — *your* client credentials for talking to the Talos API on
|
||||
this cluster (equivalent in spirit to a kubeconfig, but for Talos itself
|
||||
rather than Kubernetes).
|
||||
|
||||
`gitops-starter` is the cluster name — an arbitrary label baked into the
|
||||
generated certificates and config, used to tell clusters apart if you ever
|
||||
manage more than one. `https://<NODE_IP>:6443` is the cluster's Kubernetes
|
||||
API endpoint: port 6443 is the standard Kubernetes API server port, and this
|
||||
same address gets written into the certificates and your future kubeconfig.
|
||||
manage more than one. Port 6443 is the standard Kubernetes API server port.
|
||||
|
||||
## 2. Point talosctl at this cluster
|
||||
|
||||
```sh
|
||||
export TALOSCONFIG=_out/talosconfig
|
||||
talosctl config endpoint <NODE_IP>
|
||||
talosctl config node <NODE_IP>
|
||||
talosctl config endpoint <NODE1_IP> <NODE2_IP> <NODE3_IP>
|
||||
```
|
||||
|
||||
`endpoint` is which machine talosctl *talks to*. `node` is which machine a
|
||||
command *applies to*. On a single-node cluster they're the same address, but
|
||||
Talos always keeps the two concepts separate, because on a multi-node
|
||||
cluster you often want to ask one node about another.
|
||||
`endpoint` is which machine(s) talosctl is willing to *talk to* — giving
|
||||
it all three means it can still reach the cluster if any one of them is
|
||||
down. You'll pass `--nodes` per-command below for which machine a given
|
||||
command actually *applies to* — Talos always keeps those two concepts
|
||||
separate.
|
||||
|
||||
## 3. Apply the configuration
|
||||
## 3. Apply the configuration — once per node
|
||||
|
||||
Boot the target machine from the Talos ISO (see
|
||||
[docs/01-prerequisites.md](../docs/01-prerequisites.md) for where to get it).
|
||||
It boots into "maintenance mode" — alive enough to accept a configuration,
|
||||
with nothing installed yet. Confirm it's reachable, then apply:
|
||||
Boot each machine from your custom Talos ISO (see
|
||||
[docs/01-prerequisites.md](../docs/01-prerequisites.md)). Each boots into
|
||||
"maintenance mode" — alive enough to accept a configuration, with nothing
|
||||
installed yet. For **each** node in turn:
|
||||
|
||||
```sh
|
||||
talosctl get disks --insecure --nodes <NODE_IP>
|
||||
```
|
||||
|
||||
Note the disk name you want Talos installed to (e.g. `/dev/sda`). Then:
|
||||
Confirm you see two disks: the one Talos will install itself to, and the
|
||||
≥900GB one `talos/patch-longhorn.yaml`'s `UserVolumeConfig` will claim for
|
||||
Longhorn. Then:
|
||||
|
||||
```sh
|
||||
talosctl apply-config --insecure \
|
||||
@@ -73,32 +90,37 @@ talosctl apply-config --insecure \
|
||||
`--insecure` is required for this one command only: the node has no
|
||||
certificates yet, so talosctl can't authenticate to it normally. This is
|
||||
the one moment during setup where you're trusting the network — do this on
|
||||
a network you control. The node will reboot, install Talos to disk, and
|
||||
come back up running the configuration you gave it — now *with*
|
||||
certificates, so every command after this uses the secure path.
|
||||
a network you control. The node will reboot, install Talos to its system
|
||||
disk, format and mount its second disk for Longhorn, and come back up
|
||||
running the configuration you gave it — now *with* certificates, so every
|
||||
command after this uses the secure path.
|
||||
|
||||
## 4. Bootstrap etcd
|
||||
Repeat this for `<NODE1_IP>`, `<NODE2_IP>`, and `<NODE3_IP>` before moving on.
|
||||
|
||||
Talos is installed and Kubernetes components are starting, but the cluster
|
||||
has no data store yet. This one-time command initializes it:
|
||||
## 4. Bootstrap etcd — once, total
|
||||
|
||||
Talos is installed on all three nodes and Kubernetes components are
|
||||
starting, but the cluster has no data store yet. This one-time command
|
||||
initializes it:
|
||||
|
||||
```sh
|
||||
talosctl bootstrap --nodes <NODE_IP>
|
||||
talosctl bootstrap --nodes <NODE1_IP>
|
||||
```
|
||||
|
||||
Run this exactly once per cluster, against exactly one control-plane node,
|
||||
even if you later add more. Running it again on an existing cluster can
|
||||
corrupt it.
|
||||
Run this **exactly once**, against **exactly one** of your three nodes —
|
||||
even though all three will end up running etcd as peers, only one needs
|
||||
telling to *start* the cluster; the other two discover and join the
|
||||
existing one. Running it again against an existing cluster can corrupt it.
|
||||
|
||||
## 5. Get your kubeconfig
|
||||
|
||||
```sh
|
||||
talosctl kubeconfig --nodes <NODE_IP> .
|
||||
talosctl kubeconfig --nodes <VIP_IP> .
|
||||
```
|
||||
|
||||
Downloads a kubeconfig into the current directory (also gitignored —
|
||||
it grants full cluster access). Merge it with `~/.kube/config` or point
|
||||
`KUBECONFIG` at it directly:
|
||||
it grants full cluster access), pointed at the VIP rather than any one
|
||||
node. Merge it with `~/.kube/config` or point `KUBECONFIG` at it directly:
|
||||
|
||||
```sh
|
||||
export KUBECONFIG=$PWD/kubeconfig
|
||||
@@ -110,14 +132,16 @@ export KUBECONFIG=$PWD/kubeconfig
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
Expect to see one node, `Ready`, within a couple of minutes (it takes a
|
||||
moment for the default CNI — Talos ships Flannel out of the box — to come
|
||||
up and clear the node's networking checks).
|
||||
Expect to see **three** nodes, `Ready`, within a couple of minutes (it
|
||||
takes a moment for the default CNI — Talos ships Flannel out of the box —
|
||||
to come up and clear each node's networking checks).
|
||||
|
||||
```
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
talos-... Ready control-plane 90s v1.3x.x
|
||||
talos-... Ready control-plane 85s v1.3x.x
|
||||
talos-... Ready control-plane 80s v1.3x.x
|
||||
```
|
||||
|
||||
If it's stuck on `NotReady`, give it another minute before troubleshooting —
|
||||
first boot does a lot of one-time work.
|
||||
If any node is stuck on `NotReady`, give it another minute before
|
||||
troubleshooting — first boot does a lot of one-time work.
|
||||
|
||||
Reference in New Issue
Block a user