--- # node_baseline — first-boot floor for k3s/Cilium/Longhorn, moved out of cloud-init # (Proxmox provisioning is API-only). Runs before k3s (site.yml). Idempotent. node_baseline_packages: # VM-only in practice (binds the virtio-serial channel Proxmox attaches) but harmless to install # unconditionally: the task below only *starts* it when that channel exists, so on bare metal # (prod) it installs and stays a dormant, disabled unit. Kept in the shared list rather than # split per-platform so dev and prod install from the exact same package set (R-ENV-02 reuse). - qemu-guest-agent - open-iscsi - nfs-common - curl - cryptsetup # Longhorn >=1.12 RequiredPackages check needs the cryptsetup userspace tool (dm_crypt module alone isn't enough) # Drive health (smartctl) is a PERMANENT part of a bare-metal node's baseline, not a one-off # manual install: prod runs on real disks that can fail/degrade (see infra/PROD-PROVISIONING.md # §2/§5 — a ~15-year-old consumer SSD on node 1), where dev's virtio disks never needed it. # Installed here (not hand-run with apt-get) so it's declared, idempotent, and present on every # node from first baseline — package installation is the role's job, never a manual step # (operator ruling, 2026-08-31; same click-ops-never principle as the rest of the repo). - smartmontools node_baseline_kernel_modules: - overlay - br_netfilter - iscsi_tcp - dm_crypt - nfs node_baseline_longhorn_device: /dev/vdb node_baseline_longhorn_mount: /var/lib/longhorn node_baseline_longhorn_fs_label: longhorn # Dedicated SSD etcd disk (tofu virtio2 -> /dev/vdc), mounted at the k3s etcd data dir # so etcd WAL fsync is isolated from Longhorn rebuild I/O on the HDD data disk. # See docs/plans/ssd-etcd-isolation.md. FS label keeps the fstab entry stable across # device renames (matches the Longhorn LABEL= approach). node_baseline_etcd_device: /dev/vdc node_baseline_etcd_mount: /var/lib/rancher/k3s/server/db node_baseline_etcd_fs_label: k3s-etcd # --- DNS resilience (R-DNS-01 / R-DNS-03) ------------------------------------------- # These are the DEV values. Prod overrides them in inventory/group_vars/k3s_prod/vars.yml # (the same seam kube_vip_address / k3s_api_host / cluster_kubeconfig_name use) — NOT in # clusters/base/cluster-vars.yaml, which is a Flux postBuild ConfigMap that no Ansible # code reads. # # WHY: the nodes shipped with ONE upstream resolver. PiHole answers the local jackson.lan # zone instantly but its external recursion flaps (measured 0/25 for registry-1.docker.io / # quay.io / ghcr.io while gitea.jackson.lan resolved 5/5), and one upstream is a hard SPOF: # it broke containerd image pulls ("lookup registry-1.docker.io: Try again") AND CoreDNS, # which forwards to /etc/resolv.conf and flooded its log with i/o timeouts to 192.168.1.18. # # ORDER IS THE POLICY. This is a RESILIENCE fix, not a bypass: the first entry stays PiHole, # so *.jackson.lan and network-wide ad-blocking keep working exactly as before, and the # second is only ever consulted when the first fails. Both write paths preserve that order # (systemd-resolved tries the servers of one scope in order; glibc walks `nameserver` lines # in order). Never put a public resolver first, and never enable `rotate` — either would # silently take the local zone and the ad-block policy out of the path. # # Keep in parity with infra/tofu (var.dns_server, var.network_gateway), which sets the same # two servers via cloud-init so a freshly rebuilt dev node boots with this list already. node_baseline_dns_servers: - 192.168.1.18 # PiHole — local jackson.lan zone + ad-blocking. PRIMARY, keep first. - 192.168.1.1 # pfSense gateway — reliable external recursion. FALLBACK only. # Search domain(s). An EMPTY list means "configure no search domain" and is a legitimate # value (prod uses it — its local domain is not a decided thing), not a missing one. node_baseline_dns_search: - jackson.lan # resolv.conf `options` for the DIRECT-file mechanism only (systemd-resolved does its own # per-server failover timing and ignores these). glibc's defaults are timeout:5 attempts:2, # i.e. up to 20s of stalled lookups before it reaches the second server — long enough for # containerd to give up on a pull. These shorten the failover, and deliberately do NOT # include `rotate`, which would round-robin the servers and break PiHole-first ordering. node_baseline_dns_options: - timeout:2 - attempts:2