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]>
This commit is contained in:
1 parent
224cd8ac35
commit
f2a7e42068
29 files changed
+1168
-6
No files matched your search
+56
-2
@@ -1,3 +1,57 @@
|
||||
# infra/tofu
|
||||
# infra/tofu — dev/test cluster nodes (OpenTofu + libvirt)
|
||||
|
||||
_Phase 1 (T-1.x) — to be authored on execution go. See infra/PHASE1-SCOPE.md._
|
||||
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
|
||||
```
|
||||
Reference in new issue
Block a user