Files
home-network-k3s-impl/infra/tofu/README.md
T
jakman85andClaude Opus 4.8 f2a7e42068 Phase 1 (author-only): node provisioning IaC — tofu + cloud-init + Ansible (T-1.1/1.2/1.3)
OpenTofu libvirt module (k3s-dev NAT 10.73.0.0/24, 3x 6GiB/4vCPU VMs, 20G OS +
40G thin data, host-passthrough), cloud-init baseline (k3s/Cilium/Longhorn
prereqs, static net), Ansible k3s-HA + Cilium (kube-proxy replacement, Gateway
API, Hubble; flannel/kube-proxy/Traefik disabled). PM fix: escape ${...} in a
cloud-init template comment (tofu templatefile parse). tofu fmt/init/validate =
Success; ansible --syntax-check + yamllint pass. NOTHING executed (no apply).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-16 01:21:00 -05:00

58 lines
2.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# infra/tofu — dev/test cluster nodes (OpenTofu + libvirt)
Phase 1 **T-1.1**. Provisions the "Create" lifecycle stage (docs/02): a dedicated
NAT network and 3 cloud-init'd KVM VMs, each with a thin OS disk and a blank thin
Longhorn data disk. **Authoring only — nothing here has been applied.**
Serves **R-ENV-01/02**, **R-PLAT-01**, ADR-0001 (k3s substrate), and the network
half of the LOCKED PHASE1-SCOPE decisions.
## What it builds (defaults = LOCKED decisions)
| Resource | Value |
|---|---|
| NAT network | `k3s-dev`, `10.73.0.0/24`, gateway `10.73.0.1`, **DHCP off** (static IPs) |
| Nodes | `k3s-dev-1/2/3` at `10.73.0.11/.12/.13` |
| Per node | 4 vCPU, 6 GiB RAM, **host-passthrough** CPU (nested virt for KubeVirt) |
| OS disk | 20 GiB thin qcow2, backed by the base cloud image |
| Data disk | 40 GiB blank thin qcow2 → `/dev/vdb`, mounted at `/var/lib/longhorn` by cloud-init |
| Seed | `libvirt_cloudinit_disk` wired to `../cloud-init/` templates |
| Reserved | `10.73.0.240–.250` kept out of any DHCP range for Cilium L2 ingress VIPs |
Base OS image is a **variable** (`image_source`), defaulting to Debian 12
genericcloud amd64. All sizing/IPs/counts are variables — see `variables.tf`.
## Files
- `versions.tf` — OpenTofu `1.9.0` pin + `dmacvicar/libvirt ~> 0.8.1` provider pin.
- `variables.tf` — every knob (counts, sizing, IPs, image, SSH key).
- `main.tf` — network, base/OS/data volumes, cloud-init seed, domains.
- `outputs.tf` — node IPs/names, `primary_node_ip`, `k3s_api_endpoint`, and a
rendered `ansible_inventory` for `infra/ansible/`.
- `terraform.tfvars.example` — copy to `terraform.tfvars`; only `ssh_authorized_key` is required.
## Usage (operator, in the dev container — NOT run here)
```
cd infra/tofu
cp terraform.tfvars.example terraform.tfvars # set ssh_authorized_key
tofu init
tofu plan
tofu apply
# hand the node IPs to Ansible:
tofu output -raw ansible_inventory > ../ansible/inventory/hosts.ini
```
## Hand-off contract
- `primary_node_ip` / `k3s_api_endpoint` (`10.73.0.11:6443`) is the value the
Ansible Cilium bootstrap and the Flux `platform/cilium` HelmRelease must BOTH
use for `k8sServiceHost` / `k8sServicePort` (docs/02 parity contract).
- Destroy/rebuild proof (T-1.5): `tofu destroy -target=libvirt_domain.node[1]`
then `tofu apply` re-creates that node; Ansible re-joins it; Longhorn replicas
reconverge (nodes are cattle — docs/02 statelessness).
## Static validation (PM, in dev container)
```
tofu fmt -check -recursive
tofu init -backend=false
tofu validate
```