Get Rewarded! We will reward you with up to €50 credit on your account for every tutorial that you write and we publish!

Erstellen von eigenen Betriebsystem-Images mit HashiCorp Packer

profile picture
Author
Henrik Gerdes
Published
2023-12-11
Time to read
9 minutes reading time

About the author- Software Engineer that works with various cloud services.

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öchten Sie 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 Sie mit Packer eigene Images für Ihre Hetzner-VMs mit Infrastructure as Code (IaC) erstellen können. 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ötigen Sie:

  • Hetzner Cloud API-Token aus der Cloud-Konsole
  • Zugang zum Internet

Schritt 1 - Installieren von Packer

Besuchen Sie Hashicorp Packer und installieren Sie Packer entsprechend Ihres 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 [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

Sie können packer --version ausführen, um die Version anzuzeigen und zu prüfen, ob die Installation erfolgreich war.

Schritt 2 - Erstellen eines benutzerdefinierten Images

Erstellen Sie einen Projektordner mit mkdir my-hetzner-img und wechseln Sie 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, erstellen Sie eine Datei namens provider.pkr.hcl und fügen Sie die Provider-Konfiguration hinzu:

# packer.pkr.hcl
packer {
  required_plugins {
    hcloud = {
      source  = "github.com/hetznercloud/hcloud"
      version = ">= 1.2.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   = "cx11"
  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 Sie packer build . ausführen, wird Packer automatisch einen neuen Server und einen neuen Snapshot in Ihrem 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ühren Sie 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üfen Sie, ob das neue Image in der Cloud Console verfügbar ist:

image-overview-1.png.png

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 Sie erstellen möchten, 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   = "cx11"
  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"
    }
  }
}

Jetzt können wir das Basis-Image zur Laufzeit angeben und überschreiben, indem wir packer build -var bas_image=ubuntu-22.04 -var version=v1.1.0 . verwenden. 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ügen Sie einfach eine weitere "source" zu Ihrem Code hinzu und aktualisieren Sie 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:
Sie haben 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 Sie mit diesem Image bereitstellen 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:
Sie können cloud-init verwenden, um einige frühe Einstellungen beim Erstellen von Images mit Packer vorzunehmen, aber Sie möchten cloud-init vielleicht auch verwenden, wenn Sie Ihr benutzerdefiniertes Image für eine neue VM verwenden. Standardmäßig wird cloud-init nur einmal ausgeführt, daher muss es zurückgesetzt werden.
Fügen Sie die folgenden Zeilen in Ihr 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..."
cloud-init status --wait

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
rm -rvf /var/lib/cloud/instances /etc/machine-id /var/lib/dbus/machine-id /var/log/cloud-init*

Dies ermöglicht es den Benutzern, cloud-init ein zweites Mal auszuführen.

Verschiedene Betriebssysteme:
Sie können 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.5.5/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.

image-overview-2.png.png

Weitere Informationen zur Konfiguration finden Sie in der Packer Dokumentation und in der Dokumentation zum Hetzner Packer-Builder.
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.

License: MIT
Want to contribute?

Get Rewarded: Get up to €50 in credit! Be a part of the community and contribute. Do it for the money. Do it for the bragging rights. And do it to teach others!

Report Issue
Try Hetzner Cloud

Get 20€ free credit!

Valid until: 31 December 2024 Valid for: 3 months and only for new customers
Get started
Want to contribute?

Get Rewarded: Get up to €50 credit on your account for every tutorial you write and we publish!

Find out more