Packer i Terraform: generowanie obrazu Ubuntu 22.04-4 Server i automatyczne wdrażanie go na Proxmoxie
Przetłumaczone automatycznie z angielskiego. Przeczytaj oryginał

Jeśli zastanawiasz się, jak zautomatyzować tworzenie maszyn wirtualnych z Ubuntu, a następnie wdrażać je na Proxmoxie w wielu kopiach, to szukasz Packera i Terraforma.
Na marginesie: maszyny wirtualne w Proxmoxie to właściwa droga. Przez kilka dni próbowałem uruchomić kontenery LXC, jednak ostatecznie muszę powiedzieć, że nie jest to najlepsza opcja – wiele rzeczy się sypie, jak cgroups, AppArmor, zagnieżdżanie (nesting), FUSE, ruch przychodzący w sieci itd. Przy LXC jest po prostu za dużo do ogarnięcia, a przy VM takiego problemu nie ma, więc dyskusja kończy się tutaj na korzyść Proxmox Qemu. Kontenery LXC zostawcie do prostych rzeczy.
Po co automatyzować? #
Bo możemy.
Bo to lepszy sposób wykorzystania naszego czasu.
Bo to lepiej się skaluje.
Bo zapewnia pewną formę samodokumentacji.
Dlaczego Proxmox i VM z Ubuntu? #
Ubuntu to wiodąca dystrybucja Linuksa bez problemów licencyjnych, z 34% udziałem w rynku Linuksa. Ma silną bazę użytkowników. To także moja osobista preferencja. Daje nam możliwość wykupienia subskrypcji Ubuntu Pro, która zawiera kilka narzędzi związanych z compliance.
Z kolei Proxmox/Qemu kilka lat temu stał się oprogramowaniem do wirtualizacji klasy enterprise i jest również wiodącym rozwiązaniem open source w swojej dziedzinie. Zawiera funkcje klastrowania (w tym failover), a także obsługę różnych typów storage'u. W zależności od źródła ma około 1% udziału w rynku oprogramowania do wirtualizacji.
Instalacja Packera i Terraforma #
Ważne jest, aby zarówno Packer, jak i Terraform były we właściwych wersjach, pochodzących z oficjalnych repozytoriów. Co więcej, istotne jest to, że dokładny sposób budowania konkretnej wersji systemu operacyjnego różni się między wersjami – dlatego tytuł tego artykułu mówi o 22.04-4, a nie 22.04-3, bo mogą występować pewne różnice.
Zainstaluj właściwą wersję Packera. Wersja z pakietów Ubuntu jest nieprawidłowa i nie ma możliwości zarządzania pluginami, więc koniecznie zainstaluj Packera z oficjalnego repozytorium.
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install packerZainstaluj właściwą wersję Terraforma. Mając problemy z dołączoną wersją Packera, postanowiłem od razu pójść oficjalną drogą instalacji:
sudo apt-get update && sudo apt-get install -y gnupg software-properties-common
wget -O- https://apt.releases.hashicorp.com/gpg | \
gpg --dearmor | \
sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null
gpg --no-default-keyring \
--keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg \
--fingerprint
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
sudo apt-get install terraformTerraform Telmate/Proxmox #
Ważna uwaga dotycząca Terraforma i jego pluginu dla Proxmoxa. Ten plugin, podobnie jak API Proxmoxa w golangu, dostarcza jedna firma – Telmate LLC. Plugin ma pewne problemy z kompatybilnością i obecnie dla Proxmoxa 7 polecam używać Telmate/proxmox w wersji 2.9.0. Najnowsza wersja, czyli 2.9.14, ma pewne trudności z obsługą cloud-init, co daje 50% szans na VM wymagającą ręcznej rekonfiguracji dysków. Na dzień 2024/09/06 nie ma stabilnego wydania 3.0.1.
Jeśli masz najnowszą wersję i chcesz zrobić downgrade, usuń .terraform i .terraform.lock.hcl, a następnie zainicjalizuj ponownie poniższym poleceniem:
terraform initGenerowanie szablonu Ubuntu 22.04-4 dla Proxmoxa za pomocą Packera #
Kilka wersji temu projekt Ubuntu zmienił sposób automatyzacji instalacji. Zamiast seedowania masz teraz funkcję autoinstall. Struktura projektu Packera składa się z kilku plików, a zacznę od ubuntu-22-template/http/user-data zawierającego cloud-config:
#cloud-config
autoinstall:
version: 1
locale: en_US
ssh:
install-server: true
allow-pw: true
disable_root: true
ssh_quiet_keygen: true
allow_public_ssh_keys: true
packages:
- qemu-guest-agent
- sudo
storage:
layout:
name: lvm
sizing-policy: all
# password: xxx
user-data:
package_upgrade: false
timezone: Europe/Warsaw
users:
- name: temporary
groups: [sudo]
lock-passwd: false
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
passwd: "here you place SHA512 generated hash of a password"Aby włączyć LUKS, odkomentuj pole storage.layout.password i ustaw wybrane hasło. users.passwd można wygenerować za pomocą mkpasswd z użyciem SHA-512. Dalej jest ubuntu-22-template/files/99-pve.cfg:
datasource_list: [ConfigDrive, NoCloud]Dane uwierzytelniające dostają własny plik (./credentials.pkr.hcl). Można je oczywiście umieścić bezpośrednio w swoim pliku, jednak jeśli trzymasz te pliki w SCM, zostaną tam na stałe i będą współdzielone z innymi – dlatego warto ten plik wydzielić, a nawet nie dołączać go do commitów:
proxmox_api_url = "https://192.168.2.10:8006/api2/json"
proxmox_api_token_id = "root@pam!root-token"
proxmox_api_token_secret = "your Proxmox token"
my_ssh_password = "your new VM SSH password"Na koniec jest plik ubuntu-22-template/ubuntu-22-raw.pkr.hcl, w którym definiujesz zmienne, źródło i build. Wskazujemy obraz ISO i definiujemy parametry VM Proxmox VE Qemu. Najważniejszą i najbardziej zagadkową rzeczą jest podanie prawidłowego boot_command. Sekcje http* odnoszą się do Twojej maszyny serwującej pliki po HTTP, natomiast sekcja ssh* dotyczy konfiguracji maszyny zdalnej (nowo utworzonej VM na Proxmoxie). Nasza lokalna maszyna dostarcza po HTTP polecenia powłoki, które po przekazaniu do maszyny zdalnej są wykonywane podczas instalacji systemu.
variable "proxmox_api_url" {
type = string
}
variable "proxmox_api_token_id" {
type = string
}
variable "proxmox_api_token_secret" {
type = string
sensitive = true
}
variable "my_ssh_password" {
type = string
sensitive = true
}
source "proxmox-iso" "ubuntu-server-jammy" {
proxmox_url = "${var.proxmox_api_url}"
username = "${var.proxmox_api_token_id}"
token = "${var.proxmox_api_token_secret}"
insecure_skip_tls_verify = true
node = "lab"
vm_id = "141"
vm_name = "z10-ubuntu-22-template-RAW"
template_description = "Ubuntu Server Raw Encrypted"
iso_file = "local:iso/ubuntu-22.04.4-live-server-amd64.iso"
iso_storage_pool = "local"
unmount_iso = true
qemu_agent = true
scsi_controller = "virtio-scsi-single"
disks {
disk_size = "10G"
format = "raw"
storage_pool = "vms1"
storage_pool_type = "directory"
type = "virtio"
}
cores = "2"
memory = "4096"
network_adapters {
model = "virtio"
bridge = "vmbr0"
firewall = "false"
}
cloud_init = true
cloud_init_storage_pool = "local"
boot_command = [
"<esc><wait>",
"e<wait>",
"<down><down><down><end>",
"<bs><bs><bs><bs><wait>",
"ip=${cidrhost("192.168.2.0/24", 100)}::${cidrhost("192.168.1.0/24", 1)}:${cidrnetmask("192.168.0.0/22")}::::${cidrhost("1.1.1.0/24", 1)}:${cidrhost("9.9.9.0/24", 9)} ",
"autoinstall ds=nocloud-net\\;s=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ ---<wait>",
"<f10><wait>"
]
boot = "c"
boot_wait = "5s"
http_directory = "ubuntu-22-template/http"
http_bind_address = "IP of machine from which you will run Packer"
http_port_min = 8802
http_port_max = 8802
ssh_host = "192.168.2.100" # new VM proposed IP address
ssh_username = "temporary"
ssh_password = "${var.my_ssh_password}"
ssh_timeout = "20m"
}
build {
name = "ubuntu-server-jammy"
sources = ["proxmox-iso.ubuntu-server-jammy"]
provisioner "shell" {
inline = [
"while [ ! -f /var/lib/cloud/instance/boot-finished ]; do echo 'Waiting for cloud-init...'; sleep 1; done",
"sudo rm /etc/ssh/ssh_host_*",
"sudo truncate -s 0 /etc/machine-id",
"sudo apt -y autoremove --purge",
"sudo apt -y clean",
"sudo apt -y autoclean",
"sudo cloud-init clean",
"sudo rm -f /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg",
"sudo rm -f /etc/netplan/00-installer-config.yaml",
"sudo sync"
]
}
provisioner "file" {
source = "ubuntu-22-template/files/99-pve.cfg"
destination = "/tmp/99-pve.cfg"
}
provisioner "shell" {
inline = [ "sudo cp /tmp/99-pve.cfg /etc/cloud/cloud.cfg.d/99-pve.cfg" ]
}
}Aby to uruchomić:
packer build -var-file=credentials.pkr.hcl ubuntu-22-template/ubuntu-22-raw.pkr.hclProces instalacji jest zautomatyzowany i nie widzisz zwykłych ekranów konfiguracyjnych. Zamiast tego dostarczamy konfigurację autoinstall i zostawiamy kilka opcji do ustawienia później przez cloud-init, czyli dane użytkownika i szczegóły konfiguracji sieci. W ten sposób możemy zautomatyzować wdrożenia takiego systemu, co pokażę za chwilę w części tego artykułu poświęconej Terraformowi.
Pełny przegląd struktury projektu wygląda następująco:
├── credentials.pkr.hcl
├── main.tf
├── packer_cache
│ └── port
├── terraform.tfstate
├── terraform.tfstate.backup
└── ubuntu-22-template
├── files
│ └── 99-pve.cfg
├── http
│ ├── meta-data
│ └── user-data
└── ubuntu-22-raw.pkr.hcl
5 directories, 8 filesPo udanej instalacji Ubuntu system zrestartuje się i przekonwertuje się w szablon, dzięki czemu będzie mógł później posłużyć jako baza dla kolejnych systemów, jako linked clone albo full clone. Jeśli zależy Ci na dużej elastyczności, wybierz full clone, bo nie będziesz mieć żadnych ograniczeń dotyczących użytkowania i migracji VM.
Wdrażanie wielu VM z Ubuntu za pomocą Terraforma #
Szablonu VM w Proxmoxie można użyć do utworzenia nowej VM ręcznie z poziomu UI Proxmoxa. Jednak w przypadku tworzenia 100 VM mogłoby to trochę potrwać. Jest więc narzędzie Terraform, które z pomocą pluginów potrafi połączyć się z Proxmoxem i zautomatyzować ten proces za Ciebie.
Zdefiniuj plik Terraforma (.tf) z sekcjami terraform, provider i resource. Sekcja terraform mówi Terraformowi, jakich pluginów będziesz używać. Sekcja provider określa, jak uzyskać dostęp do środowiska wirtualizacji Proxmox. Na koniec sekcja resource, w której umieszczasz całą konfigurację związaną z Twoim Ubuntu 22.04-4 wspartym przez cloud-init. Zaczynamy więc od terraform i wymaganych pluginów providera. W zależności od wersji Proxmoxa – 7 czy 8 – trzeba będzie podać inną konfigurację zasobu:
terraform {
required_providers {
proxmox = {
source = "telmate/proxmox"
version = "2.9.0" # this version has the greatest compatibility
}
}
}Następnie umieszczasz providera Proxmox. Wszystkie wrażliwe dane można też zdefiniować jako zmienne:
provider "proxmox" {
pm_api_url = "https://192.168.2.10:8006/api2/json"
pm_user = "root@pam"
pm_password = "xxx"
pm_tls_insecure = true
}Najpierw trzeba zainicjalizować „backend” Terraforma i zainstalować pluginy. Jeśli chcesz, możesz to zrobić mając tylko sekcje terraform i provider. Możesz to też zrobić po ukończeniu pełnej specyfikacji pliku tf.
terraform initNa koniec sam zasób:
resource "proxmox_vm_qemu" "ubuntu_vm" {
name = "z10-ubuntu-22-from-terraform-1-20"
target_node = "lab"
clone = "z10-ubuntu-22-template-RAW"
memory = 4000
cores = 2
network {
bridge = "vmbr0"
model = "virtio"
}
disk {
slot = 0
storage = "vms1"
size = "10G"
type = "virtio"
}
os_type = "cloud-init"
ipconfig0 = "ip=192.168.2.20/22,gw=192.168.1.1"
ciuser = "xxx"
cipassword = "xxx"
}Aby uruchomić ten skrypt Terraforma, najpierw sprawdzasz go poleceniem plan, a wykonujesz poleceniem apply:
terraform plan
terraform applyDzięki temu mechanizm ten wykona pełny klon szablonu jako nową maszynę wirtualną z podanymi definicjami cloud-init dotyczącymi użytkownika i konfiguracji sieci.

Przygotowałem dwa przykładowe szablony – jeden z szyfrowaniem dysku LUKS, a drugi bez szyfrowania LUKS. Na potrzeby demo wystarczy niezaszyfrowany dysk, jednak w produkcji powinien to być Twój domyślny sposób instalowania systemów operacyjnych.

Checkpoint: utworzyliśmy szablon Ubuntu za pomocą Packera i użyliśmy go do utworzenia nowej VM za pomocą Terraforma.
Dalsza lektura #
- https://github.com/Telmate/terraform-provider-proxmox/tree/v2.9.0
- https://registry.terraform.io/providers/Telmate/proxmox/2.9.0/docs/resources/vm_qemu