Om te gaan testen met kubernetes wilde ik een test omgeving hebben die ik snel op en af kan bouwen. Het meerst hanidige is dan om gebruik te maken van terraform. Met een terraform apply maak je dan een kubernetes cluster aan en met terraform destroy kan je hem dan weer helemaal afbreken. Er kunnen nog wat verbeteringen aan onderstaande worden aangebracht, maar het moest snel 🙂
Zorg ervoor dat je een machine hebt waarop je terraform geinstalleerd hebt en die naar de proxmox node kan om API calls te doen. Zorg er dus ook voor dat je een user hebt die een API token heeft.
Verder moet je ook talosctl en kubectl installeren.
Maak ook in je DHCP server enkele reserveringen met de MAC addressen uit de main.tf en de ip adressen eraan gekoppeld. Aangezien we name,ijk de metal-amd64.iso gebruiken kunnen we geen ip adressen opgeven bij het starten van de talos VM.
Maak vervolgens het bestand main.tf aan met de volgende inhoud. Pas onder local de gegevens aan zoals je ze graag zou willen hebben.
terraform {
required_providers {
proxmox = {
source = "telmate/proxmox"
version = "3.0.2-rc04"
}
talos = {
source = "siderolabs/talos"
version = "0.9.0"
}
local = {
source = "hashicorp/local"
version = "~> 2.4"
}
}
}
# variable "proxmox_server" {}
# variable "proxmox_tokenid" {}
# variable "proxmox_api_token_secret" {
# sensitive = true
# }
provider "proxmox" {
pm_api_url = "https://${var.proxmox_server}:8006/api2/json"
pm_api_token_id = var.proxmox_tokenid
pm_api_token_secret = var.proxmox_api_token_secret
pm_tls_insecure = true
}
locals {
cluster_name = "talos-cluster"
k8s_endpoint = "https://192.168.11.130:6443"
iso_file = "ssd-disk1:iso/metal-amd64.iso"
proxmox_node = "pmx"
network_bridge = "vmbr0"
disk_storage = "ssd-disk1"
gateway = "192.168.11.1"
subnet_cidr = "24"
nodes = {
controlplane = {
ip = "192.168.11.130"
vmid = 140
role = "controlplane"
cores = 2
mac = "BC:24:11:21:6A:01"
memory = 4096
disk = "/dev/sda"
hostname = "talos-cp"
}
worker1 = {
ip = "192.168.11.131"
vmid = 141
role = "worker"
cores = 2
mac = "BC:24:11:D4:E0:F7"
memory = 2048
disk = "/dev/sda"
hostname = "talos-w1"
}
worker2 = {
ip = "192.168.11.132"
vmid = 142
role = "worker"
cores = 2
mac = "BC:24:11:51:D1:E1"
memory = 2048
disk = "/dev/sda"
hostname = "talos-w2"
}
worker3 = {
ip = "192.168.11.133"
vmid = 143
role = "worker"
cores = 2
mac = "BC:24:11:B7:09:E4"
memory = 2048
disk = "/dev/sda"
hostname = "talos-w3"
}
}
}
# --- Talos Secrets ---
resource "talos_machine_secrets" "machine_secrets" {}
# --- Talos Machine Configurations ---
data "talos_machine_configuration" "config" {
for_each = local.nodes
cluster_name = local.cluster_name
cluster_endpoint = local.k8s_endpoint
machine_type = each.value.role
talos_version = "v1.7.0"
machine_secrets = talos_machine_secrets.machine_secrets.machine_secrets
config_patches = [
yamlencode({
machine = {
install = {
disk = each.value.disk
bootloader = true
}
network = {
nameservers = ["192.168.10.1"]
interfaces = [{
interface = "eth0"
dhcp = false
addresses = ["${each.value.ip}/${local.subnet_cidr}"]
routes = [{
network = "0.0.0.0/0"
gateway = local.gateway
}]
}]
hostname = each.value.hostname
}
}
})
]
}
# --- Proxmox VM Creation ---
resource "proxmox_vm_qemu" "talos_nodes" {
for_each = local.nodes
name = "talos-${each.key}"
target_node = local.proxmox_node
vmid = each.value.vmid
cores = each.value.cores
memory = each.value.memory
bios = "ovmf"
scsihw = "virtio-scsi-pci"
disk {
slot = "scsi0"
type = "disk"
storage = local.disk_storage
size = "32G"
}
network {
id = 0
bridge = local.network_bridge
model = "virtio"
macaddr = each.value.mac
}
disk {
slot = "ide2"
type = "cdrom"
iso = local.iso_file
}
boot = "order=scsi0;ide2"
agent = 0
}
# --- Talos Client Config ---
data "talos_client_configuration" "client" {
cluster_name = local.cluster_name
client_configuration = talos_machine_secrets.machine_secrets.client_configuration
endpoints = [local.nodes.controlplane.ip]
nodes = [for n in local.nodes : n.ip]
}
resource "local_file" "talosconfig_file" {
content = data.talos_client_configuration.client.talos_config
filename = "${path.module}/talosconfig"
}
# # --- Apply Configurations ---
# resource "talos_machine_configuration_apply" "apply" {
# for_each = local.nodes
# client_configuration = talos_machine_secrets.machine_secrets.client_configuration
# machine_configuration_input = data.talos_machine_configuration.config[each.key].machine_configuration
# node = each.value.ip
# apply_mode = "auto"
# depends_on = [
# proxmox_vm_qemu.talos_nodes,
# local_file.talosconfig_file
# ]
# }
# Apply CP first
resource "talos_machine_configuration_apply" "controlplane" {
for_each = { for k,v in local.nodes : k => v if v.role == "controlplane" }
client_configuration = talos_machine_secrets.machine_secrets.client_configuration
machine_configuration_input = data.talos_machine_configuration.config[each.key].machine_configuration
node = each.value.ip
apply_mode = "auto"
# Optional: give install time
# timeouts { create = "20m" } # adjust if your storage is slow
}
# Apply workers after CP
resource "talos_machine_configuration_apply" "workers" {
depends_on = [talos_machine_configuration_apply.controlplane]
for_each = { for k,v in local.nodes : k => v if v.role == "worker" }
client_configuration = talos_machine_secrets.machine_secrets.client_configuration
machine_configuration_input = data.talos_machine_configuration.config[each.key].machine_configuration
node = each.value.ip
apply_mode = "auto"
# timeouts { create = "20m" }
}
# --- Update Boot Order & Reboot via Proxmox API ---
# resource "null_resource" "update_boot_order" {
# depends_on = [talos_machine_configuration_apply.workers]
# provisioner "local-exec" {
# command = <<-EOT
# echo "Updating boot order to disk-only and rebooting VMs via Proxmox API..."
# for vmid in ${join(" ", [for n in local.nodes : n.vmid])}; do
# curl -s -k -X PUT \
# -H "Authorization: PVEAPIToken=${var.proxmox_tokenid}=${var.proxmox_api_token_secret}" \
# -d "boot=order=scsi0" \
# "https://${var.proxmox_server}:8006/api2/json/nodes/${local.proxmox_node}/qemu/$vmid/config"
# curl -s -k -X POST \
# -H "Authorization: PVEAPIToken=${var.proxmox_tokenid}=${var.proxmox_api_token_secret}" \
# "https://${var.proxmox_server}:8006/api2/json/nodes/${local.proxmox_node}/qemu/$vmid/status/reboot"
# done
# EOT
# }
# }
# --- Bootstrap Cluster ---
resource "talos_machine_bootstrap" "bootstrap" {
depends_on = [talos_machine_configuration_apply.workers]
node = local.nodes.controlplane.ip
client_configuration = talos_machine_secrets.machine_secrets.client_configuration
endpoint = local.nodes.controlplane.ip
}
# --- Retrieve Kubeconfig ---
data "talos_cluster_kubeconfig" "kubeconfig" {
depends_on = [talos_machine_bootstrap.bootstrap]
node = local.nodes.controlplane.ip
client_configuration = talos_machine_secrets.machine_secrets.client_configuration
}
resource "local_file" "kubeconfig_file" {
content = data.talos_cluster_kubeconfig.kubeconfig.kubeconfig_raw
filename = "${path.module}/kubeconfig"
}
output "config" {
value = data.talos_client_configuration.client.talos_config
sensitive = true
}
output "kubeconfig" {
value = data.talos_cluster_kubeconfig.kubeconfig.kubeconfig_raw
sensitive = true
}
Vervolgens maken we een bestand aan terraform.tfvars met de volgende inhoud. Pas ook hier weer alles aan zoals je het graag zou willen hebben:
proxmox_server = "192.168.11.12"
proxmox_user = "root"
proxmox_password = "Password" # Sensitive data
proxmox_tokenid = "root@pam!terraform"
proxmox_api_token_secret = "secret_token_api" # Sensitive data
proxmox_node = "pmx"
storage_name = "ssd-disk1" # Or whatever your actual storage name is
iso_file = "ssd-disk1:iso/metal-amd64.iso" # Optional, you can modify as needed
En nog een variables.tf met de volgende inhoud
variable "proxmox_server" {
description = "The Proxmox server's IP or hostname"
type = string
}
variable "proxmox_user" {
description = "The Proxmox username"
type = string
}
variable "proxmox_password" {
description = "The Proxmox password"
type = string
sensitive = true
}
variable "proxmox_api_token_secret" {
description = "The Proxmox secret"
type = string
sensitive = true
}
variable "proxmox_tokenid" {
description = "The Proxmox tokenid"
type = string
sensitive = true
}
variable "proxmox_node" {
description = "The Proxmox node to deploy the VMs"
type = string
}
variable "storage_name" {
description = "The storage resource name in Proxmox"
type = string
}
variable "iso_file" {
description = "The path to the Talos ISO file"
type = string
default = "iso/metal-amd64.iso" # Optional default value
}
En daarna kunnen we een terraform init, terraform validate, terraform plan en terraform apply doen. Ons cluster wordt dan opgebouwd.
Om vervolgens kubectl en talosctl, welke je ook moet installeren, te gebruiken kun je de volgende commando's gebruiken vanuit de directopry van waarin je terraform apply gestart hebt;kubectl --kubeconfig kubeconfig get nodes
en talosctl --talosconfig ./talosconfig -n 192.168.11.131 -e 192.168.11.130 health
De bestanden kubeconfig en talosconfig worden aagemaakt met de terraform apply.
Het is handig om de pods ook van buiten het clustyer bereikbaar te maken. Omdat het om een baremetal omgeving gaat is daar metallb handig voor. Om die te kunnen gebruiken doen we het volgende:kubectl apply --kubeconfig kubeconfig -f https://raw.githubusercontent.com/metallb/metallb/v0.14.5/config/manifests/metallb-native.yaml
Daarna maken we een ipaddresses.yml aan met de volgende content:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 192.168.11.136-192.168.11.139
En een bestand layer2.yml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: first-pool
namespace: metallb-system
En die gebruiken we dan in het volgende commando's:kubectl create -f metallb/ipaddresses.yml --kubeconfig kubeconfigkubectl create -f metallb/layer2.yml --kubeconfig kubeconfig
Leuk om ook iets te laten draaien via deze load balancer. We doen een nginx deploymentkubectl create deploy nginx --image nginx:latest --kubeconfig kubeconfig
En exposen deze dan via de loadbalancer met;kubectl expose deploy nginx --port 80 --type LoadBalancer --kubeconfig kubeconfig
Als alles goed is gegaan kunnen we nu met het volgende commando kijken wat het "externe" ip adres is:kubectl get svc --kubeconfig kubeconfig
