Kubernetes od lat stanowi standard zarządzania konteneryzowanymi aplikacjami w środowiskach produkcyjnych i deweloperskich. Jednak jego domyślna architektura – zakładająca osobne maszyny wirtualne lub fizyczne serwery dla każdego węzła – często prowadzi do znacznego marnowania zasobów, szczególnie w małych i średnich wdrożeniach. Z drugiej strony, Proxmox VE, jako potężna platforma do zarządzania wirtualizacją opartą na KVM oraz kontenerami LXC, oferuje lekką i elastyczną alternatywę dla klasycznych hiperwizorów. Połączenie tych dwóch technologii – czyli uruchomienie klastra Kubernetes wewnątrz kontenerów Proxmox LXC – może przynieść wymierne korzyści: mniejsze zużycie pamięci i CPU, szybszy start węzłów oraz większą gęstość upakowania usług. Jednak taka konfiguracja nie jest oczywista. Kontenery LXC, choć bardzo wydajne, nakładają ograniczenia związane z dostępem do modułów jądra, konfiguracją cgroups czy uprawnieniami. W tym artykule pokażemy, jak krok po kroku przygotować środowisko Proxmox, skonfigurować kontenery LXC pod kątem wymagań Kubernetesa (w tym obsługę kubelet, containerd lub dockera) oraz postawić działający klaster – wszystko bez nadmiarowej wirtualizacji.
Przygotowanie serwera Proxmox
Zaczynamy od przygotowania hiperwizora od załadowania niezbędnych modułów jądra Linuxa do obsługi mostka sieciowego (bridge) oraz br_netfilter, który umożliwia iptables przetwarzanie ruchu sieciowego przechodzącego przez mostek. Należy także załadować moduł overlay (OverlayFS), to moduł jądra systemu Linux, który umożliwia stworzenie jednego, ujednoliconego systemu plików poprzez nałożenie jednego katalogu (lub systemu plików) na drugi.
cat <<EOF | cat >> /etc/modules
modprobe bridge
modprobe br_netfilter
modprobe overlay
overlay
EOF
Oryginalny kod Proxmox HA zawierał linię forceStop => 1 w pliku PVECT.pm, która, jak stwierdzono, powodowała nieoczekiwane i agresywne zamykanie kontenerów. Zmieniamy sposób zamykania kontenerów LXC zarządzanych przez HA, zmieniając wartość parametru forceStop z 1 (prawda) na 0 (fałsz).
sed -i 's/forceStop => 1/forceStop => 0/' /usr/share/perl5/PVE/HA/Resources/PVECT.pm
Template systemu operacyjnego dla kontenerów LXC
Będziemy potrzebowali templatki systemu operacyjnego, w tym przypadku debian-12-standard_12.7-1_amd64.tar.zst. Należy pobrać z http://download.proxmox.com/images/system/ i umieścić w lokalnym katalogu, np. local:vztmpl/ lub pobrać template do lokalnego storage przez UI proxmox-a:

Użyjemy go później w Terrafoemie:
ostemplate = "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
Użytkownik Proxmox i rola dla terraforma
Tworzymy urzytkownika i rolę w Proxmox na potrzeby Terraforma. Nadajemy mu odpowiednie uprawniena.
pveum role add TerraformProv -privs "Datastore.AllocateSpace Datastore.AllocateTemplate Datastore.Audit Pool.Allocate Pool.Audit Sys.Audit Sys.Console Sys.Modify VM.Allocate VM.Audit VM.Clone VM.Config.CDROM VM.Config.Cloudinit VM.Config.CPU VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.Migrate VM.PowerMgmt SDN.Use"
pveum user add terraform-prov@pve --password <password>
pveum aclmod / -user terraform-prov@pve -role TerraformProv
Terraform
Do terraforma użyjemy provider-a Telmate/proxmox, który jest odpowiedzialny za interakcję z API Proxmox-a i udostępnianie zasobów proxmox_vm_qemu i proxmox_lxc. Przykładowa konfiguracja providera:
terraform {
required_providers {
proxmox = {
source = "Telmate/proxmox"
version = "3.0.1-rc8"
}
}
}
provider "proxmox" {
pm_api_url = var.pm_api_url
pm_api_token_secret = var.pm_api_token_secret
pm_api_token_id = var.pm_api_token_id
pm_debug = true
pm_log_enable = true
pm_tls_insecure = true
pm_timeout = 3000
pm_log_file = "terraform-plugin-proxmox.log"
}
Całość kodu tarraform znajduje się w repozytorium GitHub:
https://github.com/krdian/kubernetes-proxmox-lxc
Terraform Plan i apply
Przed uruchomieniem terraform możemy przygotować plik .auto.tfvars: zawierający zmienne dla naszego klastra Proxmox i kubernetes, między innymi URL API Proxmoxa, utworziny uprzednio token, adres IP proxmox-a, rozmiar dysku dla węzła Kubernetes.
Przykładowy plik *.auto.tfvars:
pm_api_token_secret = "<token>"
pm_api_token_id = "terraform-prov@pve!terraform"
nameserver = "192.168.32.1"
ssh_public_keys = <<EOT
<ssh_public_key>
EOT
nodes = ["02", "03"]
ostemplate = "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
memory = 4096
swap = 0
rootfs = "local-lvm"
rootfs_size = "32G"
network_name = "eth0"
network_bridge = "vmbr0"
network_ip = "dhcp"
target_node = "pve"
target_node_ip = "192.168.32.140"
pm_api_url = "https://192.168.32.140:8006/api2/json"
k8s_version = "v1.33"
private_key_path = "/Users/<user>/.ssh/id_rsa"
Inicjujemy Terraform-a:
terraform init
Następnie puszczamy plan terraforma i jak wszystko przebiegnie pomyslnie to zatwierdzamy plan:
terraform plan
terraform apply
Po zakończeniu działania terraforma powinny pojawić się node-y k8s i klaster powinien zacząć działać:


W następnym artykule opiszę jak dodać do naszego klastra Cillium, MetalLb i Traefic.