Einführung
Hetzner Cloud bietet eine Reihe verschiedener Betriebssystem-Images, aus denen man bei der Erstellung einer neuen virtuellen Maschine wählen kann. Diese Images bilden die Basis, um eigene Dienste aufzubauen, sind aber selten bereits für die jeweiligen Anforderungen konfiguriert. Vielleicht möchtest du eine Datenbank oder einen Webserver installieren, einen Wartungsbenutzer hinzufügen oder einfach die Systemeinstellungen optimieren, um die Leistung und Sicherheit zu verbessern. Es gibt zwar Tools, die dies automatisch für jede neue VM erledigen, aber dies kostet Zeit und ist nicht immer reproduzierbar. Wäre es nicht einfacher, ein eigenes, gut funktionierendes, persönliches konfiguriertes Betriebssystem-Image zu haben, das für jede neue VM verwendet werden kann?
Dieses kann mit HashiCorp Packer erreicht werden.
Dieser Artikel zeigt, wie du mit Packer eigene Images für deine Hetzner-VMs mit Infrastructure as Code (IaC) erstellen kannst. Es ermöglicht sogar die Erstellung von VMs mit Betriebssystemen, die nicht offiziell von Hetzner unterstützt werden.
Voraussetzungen
Um diesem Tutorial zu folgen, benötigst du:
- Hetzner Cloud API-Token aus der Cloud Console
- Zugang zum Internet
Schritt 1 - Installieren von Packer
Besuche Hashicorp Packer und installiere Packer entsprechend deines Betriebssystems.
Auf einem Debian-basierten Linux, muss folgendes ausgeführt werden:
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) 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 install packer
Führe
packer --version
aus, um die Version anzuzeigen und zu prüfen, ob die Installation erfolgreich war.
Schritt 2 - Erstellen eines benutzerdefinierten Images
Erstelle einen Projektordner mit mkdir my-hetzner-img
und wechsel in das Verzeichnis.
Ähnlich wie Terraform verwendet Packer Provider, um mit dem benötigten Build-System zu kommunizieren. Die Verwendung verschiedener Provider ermöglicht es Packer, eine große Auswahl an verschiedenen Zielsystemen anzubieten. Es ermöglicht Nutzern, Software- oder Betriebssystempakete für alle wichtigen Cloud-Anbieter, On-Prem-Systeme und sogar Docker-Images zu erstellen.
Um einen Provider zu verwenden, erstelle eine Datei namens provider.pkr.hcl
und füge die Provider-Konfiguration hinzu:
# packer.pkr.hcl
packer {
required_plugins {
hcloud = {
source = "github.com/hetznercloud/hcloud"
version = ">= 1.6.0"
}
}
}
Packer verwendet die HashiCorp Konfigurationssprache (HCL), um den gewünschten Zustand zu deklarieren, genau wie Terraform.
Um ein personalisiertes, benutzerdefiniertes Image zu erstellen, benötigt Packer eine Basis, von der aus es startet. Diese wird als "source" bezeichnet:
# custom-img-v1.pkr.hcl
source "hcloud" "base-amd64" {
image = "debian-12"
location = "nbg1"
server_type = "cx22"
ssh_keys = []
user_data = ""
ssh_username = "root"
snapshot_name = "custom-img"
snapshot_labels = {
base = "debian-12",
version = "v1.0.0",
name = "custom-img"
}
}
Damit wird Packer mitgeteilt, wo es beginnen soll. Im Fall von Hetzner muss Packer das Basis-Image, den Servertyp und den Standort der für die Erstellung verwendet werden soll, kennen. Außerdem werden der Name des Snapshots, welches erstellt wird, und die Tags, die auf dieses angewendet werden sollen, angegeben.
Der nächste Schritt besteht darin, die eigenen Anpassungen vorzunehmen. Damit ist der eigentliche Build-Schritt gemeint.
# custom-img-v1.pkr.hcl
build {
sources = ["source.hcloud.base-amd64"]
provisioner "shell" {
inline = [
"apt-get update",
"apt-get install -y wget fail2ban cowsay",
"/usr/games/cowsay 'Hi Hetzner Cloud' > /etc/motd",
]
env = {
BUILDER = "packer"
}
}
}
Man verwendet die zuvor angegebene Quelle und gibt dann z.B den shell
-Parameter an. Jede Zeile in inline
wird dann auf dem Server ausgeführt. In diesem Fall wird fail2ban
installiert, um den Server abzusichern und es wird eine kleine Meldung in die motd
-Datei einfügt.
Hinweis: Wenn du
packer build .
ausführst, wird Packer automatisch einen neuen Server und einen neuen Snapshot in deinem Hetzner Cloud Projekt erstellen. Beide sind kostenpflichtig. Der Server wird automatisch gelöscht, sobald entweder der Snapshot erstellt wurde oder die Erstellung fehlgeschlagen ist.
Um das Image zu erstellen, führe die folgenden Befehle aus:
# Set your Hetzner API Token
export HCLOUD_TOKEN="XXX"
# Initialize the project - only needed once
packer init .
# Build
packer build .
Jetzt baut Packer den angegebenen Server mit allen Anweisungen und überträgt alle Ausgaben auf das aktuelle Terminal. Überprüfe, ob das neue Image in der Cloud Console verfügbar ist:
Schritt 3 - Konfigurierbare Images
Wenn man alle Befehle in die HCL einträgt, kann es schnell unübersichtlich werden. Es ist auch nicht sinnvoll, den Code für jede Variante eines Images, das du erstellen möchtest, zu kopieren und einzufügen. Um die Dinge allgemeiner zu gestalten, kann Packer Variablen verwenden:
# custom-img-v2.pkr.hcl
variable "base_image" {
type = string
default = "debian-12"
}
variable "output_name" {
type = string
default = "snapshot"
}
variable "version" {
type = string
default = "v1.0.0"
}
variable "user_data_path" {
type = string
default = "cloud-init-default.yml"
}
source "hcloud" "base-amd64" {
image = var.base_image
location = "nbg1"
server_type = "cx22"
ssh_keys = []
user_data = file(var.user_data_path)
ssh_username = "root"
snapshot_name = "${var.output_name}-${var.version}"
snapshot_labels = {
base = var.base_image,
version = var.version,
name = "${var.output_name}-${var.version}"
}
}
build {
sources = ["source.hcloud.base-amd64"]
provisioner "shell" {
scripts = [
"os-setup.sh",
]
env = {
BUILDER = "packer"
}
}
}
Erstelle folgende Dateien:
cloud-init-default.yml
#cloud-config ssh_pwauth: false disable_root_opts: no-port-forwarding,no-agent-forwarding,no-X11-forwarding # Install base packages package_update: true package_upgrade: true packages: - gnupg - curl - jq - unzip - apparmor - aptitude - lsb-release - ca-certificates - apt-transport-https - unattended-upgrades - apparmor-profiles-extra - logrotate - wget
os-setup.sh
#!/bin/bash set -e -o pipefail echo "waiting for cloud-init to finish..." if ! cloud-init status --wait; then echo "Note: cloud-init failed or was already completed." fi echo "Installing packages..." apt-get update apt-get install --yes --no-install-recommends wget fail2ban echo "cleanup..." cloud-init clean --machine-id --seed --logs
Jetzt können wir das Basis-Image zur Laufzeit angeben und überschreiben, indem wir diesen Befehl verwenden:
packer build -var base_image=ubuntu-24.04 -var version=v1.1.0 .
Diese Version verwendet auch den scrips
-Parameter, anstatt alle Befehle direkt anzugeben. Dies ermöglicht viel komplexere Setups. Außerdem wird auch cloud-init verwendet, um einige Einstellungen deklarativ zu konfigurieren.
Hetzner unterstützt auch Arm64-basierte Server, die ein hervorragendes Preis-Leistungs-Verhältnis und eine beeindruckende Effizienz bieten. Aufgrund der anderen Architektur von Arm muss die Software für diese Architektur kompiliert werden und alle Konfigurationen müssen auch für diese angewendet werden. Glücklicherweise erlaubt Packer die Wiederverwendung beliebiger Build-Skripte für Arm.
Füge im Code einfach eine weitere "source" hinzu und aktualisiere den Build-Schritt, um diese Quelle einzubeziehen:
# custom-img-v2.pkr.hcl
source "hcloud" "base-arm64" {
image = var.base_image
location = "nbg1"
server_type = "cax11"
ssh_keys = []
user_data = file(var.user_data_path)
ssh_username = "root"
snapshot_name = "${var.output_name}-${var.version}"
snapshot_labels = {
base = var.base_image,
version = var.version,
name = "${var.output_name}-${var.version}"
}
}
...
build {
- sources = ["source.hcloud.base-amd64"]
+ sources = ["source.hcloud.base-amd64", "source.hcloud.base-arm64"]
provisioner "shell" {
...
In dieser Version werden nun automatisch Images für Arm64-basierte Server erstellt.
Schritt 4 - Tipps und fortgeschrittene Verwendung:
Über die Festplattengröße:
Du hast vielleicht bemerkt, dass der obige Code die kleinsten auf Hetzner verfügbaren Instanzen verwendet. Dies dient nicht nur der Kostenreduzierung, sondern ermöglicht auch, dass der erstellte Snapshot für jede VM eingesetzt werden kann. Aufgrund der Art und Weise, wie Festplatten bei Hetzner verwaltet werden, muss eine neue VM über eine Festplatte verfügen, die mindestens so groß ist wie diejenige, von der der Snapshot erstellt wurde. Die Verwendung eines Servers mit acht Kernen könnte etwas schneller sein als die Verwendung eines kleineren Servers, aber das würde die Server, die mit diesem Image bereitgestellt werden können, auf VMs mit mindestens 240 GB Festplatten beschränken.
Über Tags:
Tags sind Metadaten, die einem Snapshot hinzufügt werden können. Sie sind nützlich, um Informationen über die Herkunft des Abbilds und dessen Verwendungszweck bereitzustellen. Außerdem sind Tags die einzige Möglichkeit, einen vorhandenen Snapshot in Terraform auszuwählen, um eine neue VM bereitzustellen. Sie sollten definitiv verwendet werden.
Über cloud-init:
Du kannst cloud-init
verwenden, um einige frühe Einstellungen beim Erstellen von Images mit Packer vorzunehmen. Du kannst cloud-init
aber auch verwenden, um ein benutzerdefiniertes Image für eine neue VM zu verwenden. Standardmäßig wird cloud-init
nur einmal ausgeführt, daher muss es zurückgesetzt werden.
Füge die folgenden Zeilen in das Bash-Skript ein, um auf die Fertigstellung von cloud-init
zu warten und um es abschließend zurück zu setzen.
# os-setup.sh
#!/bin/bash
set -e -o pipefail
echo "Waiting for cloud-init to finish..."
if ! cloud-init status --wait; then
echo "Note: cloud-init failed or was already completed."
fi
echo "Installing packages..."
apt-get update
apt-get install --yes --no-install-recommends wget fail2ban
# My setup...
echo "Cleanup..."
cloud-init clean --machine-id --seed --logs
Dies ermöglicht es den Benutzern, cloud-init
ein zweites Mal auszuführen.
Verschiedene Betriebssysteme:
Du kannst auch andere Betriebssysteme erstellen und booten und mit Packer eigene Images für diese erstellen. Durch Booten in den Hetzner-rescue Modus kann man die gesamte Disk einer VM überschreiben:
build {
sources = ["source.hcloud.myvm"]
provisioner "shell" {
inline = [
"apt-get install -y wget",
"wget https://github.com/siderolabs/talos/releases/download/v1.7.7/hcloud-amd64.raw.xz",
"xz -d -c hcloud-amd64.raw.xz | dd of=/dev/sda && sync",
]
}
}
Mein persönlicher Anwendungsfall für Packer ist die Erstellung vorkonfigurierter Kubernetes-Images mit fein abgestimmten Systemparametern und vorgeladenen Container-Images. Der horizontale Autoscaler kann diese Images verwenden, um neue Worker Nodes schnell und zuverlässig bereitzustellen. Aber es gibt noch viele andere Anwendungsfälle.
Weitere Informationen zur Konfiguration werden in der Packer Dokumentation und in der Dokumentation zum Hetzner Packer-Builder bereitgestellt.
Der gesamte Code, der in diesem Artikel verwendet wurde, ist auf GitHub zu finden.
Ergebnis
Dieser Artikel gab eine umfassende Anleitung zur Installation und Verwendung von HashiCorp Packer. Er lieferte ein Basisprojekt, wie man Packer verwendet, um benutzerdefinierte Betriebssystem-Images für Hetzner Cloud VMs zu erstellen und dabei Infrastructure as Code Praktiken zu nutzen. Es wurde auch auf die fortgeschrittene Verwendung von Variablen und cloud-init eingegangen. Dieser Ansatz ermöglicht die Erstellung von maßgeschneiderten, vorkonfigurierten Betriebssystem-Images und bietet einen effizienteren und schlankeren Prozess für die Bereitstellung von VMs mit benutzerdefinierten Einstellungen und Anwendungen.