Automatische Image Erstellung mit Packer
Mittlerweile gibt es viele verschiedene Virtualisierer, um Systeme für Test und Produktion zur Verfügung zu stellen: Docker, KVM, VMware, Proxmox, Virtualbox, HyperV, ….
Allen gemein bleibt, dass sie ein Image mit einem installierten Betriebssystem für die von ihnen zur Verfügung gestellten VMs benötigen.
Neben der altbewährten, aber zeitaufwendigen manuellen Installation eines Betriebssystems gibt es einige klassische Provisioning Tools wie Foreman, Spacewalk, FAI, etc, die aber alle vor ihrer Nutzung ebenfalls relativ aufwendig installiert werden müssen und schon eine gewisse Anforderung an die eigene Hardware stellen.
Eine interessante Alternative ist das Open Source Tool Packer von der Firma HashiCorp.
Es besteht aus einem einzigen Binary, das mit Hilfe von Plugins aus einer JSON artigen Template-Datei, welche das Zielimage beschreibt, Systemimages für die verschiedensten Zielplattformen erstellt. Dabei lassen sich auch vorhandene Tools wie Ansible, ssh oder auch einfache Bash Skripte integrieren.
Packer schliesst mit seiner Philosophie die Automatisierungslücke zwischen dem Entstehen einer VM und ihrer Konfiguration. Außerdem werden durch Packer auch bei der Imageerstellung die aus der Infrastructure-as-a-Code bekannten Vorteile sichergestellt: Nachvollziehbarkeit, Wiederholbarkeit und Automatisierung.
Installation
Packer kann aus verschiedenen Quellen installiert werden: Homebrew, fertiges deb/rpm Paket oder direkter Download des Binary:
wget https://releases.hashicorp.com/packer/1.8.2/packer_1.8.2_linux_amd64.zip
sudo unzip packer_1.8.2_linux_amd64.zip -d /usr/local/bin/
Template
Eine Template Datei beschreibt, was für ein Image wie von Packer gebaut werden soll. Früher im JSON Format ist seit Version 1.7 das hauseigene Format HCL die empfohlene Definitionssprache für Templates.
Ein Template besteht aus drei Blöcken:
Packer Block
In diesem Block werden Einstellungen hinterlegt, die Packer direkt betreffen und vor allem, welche Plugins genutzt werden sollen.
Source Block
Hier werden die Plugin-spezifischen Einstellungen festgelegt:
Es können mehrere source Blöcke existieren, je nachdem, für welche Virtualisierer Images gebaut werden sollen.
Build Block
Was genau, also welches OS mit welchem Inhalt, gebaut werden soll und ggf. mit welchen zusätzlichen Hilfsmitteln, wird in diesem Abschnitt beschrieben.
Ein einfaches Beispiel Template, welches jeweils ein Image mit Rocky Linux für Proxmox und Virtualbox erstellt, könnte so aussehen:
packer {
required_plugins {
virtualbox = {
version = "1.0.4"
source = "github.com/hashicorp/virtualbox"
}
proxmox = {
version = "= 1.0.8"
source = "github.com/hashicorp/proxmox"
}
}
}
source "proxmox-iso" "rocky" {
proxmox_url = "https://proxmox.pve:8006/api2/json"
username = "root@pam!packer"
token = "xx-xx-xx-xx"
node = "example"
iso_storage_pool = "iso"
iso_file = "iso:iso/Rocky-8.6-x86_64-dvd1.iso"
iso_checksum = "sha256:1d48e0af63d07ff4e582a1819348e714c694e7fd33207f48879c2bc806960786"
pool = "terraform"
memory = 4096
cores = 2
sockets = 1
cpu_type = "host"
os = "l26"
qemu_agent = true
unmount_iso = true
scsi_controller = "virtio-scsi-single"
disks {
type = "scsi"
disk_size = "8G"
storage_pool = "ceph_vm"
storage_pool_type = "rbd"
format = "raw"
}
network_adapters {
bridge = "vmbr0"
model = "virtio"
firewall = false
}
communicator = "ssh"
ssh_username = "packer"
ssh_password = "packer"
ssh_timeout = "10m"
vm_name = "packer-rocky"
vm_id = 110
boot_wait = "3s"
boot_command = [
"<up><tab><end> inst.text inst.ks=http://webserver.example.com/provision/rocky/8/ks.cfg<wait5><enter>"
]
}
source "virtualbox-iso" "rocky" {
guest_os_type = "RedHat_64"
iso_url = "https://download.rockylinux.org/pub/rocky/8/isos/x86_64/Rocky-8.6-x86_64-dvd1.iso"
iso_checksum = "sha256:1d48e0af63d07ff4e582a1819348e714c694e7fd33207f48879c2bc806960786"
output_directory = "rocky_vbox"
cpus = "2"
memory = "2048"
guest_additions_mode = "disable"
http_directory = "./http/"
communicator = "ssh"
ssh_username = "packer"
ssh_password = "packer"
ssh_timeout = "20m"
shutdown_command = "echo 'packer' | sudo -S shutdown -P now"
boot_wait = "3s"
boot_command = [
"<up><tab><end> inst.text inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg<wait5><enter>"
]
}
build {
sources = [
"source.proxmox-iso.rocky",
"source.virtualbox-iso.rocky",
]
provisioner "shell" {
only = ["proxmox-iso.rocky"]
inline = [
"echo Adrian! :-D",
]
}
provisioner "shell" {
inline = [
"echo Packer did it"
]
}
}
Erläuterungen zum Template Beispiel
Die meisten Parameter erklären sich von selbst und ergeben sich teilweise aus dem jeweiligen genutzten Plugin. Einige Parameter sind aber eine besondere Erwähnung wert:
Usage
Das obige Beispiel wird in einem neuen Unterordner als Datei unter dem Namen rocky.pkr.hcl gespeichert. Zusätzlich wird ein weiterer Unterordner namens http angelegt, in der eine Kickstart Datei abgelegt werden kann:
tree
.
├── http
│ └── ks.cfg
└── rocky.pkr.hcl
1 directory, 2 files
Nun muss die Konfiguration mit Packer initialisiert werden, um die benötigten Plugins zu erhalten:
[0][packer]$ packer init .
Installed plugin github.com/hashicorp/virtualbox v1.0.4 in "/home/packer/.config/packer/plugins/github.com/hashicorp/virtualbox/packer-plugin-virtualbox_v1.0.4_x5.0_linux_amd64"
Installed plugin github.com/hashicorp/proxmox v1.0.8 in "/home/packer/.config/packer/plugins/github.com/hashicorp/proxmox/packer-plugin-proxmox_v1.0.8_x5.0_linux_amd64"
[0][packer]$
Packer bietet zwei Optionen an, um eine Template Datei zum einen zu formatieren und zum anderen auf richtige Syntax zu prüfen:
[0][packer]$ packer fmt .
rocky.pkr.hcl
[0][packer]$ packer validate .
The configuration is valid.
[0][packer]$
Ist so weit alles korrekt, kann mit dem Parameter build der Bau des Images gestartet werden:
[0][packer]$ packer build .
proxmox-iso.rocky: output will be in this color.
virtualbox-iso.rocky: output will be in this color.
==> virtualbox-iso.rocky: Retrieving ISO
==> proxmox-iso.rocky: Creating VM
==> virtualbox-iso.rocky: Trying /srv/external-usb/isos/Rocky-8.6-x86_64-dvd1.iso
==> virtualbox-iso.rocky: Trying /srv/external-usb/isos/Rocky-8.6-x86_64-dvd1.iso?checksum=sha256%3A1d48e0af63d07ff4e582a1819348e714c694e7fd33207f48879c2bc806960786
==> proxmox-iso.rocky: Starting VM
==> proxmox-iso.rocky: Waiting 3s for boot
==> proxmox-iso.rocky: Typing the boot command
==> proxmox-iso.rocky: Waiting for SSH to become available...
==> virtualbox-iso.rocky: /srv/external-usb/isos/Rocky-8.6-x86_64-dvd1.iso?checksum=sha256%3A1d48e0af63d07ff4e582a1819348e714c694e7fd33207f48879c2bc806960786 => /srv/external-usb/isos/Rocky-8.6-x86_64-dvd1.iso
==> virtualbox-iso.rocky: Starting HTTP server on port 8730
==> virtualbox-iso.rocky: Creating virtual machine...
==> virtualbox-iso.rocky: Creating hard drive rocky_vbox/packer-rocky-1659535275.vdi with size 40000 MiB...
==> virtualbox-iso.rocky: Mounting ISOs...
virtualbox-iso.rocky: Mounting boot ISO...
==> virtualbox-iso.rocky: Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port 3607)
==> virtualbox-iso.rocky: Starting the virtual machine...
==> virtualbox-iso.rocky: Waiting 3s for boot...
==> virtualbox-iso.rocky: Typing the boot command...
==> virtualbox-iso.rocky: Using SSH communicator to connect: 127.0.0.1
==> virtualbox-iso.rocky: Waiting for SSH to become available...
==> proxmox-iso.rocky: Connected to SSH!
==> proxmox-iso.rocky: Provisioning with shell script: /tmp/packer-shell3717013971
proxmox-iso.rocky: Adrian! :-D
==> proxmox-iso.rocky: Provisioning with shell script: /tmp/packer-shell800680251
proxmox-iso.rocky: Packer did it
==> proxmox-iso.rocky: Stopping VM
==> proxmox-iso.rocky: Converting VM to template
Build 'proxmox-iso.rocky' finished after 8 minutes 54 seconds.
==> virtualbox-iso.rocky: Connected to SSH!
==> virtualbox-iso.rocky: Uploading VirtualBox version info (6.1.34)
==> virtualbox-iso.rocky: Provisioning with shell script: /tmp/packer-shell2506019040
virtualbox-iso.rocky: Packer did it
==> virtualbox-iso.rocky: Gracefully halting virtual machine...
==> virtualbox-iso.rocky: Preparing to export machine...
virtualbox-iso.rocky: Deleting forwarded port mapping for the communicator (SSH, WinRM, etc) (host port 3607)
==> virtualbox-iso.rocky: Exporting virtual machine...
virtualbox-iso.rocky: Executing: export packer-rocky-1659535275 --output rocky_vbox/packer-rocky-1659535275.ovf
==> virtualbox-iso.rocky: Cleaning up floppy disk...
==> virtualbox-iso.rocky: Deregistering and deleting VM...
Build 'virtualbox-iso.rocky' finished after 10 minutes 47 seconds.
==> Wait completed after 10 minutes 47 seconds
==> Builds finished. The artifacts of successful builds are:
--> proxmox-iso.rocky: A template was created: 110
--> virtualbox-iso.rocky: VM files in directory: rocky_vbox
[0][packer]$
Sind in der Template Datei mehrere Source Blöcke definiert, wird das Image mit dem Aufruf für alle erstellt. Falls das Image nur für eine Sourcedefinition erstellt werden soll, kann das beim build Aufruf als Option mitgegeben werden:
[0][packer]$ packer build -only=proxmox-iso.rocky rocky.pkr.hcl
Advanced
In einem zweiten Teil werden weiterführende Packer Techniken betrachtet:
Fazit
Mit Packer ist es möglich, schnell und flexibel individuell standardisierte Basis Images für die verschiedensten Virtualisierungsplattformen zu erstellen. Im Internet sind viele fertige Packer Templates zu finden, so dass sich schnell Erfolge erzielen lassen.
Ein How-To von Martin.
Weiterführende Links
https://www.packer.io/plugins
https://github.com/chef/bento