Files
homelab/terraform/talos/cluster/main.tf
Ryan Blundon 97e9889251 feat(fastpass): Talos cluster provisioning and bootstrap
- Terraform: VM provisioning, Unifi DHCP, Technitium DNS
- talhelper: cluster config for 6-node Talos cluster
- Cilium 1.19.4 CNI with Talos-compatible security context
- docs: city-hall setup guide and bootstrap runbook
2026-05-17 16:08:19 -05:00

152 lines
5.2 KiB
HCL

# ─── fastpass Talos Cluster ───────────────────────────────────────────────────
#
# Provisions all infrastructure for the fastpass Kubernetes cluster:
# - Talos ISO download to Proxmox
# - 3 control plane VMs (space-mountain, big-thunder-mountain, splash-mountain)
# - 3 worker VMs (jungle-cruise, haunted-mansion, peter-pans-flight)
# - Unifi DHCP reservations (MAC → IP) for all 6 nodes
# - Technitium DNS A records for all 6 nodes + cluster VIP
#
# After apply:
# Run talhelper from city-hall to generate and apply machine configs.
# See talos/talhelper/ for cluster configuration.
#
# IP Allocation (10.1.71.64/26 — Container Orchestration):
# .65 — Kubernetes API VIP (Talos native VIP, not a VM)
# .66 — space-mountain (control plane, main-street-usa)
# .67 — big-thunder-mountain (control plane, tomorrowland)
# .68 — splash-mountain (control plane, fantasyland)
# .69 — jungle-cruise (worker, main-street-usa)
# .70 — haunted-mansion (worker, tomorrowland)
# .71 — peter-pans-flight (worker, fantasyland)
# .80-.99 — Cilium LB pool (managed by Cilium, not Terraform)
locals {
# Node definitions — single source of truth for the entire cluster.
# VM ID convention: {third_octet}{fourth_octet:03d}
nodes = {
"space-mountain" = {
role = "controlplane"
target_node = "main-street-usa"
ip_address = "10.1.71.66"
vm_id = 71066
cpu_cores = 4
memory_mb = 8192
disk_size = 50
}
"big-thunder-mountain" = {
role = "controlplane"
target_node = "tomorrowland"
ip_address = "10.1.71.67"
vm_id = 71067
cpu_cores = 4
memory_mb = 8192
disk_size = 50
}
"splash-mountain" = {
role = "controlplane"
target_node = "fantasyland"
ip_address = "10.1.71.68"
vm_id = 71068
cpu_cores = 4
memory_mb = 8192
disk_size = 50
}
"jungle-cruise" = {
role = "worker"
target_node = "main-street-usa"
ip_address = "10.1.71.69"
vm_id = 71069
cpu_cores = 8
memory_mb = 16384
disk_size = 100
}
"haunted-mansion" = {
role = "worker"
target_node = "tomorrowland"
ip_address = "10.1.71.70"
vm_id = 71070
cpu_cores = 8
memory_mb = 16384
disk_size = 100
}
"peter-pans-flight" = {
role = "worker"
target_node = "fantasyland"
ip_address = "10.1.71.71"
vm_id = 71071
cpu_cores = 8
memory_mb = 16384
disk_size = 100
}
}
}
# ─── Talos ISO ───────────────────────────────────────────────────────────────
# ISO is pre-uploaded to the shared 'templates' datastore (Ceph-backed,
# visible to all nodes). File ID format: <datastore>:iso/<filename>
locals {
talos_iso_file_id = "${var.iso_datastore}:iso/metal-amd64.iso"
}
# ─── Cluster Nodes ───────────────────────────────────────────────────────────
# Iterate over the node map and call the node module for each.
module "nodes" {
source = "../modules/node"
for_each = local.nodes
hostname = each.key
vm_id = each.value.vm_id
node_role = each.value.role
target_node = each.value.target_node
iso_file_id = local.talos_iso_file_id
cpu_cores = each.value.cpu_cores
memory_mb = each.value.memory_mb
disk_size = each.value.disk_size
datastore = var.datastore
bridge = var.bridge
}
# ─── Unifi DHCP Reservations ─────────────────────────────────────────────────
# Create a static MAC→IP reservation for each node.
# Reservations are created before VMs start so the IP is guaranteed
# on first DHCP broadcast.
resource "unifi_user" "nodes" {
for_each = local.nodes
name = each.key
mac = lower(module.nodes[each.key].mac_address)
fixed_ip = each.value.ip_address
note = "Managed by mk-labs Terraform — fastpass cluster ${each.value.role}"
depends_on = [module.nodes]
}
# ─── DNS A Records ───────────────────────────────────────────────────────────
# Create A records for all 6 nodes in the internal zone.
# The cluster VIP (.65) is managed by Talos natively — no VM, but still
# needs a DNS entry for kubectl and talhelper to resolve the endpoint.
resource "technitium_record" "nodes" {
for_each = local.nodes
zone = var.dns_zone
name = "${each.key}.${var.dns_zone}"
type = "A"
ttl = 300
value = each.value.ip_address
}
resource "technitium_record" "vip" {
zone = var.dns_zone
name = "${var.cluster_name}-api.${var.dns_zone}"
type = "A"
ttl = 300
value = "10.1.71.65"
}