--- # Home's member of the 3-way etcd quorum used by the failover-watcher # (see /home/scooby/.claude/plans/jiggly-snacking-iverson.md, "Multi-Site # Active Failover" section) to safely decide when to trigger a CNPG # distributed-topology promotion. VPS and witness run this same etcd # version as plain systemd services (simpler - no k3s/Kyverno involved # there); home has to run it as a real K8s workload since Talos doesn't # allow bare-metal process installs. # # Pinned to a specific node (talos-cp-01) via nodeSelector so its # advertised peer/client URLs (which must be stable, not "whichever node # it landed on today") stay correct. # # Reachable from the VPS/witness via a UniFi WAN port-forward + a # source-IP-restricted WAN_IN firewall rule (limited to just the VPS and # witness public IPs) - NOT via the Netbird mesh directly. talos-cp-01 # does have a real, host-bound Netbird IP (100.108.42.109 on its wt0 # interface, confirmed live via `talosctl get addresses`), but that's a # dead end for inbound WAN traffic: UniFi's router only has a route to # its own directly-connected LAN (172.28.101.0/24), nothing advertises # 100.108.0.0/16 to it, so a port-forward targeting the Netbird IP would # never actually route. The port-forward instead targets talos-cp-01's # real LAN IP (172.28.101.41) - NodePort Services bind on every interface # on a node, so the same ports are reachable there too, and that's an # address UniFi can actually route to natively. # # The NodePort Service itself needs # infrastructure/kyverno/policies/ha-failover-nodeport-exception.yaml # (disallow-nodeport-services is enforced cluster-wide otherwise). apiVersion: v1 kind: Namespace metadata: name: ha-failover --- apiVersion: apps/v1 kind: StatefulSet metadata: name: ha-etcd namespace: ha-failover spec: serviceName: ha-etcd replicas: 1 selector: matchLabels: app: ha-etcd template: metadata: labels: app: ha-etcd spec: nodeSelector: kubernetes.io/hostname: talos-cp-01 securityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 1000 seccompProfile: type: RuntimeDefault containers: - name: etcd image: gcr.io/etcd-development/etcd:v3.5.17 securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] command: - etcd - --name=home - --data-dir=/var/lib/etcd/data - --listen-client-urls=http://0.0.0.0:2379 - --advertise-client-urls=http://home.kube.huskypup.net:61379 - --listen-peer-urls=http://0.0.0.0:2380 - --initial-advertise-peer-urls=http://home.kube.huskypup.net:61380 - --initial-cluster=home=http://home.kube.huskypup.net:61380,vps=http://100.108.113.41:2380,witness=http://100.108.130.74:2380 - --initial-cluster-state=new - --initial-cluster-token=ha-failover-quorum ports: - containerPort: 2379 name: client - containerPort: 2380 name: peer volumeMounts: - name: data mountPath: /var/lib/etcd resources: requests: cpu: 25m memory: 64Mi limits: memory: 256Mi volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] storageClassName: rook-ceph-block resources: requests: storage: 1Gi --- apiVersion: v1 kind: Service metadata: name: ha-etcd namespace: ha-failover spec: type: NodePort selector: app: ha-etcd ports: - name: client port: 2379 targetPort: 2379 nodePort: 32379 - name: peer port: 2380 targetPort: 2380 nodePort: 32380