Einleitung
Packer ist eine Open-Source-Anwendung von HashiCorp, um Maschine Images automatisiert zu erstellen. Damit werden bisher zumeist zeitaufwendige manuelle Arbeiten – beispielsweise an einem Golden Image – deutlich nachvollziehbarer und vor allem zuverlässiger.
Installation
Die Installation erfolgt unkompliziert über die offizielle Packer-Website. Dafür steht der Download des Binary Files für die Plattformen macOS, Windows, Linux sowie FreeBSD, OpenBSD und Solaris bereit. In der jeweiligen Kommandozeile sollte anschließend die Eingabe packer
die verfügbaren Befehle anbieten. Wir verwenden in unserem Beispiel die Version 1.7.4.
Templates
Anhand eines Templates werden wir die grundlegenden Funktionsweisen von Packer demonstrieren. Dazu wird folgender Code unter dem Dateinamen azure-ubuntu.pkr.hcl
gespeichert.
Zunächst werden die Variablen subscription_id
, client_id
sowie client_secret
definiert, die wir für die Authentifizierung zu Azure benötigen. Diese können vorerst ignoriert werden, da wir sie uns im nächsten Schritt genauer ansehen.
variable "subscription_id" {
type = string
}
variable "client_id" {
type = string
}
variable "client_secret" {
type = string
sensitive = true
}
Anschließend folgt der Block packer
, in dem das für uns notwendige Plugin azure
in einer bestimmten Version näher spezifiziert wird.
packer {
required_plugins {
azure = {
version = ">= 1.0.0"
source = "github.com/hashicorp/azure"
}
}
}
Der Block source legt das Builder Plugin fest. Innerhalb unseres Beispiels verwenden wir azure-arm, welches eine Virtuelle Maschine (VM) in einer selbständig angelegten Ressource Group provisioniert und uns das daraus gewünschte Abbild erzeugt. Nicht mehr benötigte Artefakte werden anschließend durch Packer gelöscht. Hierfür werden Location, Größe der zu verwendenden VM sowie Details zum Betriebssystem angegeben. Wir nutzen hier Azure Managed Images als Alternative zu VHD. Dadurch ist zwingend die Angabe des managed_image_resource_group_name
erforderlich, worin das Image abgelegt werden soll. Diese Ressource Group wird nicht durch Packer angelegt, muss also vor der Ausführung bereits existieren.
Unter managed_image_name
erfolgt die Benamung des zu erzeugenden Images. Wichtig ist zu beachten, dass dieser Name einzigartig sein muss, da Packer in seiner Standardeinstellung keine Datei überschreibt. Anschließend werden azure_tags
zur besseren Nachvollziehbarkeit angelegt. Der beschriebene Block kann für sogenannte Parallel Builds auch mehrfach in verschiedenen Ausführungen auftreten.
source "azure-arm" "example" {
subscription_id = "${var.subscription_id}"
client_id = "${var.client_id}"
client_secret = "${var.client_secret}"
location = "West Europe"
managed_image_name = "packerimage"
managed_image_resource_group_name = "packerdemo"
os_type = "Linux"
image_publisher = "Canonical"
image_offer = "UbuntuServer"
image_sku = "16_04-lts-gen2"
vm_size = "Standard_DC2s_v2"
azure_tags = {
dept = "engineering"
}
}
Der letzte Block build
definiert, was nach dem Start der VM ausgeführt werden soll. Momentan wird nur auf die Quelle sources.azure-arm.example
referenziert, damit die durch uns definierten Werte übernommen werden.
build {
sources = ["sources.azure-arm.example"]
}
Variablen
Um die oben definierten Variablen zu initialisieren, legen wir nun im selben Verzeichnis eine Datei namens azure.auto.pkrvars.hcl
mit dem untenstehenden Inhalt an. Packer lädt automatisch jede Datei mit der Dateiendung auto.pkrvars.hcl
. Ansonsten kann die Flag --vars-file=
in der Kommandozeile gesetzt werden. Die Beispielwerte müssen entsprechend ersetzt werden. Ein Service Principal kann dazu wie folgt in Azure angelegt werden. Dieser benötigt Zugriffrechte auf die genannten Ressource Groups.
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"
subscription_id = "YOUR_SUBSCRIPTION_ID"
Ausführung
Da nun die Vorbereitungen abgeschlossen sind, können wir Packer ausführen. Dafür nutzen wir als erstes den Befehl packer init .
, der uns das angegebene Plugin (azure) herunterlädt. Mit dem Befehl packer fmt .
kann eine konsistente Formatierung gewährleistet werden; packer validate .
prüft hingegen die Konfiguration auf mögliche syntaktische Fehler. Sofern keine Ausgabe beim Ausführen der Befehle erfolgt, wurden auch keine Veränderungen vorgenommen. Durch packer build .
kann das Image erstellt werden. Das Image sollte nach erfolgreichem Durchlauf in Azure zur Verfügung stehen.
Provisioners
Der wirkliche Mehrwert von Packer erfolgt durch sogenannte Provisioners. Durch Provisioners können moderne Automatisierungstools wie Ansible, Chef und Puppet, aber auch herkömmliche Werkzeuge wie Bash oder Powershell, in den Build-Prozess einfließen.
Wir entscheiden uns, den Webserver NGINX innerhalb des Images zu installieren. Dazu müssen folgende provisioner
Blöcke in die bestehende Datei azure-ubuntu.pkr.hcl
innerhalb des build
Blocks unter der Zuordnung von source
eingefügt werden.
provisioner "shell" {
execute_command = "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'"
inline = ["apt-get update", "apt-get upgrade -y", "apt-get -y install nginx"]
inline_shebang = "/bin/sh -x"
}
provisioner "file" {
destination = "/tmp/"
source = "./index.nginx.html"
}
provisioner "shell" {
execute_command = "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'"
inline = [
"mv /tmp/index.nginx.html /var/www/html/",
"chown root:root /var/www/html/index.nginx.html",
"chmod 0644 /var/www/html/index.nginx.html"
]
inline_shebang = "/bin/sh -x"
}
provisioner "shell" {
execute_command = "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'"
inline = [
"/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"
]
inline_shebang = "/bin/sh -x"
}
Wie dem Ablauf zu entnehmen ist, wird zudem die Index-Datei angepasst. Das Dokument muss dafür im gleichen Verzeichnis mit dem folgenden oder ähnlichen Inhalt unter dem Namen index.nginx.html
angelegt werden.
<!doctype html>
<html>
<head>
<title>Hier kommt der Titel hin</title>
</head>
<body>
<p>Das ist ein Text</p>
</body>
</html>
Ergänzend sei erwähnt, dass der letzte provisioner Block der ordnungsgemäßen Verbindung der VM zu Azure dient – weitere Informationen sind unter Azure-Linux-VM-Agent – Übersicht – Azure Virtual Machines | Microsoft Docs zu entnehmen.
Da wir den Dateinamen des zu erstellenden Images nicht jedes Mal manuell anpassen wollen, fügen wir zu den bereits definierten Variablen zwei weitere hinzu. Zum einen die Input-Variable azure_prefix
und zum anderen die lokale Variable timestamp
.
variable "azure_prefix" {
type = string
default = "azure-nginx"
}
locals {
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
}
Jetzt müssen wir im oberen Abschnitt nur noch den Wert "packerimage"
für managed_image_name
durch "${var.azure_prefix}-${local.timestamp}"
ersetzen. Zukünftig wird hiermit ein einzigartiger Name für das jeweilige Image generiert. Wir können nun packer build .
erneut ausführen.
Ausblick
Packer ist nicht für das weitere Management von VMs vorgesehen, beschränkt sich also nur auf den Build-Prozess des Images. Nach einem Deployment, zum Beispiel via HashiCorp Terraform, kann nun der Inhalt der erstellten Beispiel-Website angezeigt werden.
Zurzeit kann packer init
als optionaler Schritt angesehen werden. Auch das Verwenden von JSON anstelle von HCL stellt momentan kein Problem dar. In der kommenden Version 2.0. wird HCL (wie hier verwendet) und packer init
zwingend bei der Verwendung von Packer vorausgesetzt.