Files
home-network-k3s-impl/infra/tofu/variables.tf
jakman85andClaude Opus 4.8 676c37a247 tofu: node vCPU 6 -> 8 (CPU spike headroom)
Steady-state CPU PSI 15-23% + 15-min load 5.6-8.0 on 6 cores (run queue at/over core count) left no
headroom for bursty workloads. Proxmox host has 24 cores at ~4% load, so 3x8 = 24 vCPU is 1:1 with
zero oversubscription. In-place tofu update (cores change is pending until a per-node VM stop/start);
rolled one node at a time to keep etcd quorum. Comment also corrected 16->24 GiB (stale).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-30 23:47:49 -05:00

222 lines
7.7 KiB
Terraform

# variables.tf — every sizing / IP / image knob is a variable (Proxmox migration).
# Defaults encode the migration target: 3 k3s nodes on the Proxmox VE box
# (node "proxmox", 192.168.1.0/24 LAN). Override via terraform.tfvars (see
# terraform.tfvars.example). Traces R-ENV-01 (reproducible node provisioning),
# R-PLAT-01.
# --- Proxmox connection / placement ----------------------------------------
variable "proxmox_endpoint" {
description = <<-EOT
Proxmox VE API endpoint, e.g. "https://192.168.2.102:8006/". Optional: if unset
the provider reads PROXMOX_VE_ENDPOINT from the environment directly. Set this
(e.g. via `export TF_VAR_proxmox_endpoint="$PROXMOX_VE_ENDPOINT"`) to let tofu
NORMALIZE a schemeless endpoint (prepend https://) — see versions.tf. The API
TOKEN is NOT a variable; it is read from PROXMOX_VE_API_TOKEN by the provider so
it never enters tofu state/vars.
EOT
type = string
default = null
}
variable "proxmox_insecure" {
description = "Skip TLS verification for the Proxmox self-signed cert (also read from PROXMOX_VE_INSECURE)."
type = bool
default = true
}
variable "proxmox_node" {
description = "Proxmox VE node name that hosts the VMs (VERIFIED: proxmox)."
type = string
default = "proxmox"
}
variable "datastore" {
description = "Datastore for VM OS + data disks (VERIFIED: Storage, ZFS 4.3TB, rootdir+images)."
type = string
default = "Storage"
}
variable "image_datastore" {
description = "Datastore that holds the downloaded cloud image / cloud-init snippets (VERIFIED: local, dir storage)."
type = string
default = "local"
}
variable "bridge" {
description = "Proxmox network bridge for the node NIC (VERIFIED: vmbr0, no VLAN tag)."
type = string
default = "vmbr0"
}
# --- cluster identity / naming ---------------------------------------------
variable "cluster_name" {
description = "Logical cluster name; used as a resource-name prefix."
type = string
default = "k3s-dev"
}
variable "node_name_prefix" {
description = "VM hostname prefix. Nodes are <prefix>1, <prefix>2, ... (LOCKED: k3s-dev-1/2/3)."
type = string
default = "k3s-dev-"
}
variable "node_count" {
description = "Number of k3s nodes (LOCKED: 3-server HA embedded etcd)."
type = number
default = 3
validation {
condition = var.node_count == length(var.node_ips)
error_message = "node_count must equal the number of entries in node_ips."
}
}
# --- per-node sizing (Proxmox target: 8 vCPU / 24 GiB / 64 GiB OS + 128 GiB data) --
variable "vcpu" {
# 8 (was 6): steady-state CPU PSI was 15-23% and 15-min load 5.6-8.0 on 6 cores (run queue at/over
# the core count) — bursty workloads had no spike headroom. The Proxmox host has 24 physical cores
# at ~4% load, so 3x8 = 24 vCPU is a clean 1:1 with zero oversubscription. Roll one node at a time
# (etcd quorum) — `qm set` cores needs a VM stop/start, not a reboot, to take effect.
description = "vCPUs (cores) per node. Generous — the box has plenty."
type = number
default = 8
}
variable "memory_mib" {
description = "RAM per node in MiB (24 GiB)."
type = number
default = 24576
}
variable "os_disk_gib" {
description = "OS (root) disk size per node, GiB. Imported from the cloud image, on the Storage datastore."
type = number
default = 64
}
variable "data_disk_gib" {
description = "Dedicated Longhorn data disk per node, GiB. Blank, on the Storage datastore."
type = number
default = 128
}
# --- dedicated SSD etcd disk (interim etcd-on-SSD isolation, docs/plans/ssd-etcd-isolation.md) --
# etcd WAL fsync was being starved by Longhorn replica-rebuild I/O on the shared HDD
# `Storage` pool (OS + Longhorn disks). A tiny dedicated SSD disk per node moves the
# etcd data dir (/var/lib/rancher/k3s/server/db, mounted by node_baseline) off the HDD.
# Interim bridge until the all-SSD mini-PCs land; Longhorn stays on HDD (does not fit).
variable "etcd_ssd_datastore" {
description = "SSD-backed datastore for the dedicated etcd disk (VERIFIED: local-zfs = rpool/data, ZFS mirror on Micron 5100 SATA SSDs w/ power-loss protection)."
type = string
default = "local-zfs"
}
variable "etcd_disk_gib" {
description = "Dedicated SSD etcd disk per node, GiB (etcd DB is ~83 MB; 16 GiB is generous headroom)."
type = number
default = 16
}
variable "cpu_type" {
description = "Proxmox vCPU type. `host` passes through the physical CPU (nested-virt for KubeVirt, Phase 5)."
type = string
default = "host"
}
# --- network (Proxmox target: main LAN 192.168.1.0/24, gw .1, DNS PiHole .18) ---
variable "network_cidr" {
description = "Cluster subnet in CIDR (informational; nodes get static IPs from node_ips)."
type = string
default = "192.168.1.0/24"
}
variable "network_prefix" {
description = "Netmask prefix length for node static addressing."
type = number
default = 24
}
variable "network_gateway" {
description = "Default gateway for nodes (VERIFIED: 192.168.1.1, pfSense)."
type = string
default = "192.168.1.1"
}
variable "dns_server" {
description = "DNS resolver for nodes (VERIFIED: 192.168.1.18, PiHole)."
type = string
default = "192.168.1.18"
}
variable "node_ips" {
description = "Static IPs per node, in order (VERIFIED: .50/.51/.52 on 192.168.1.0/24)."
type = list(string)
default = ["192.168.1.50", "192.168.1.51", "192.168.1.52"]
}
# Reserved for Cilium L2-announcement ingress VIPs (VERIFIED: .53-.60).
# Not materialised as Proxmox objects; surfaced in outputs. Cilium's IP pool
# (platform-config/cilium/l2-ip-pool.yaml) consumes this range.
variable "cilium_l2_vip_range_start" {
description = "First reserved Cilium L2 VIP."
type = string
default = "192.168.1.53"
}
variable "cilium_l2_vip_range_end" {
description = "Last reserved Cilium L2 VIP."
type = string
default = "192.168.1.60"
}
# --- base OS image ----------------------------------------------------------
variable "image_source" {
description = <<-EOT
Source URL for the base OS cloud image (qcow2), downloaded to image_datastore
by proxmox_virtual_environment_download_file. Default is Debian 12 (bookworm)
generic amd64 (full firmware/driver, hybrid BIOS/UEFI). Use `generic`, NOT
`genericcloud`: the genericcloud image panics with "Attempted to kill init" on
import + disk-resize under bpg (bpg issue #1639).
EOT
type = string
default = "https://cloud.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2"
}
variable "image_file_name" {
description = <<-EOT
File name the cloud image is stored as on image_datastore. With `import`-content
storage (PVE 8.2+) the native .qcow2 name is used directly — the old .img rename
was an `iso`-content workaround and is no longer needed. Uses the `generic`
(full firmware/driver, hybrid BIOS/UEFI) image, NOT `genericcloud`: genericcloud
panics with "Attempted to kill init" on import + disk-resize (bpg issue #1639).
EOT
type = string
default = "debian-13-generic-amd64.qcow2"
}
# --- cloud-init identity ----------------------------------------------------
variable "ssh_authorized_key" {
description = "SSH PUBLIC key injected into the login user (public only — never a private key)."
type = string
}
variable "login_user" {
description = "Non-root login user created by cloud-init; also the Ansible ansible_user."
type = string
default = "k3s"
}
variable "node_domain" {
description = "DNS domain suffix for node FQDNs (informational; dev uses split-horizon / hosts)."
type = string
default = "k3s.dev.local"
}