From d19a2c379819d982687f614740bff555026e67a2 Mon Sep 17 00:00:00 2001 From: Vezpi Date: Tue, 8 Sep 2026 20:56:43 +0000 Subject: [PATCH] add: terraform project kubernetes_vms --- terraform/projects/kubernetes_vms/main.tf | 72 +++++++++++++++++++ terraform/projects/kubernetes_vms/provider.tf | 21 ++++++ .../projects/kubernetes_vms/variables.tf | 63 ++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 terraform/projects/kubernetes_vms/main.tf create mode 100644 terraform/projects/kubernetes_vms/provider.tf create mode 100644 terraform/projects/kubernetes_vms/variables.tf diff --git a/terraform/projects/kubernetes_vms/main.tf b/terraform/projects/kubernetes_vms/main.tf new file mode 100644 index 0000000..4653221 --- /dev/null +++ b/terraform/projects/kubernetes_vms/main.tf @@ -0,0 +1,72 @@ +module "pve_vm" { + source = "../../modules/pve_vm" + for_each = local.vm_list + node_name = each.value.node_name + vm_name = each.key + vm_cpu = each.value.vm_cpu + vm_ram = each.value.vm_ram + vm_vlan = each.value.vm_vlan + vm_tags = var.vm_tags +} + +locals { + node_stats = [ + for i, name in data.proxmox_virtual_environment_nodes.pve_nodes.names : { + name = name + free_mib = floor(data.proxmox_virtual_environment_nodes.pve_nodes.memory_available[i] / 1024 / 1024) + } + if data.proxmox_virtual_environment_nodes.pve_nodes.online[i] + ] + + max_free_mib = length(local.node_stats) > 0 ? max([for node in local.node_stats : node.free_mib]...) : 0 + + # Online nodes ordered by available memory, most available first (name as tie-break) + node_order = var.target_node != "" ? [var.target_node] : [ + for key in sort([ + for node in local.node_stats : "${format("%012d", local.max_free_mib - node.free_mib)}|${node.name}" + ]) : split("|", key)[1] + ] + + vm_layout = flatten([ + for role in ["master", "worker"] : [ + for i in range(role == "master" ? var.master_count : var.worker_count) : { + vm_name = "kub-${var.vm_env}-${substr(role, 0, 1)}${format("%02d", i + 1)}" + seq_index = (role == "master" ? 0 : var.master_count) + i + vm_role = role + } + ] + ]) + + vm_list = { + for vm in local.vm_layout : + vm.vm_name => { + node_name = length(local.node_order) > 0 ? local.node_order[vm.seq_index % length(local.node_order)] : "" + vm_cpu = var.vm_attr[vm.vm_role].cpu + vm_ram = var.vm_attr[vm.vm_role].ram + vm_vlan = var.vm_attr[vm.vm_role].vlan + vm_role = vm.vm_role + } + } + + roles = toset([for vm in local.vm_list : vm.vm_role]) +} + +data "proxmox_virtual_environment_nodes" "pve_nodes" {} + +output "vm_ip" { + value = { for k, v in module.pve_vm : k => v.vm_ip } +} + +resource "ansible_group" "vm_groups" { + for_each = local.roles + name = each.key +} + +resource "ansible_host" "vm_hosts" { + for_each = module.pve_vm + name = each.key + variables = { + ansible_host = each.value.vm_ip + } + groups = [local.vm_list[each.key].vm_role] +} \ No newline at end of file diff --git a/terraform/projects/kubernetes_vms/provider.tf b/terraform/projects/kubernetes_vms/provider.tf new file mode 100644 index 0000000..0b2729c --- /dev/null +++ b/terraform/projects/kubernetes_vms/provider.tf @@ -0,0 +1,21 @@ +terraform { + required_providers { + proxmox = { + source = "bpg/proxmox" + } + ansible = { + source = "ansible/ansible" + } + } +} + +provider "proxmox" { + endpoint = var.proxmox_endpoint + api_token = var.proxmox_api_token + insecure = false + ssh { + agent = false + private_key = file("~/.ssh/id_ed25519") + username = "root" + } +} \ No newline at end of file diff --git a/terraform/projects/kubernetes_vms/variables.tf b/terraform/projects/kubernetes_vms/variables.tf new file mode 100644 index 0000000..e94d4df --- /dev/null +++ b/terraform/projects/kubernetes_vms/variables.tf @@ -0,0 +1,63 @@ +variable "proxmox_endpoint" { + description = "Proxmox URL endpoint" + type = string +} + +variable "proxmox_api_token" { + description = "Proxmox API token" + type = string + sensitive = true +} + +variable "target_node" { + description = "Node which hosts the VMs when set; otherwise a node is selected based on available resources" + type = string + default = "" +} + +variable "master_count" { + description = "Number of Kubernetes control-plane (master) VMs to deploy" + type = number + default = 3 + + validation { + condition = var.master_count >= 1 + error_message = "master_count must be at least 1." + } +} + +variable "worker_count" { + description = "Number of Kubernetes worker VMs to deploy" + type = number + default = 3 + + validation { + condition = var.worker_count >= 1 + error_message = "worker_count must be at least 1." + } +} + +variable "vm_attr" { + description = "VM attributes" + type = map(object({ + ram = number + cpu = number + vlan = number + })) + default = { + "master" = { ram = 2048, cpu = 2, vlan = 66 } + "worker" = { ram = 4096, cpu = 2, vlan = 66 } + } +} + +variable "vm_env" { + description = "VM environment" + type = string + default = "prod" +} + +variable "vm_tags" { + description = "Tags for the VM" + type = list(any) + default = ["prod"] +} \ No newline at end of file