--- # Full dev/test cluster bring-up, in order (docs/02 sequencing; run AFTER # `tofu apply` has created and cloud-init has baselined the VMs): # k3s HA install -> kube-vip control-plane VIP -> Cilium CNI -> verify readiness # -> Flux + SOPS hand-off. # (kube-vip joined the chain 2026-09-06 — see the note at its import below; it is a # prerequisite of the CNI now, not a separate operator step.) # # TARGET (R-ENV-02): every imported play takes its host pattern from a var that # defaults to the dev group, so `ansible-playbook playbooks/site.yml` with no extra # vars is byte-for-byte the dev run it always was. Extra vars are global and reach # imported playbooks, so the whole chain retargets with three flags: # ansible-playbook playbooks/site.yml \ # -e k3s_hosts=k3s_prod \ # -e k3s_primary_hosts=k3s_prod_primary \ # -e k3s_secondary_hosts=k3s_prod_secondary # DO NOT run that yet — prod is blocked on the disk-layout decision # (infra/PROD-PROVISIONING.md §2) and the §7 repo blockers, and the operator has ruled # the prod cluster stays uninitialized until all three nodes are present (§8). - name: Import - node baseline (packages, kernel modules, Longhorn disk, swap-off) ansible.builtin.import_playbook: node-baseline.yml - name: Import - install k3s HA control plane ansible.builtin.import_playbook: k3s.yml # ADDED 2026-09-07, after the k3s-prod-1 etcd I/O-starvation outage. It must come AFTER # k3s.yml because it writes an IOWeight drop-in for k3s.service and verifies the value in # that unit's LIVE cgroup — on a cold build the cgroup does not exist until k3s runs, and # the role reports that rather than asserting on it. It restarts nothing. # # A NO-OP FOR DEV, by design: roles/etcd_io_priority defaults its master switch to false and # only inventory/group_vars/k3s_prod/vars.yml sets it true. Dev has a dedicated etcd device # and must not be dragged onto a different I/O scheduler by a shared role. - name: Import - I/O arbitration for the shared etcd + Longhorn device (prod only) ansible.builtin.import_playbook: etcd-io-priority.yml # ADDED 2026-09-06, AND ITS POSITION IS LOAD-BEARING: kube-vip MUST come up between k3s and # Cilium. Cilium's k8sServiceHost is now the control-plane VIP (k3s_apiserver_endpoint = # kube_vip_address) rather than the founder's own node IP, so installing Cilium before the # VIP is announced would point every agent at an address nothing answers on — the exact # shape of the 2026-09-01 incident, where agents that could not reach the API server # deadlocked on a cold cluster. # # IT CANNOT COME EARLIER. kube-vip ships as a k3s auto-deploy manifest # (/server/manifests), so a cluster must exist before it can be applied — hence # after k3s.yml, not before. # # IT DOES NOT NEED THE CNI, which is what makes this ordering possible at all: the kube-vip # DaemonSet is hostNetwork with KUBERNETES_SERVICE_HOST=127.0.0.1 (roles/kube_vip's template # explains why), so it reaches the node-local apiserver with no CNI and no kube-proxy, and # DaemonSet pods carry the default not-ready/network-unavailable tolerations that let them # schedule onto a still-NotReady node. roles/kube_vip blocks until the VIP actually answers # on 6443, so cilium.yml below cannot start against a VIP that is not up. # # Re-running this against the LIVE dev cluster is a no-op for this play: dev already runs # kube-vip with the same rendered manifest, and the wait finds the VIP already answering. - name: Import - float the control-plane HA VIP (kube-vip) BEFORE the CNI dials it ansible.builtin.import_playbook: kube-vip.yml - name: Import - install Cilium CNI ansible.builtin.import_playbook: cilium.yml - name: Import - verify cluster readiness ansible.builtin.import_playbook: verify.yml - name: Import - Flux + SOPS bootstrap hand-off ansible.builtin.import_playbook: flux-bootstrap.yml