Packer automatisiert die Erstellung von Machine Images für verschiedene Plattformen. Es erstellt identische Images für Cloud, VM und Container aus einer einzigen Konfiguration.
Installation
Debian/Ubuntu
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list
apt update && apt install packer
# Version prüfen
packer versionGrundbefehle
packer init . # Plugins initialisieren
packer fmt . # Formatieren
packer validate . # Validieren
packer build . # Image bauenGrundstruktur (HCL2)
Einfaches Template
# ubuntu.pkr.hcl
packer {
required_plugins {
amazon = {
source = "github.com/hashicorp/amazon"
version = "~> 1"
}
}
}
source "amazon-ebs" "ubuntu" {
ami_name = "ubuntu-base-{{timestamp}}"
instance_type = "t3.micro"
region = "eu-central-1"
source_ami_filter {
filters = {
name = "ubuntu/images/*ubuntu-jammy-22.04-amd64-server-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["099720109477"]
}
ssh_username = "ubuntu"
}
build {
sources = ["source.amazon-ebs.ubuntu"]
provisioner "shell" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx"
]
}
}Builder
AWS AMI
source "amazon-ebs" "webserver" {
ami_name = "webserver-{{timestamp}}"
ami_description = "Web Server Image"
instance_type = "t3.micro"
region = "eu-central-1"
source_ami_filter {
filters = {
name = "ubuntu/images/*ubuntu-jammy-22.04-amd64-server-*"
}
most_recent = true
owners = ["099720109477"]
}
ssh_username = "ubuntu"
tags = {
Name = "webserver"
Version = "1.0"
}
# AMI in mehrere Regionen kopieren
ami_regions = ["eu-west-1", "us-east-1"]
}Hetzner Cloud
packer {
required_plugins {
hcloud = {
source = "github.com/hetznercloud/hcloud"
version = "~> 1"
}
}
}
source "hcloud" "ubuntu" {
image = "ubuntu-22.04"
location = "nbg1"
server_type = "cx11"
ssh_username = "root"
snapshot_name = "ubuntu-base-{{timestamp}}"
snapshot_labels = {
type = "base"
}
}VirtualBox
source "virtualbox-iso" "ubuntu" {
iso_url = "https://releases.ubuntu.com/22.04/ubuntu-22.04.3-live-server-amd64.iso"
iso_checksum = "sha256:..."
ssh_username = "vagrant"
ssh_password = "vagrant"
shutdown_command = "sudo shutdown -P now"
guest_os_type = "Ubuntu_64"
disk_size = 20000
memory = 2048
cpus = 2
boot_command = [
"<esc><wait>",
"linux /casper/vmlinuz --- autoinstall",
"<enter>"
]
http_directory = "http"
}Docker
source "docker" "ubuntu" {
image = "ubuntu:22.04"
commit = true
}
build {
sources = ["source.docker.ubuntu"]
provisioner "shell" {
inline = [
"apt-get update",
"apt-get install -y nginx"
]
}
post-processor "docker-tag" {
repository = "myrepo/nginx"
tags = ["latest", "1.0"]
}
}Provisioner
Shell
build {
sources = ["source.amazon-ebs.ubuntu"]
provisioner "shell" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx"
]
}
provisioner "shell" {
script = "scripts/setup.sh"
}
provisioner "shell" {
scripts = [
"scripts/install.sh",
"scripts/configure.sh",
"scripts/cleanup.sh"
]
}
# Mit Umgebungsvariablen
provisioner "shell" {
environment_vars = [
"DEBIAN_FRONTEND=noninteractive",
"APP_ENV=production"
]
script = "scripts/install.sh"
}
}File
provisioner "file" {
source = "files/nginx.conf"
destination = "/tmp/nginx.conf"
}
provisioner "shell" {
inline = ["sudo mv /tmp/nginx.conf /etc/nginx/nginx.conf"]
}Ansible
provisioner "ansible" {
playbook_file = "ansible/playbook.yml"
extra_arguments = [
"--extra-vars", "env=production"
]
}
# Oder Ansible Local (in der VM)
provisioner "ansible-local" {
playbook_file = "ansible/playbook.yml"
}Chef/Puppet
provisioner "chef-solo" {
cookbook_paths = ["cookbooks"]
run_list = ["recipe[webserver]"]
}
provisioner "puppet-masterless" {
manifest_file = "manifests/default.pp"
}Variablen
Definition
# variables.pkr.hcl
variable "aws_region" {
type = string
default = "eu-central-1"
}
variable "instance_type" {
type = string
default = "t3.micro"
}
variable "ami_name" {
type = string
}
variable "tags" {
type = map(string)
default = {
Environment = "production"
}
}
variable "ssh_password" {
type = string
sensitive = true
}Verwendung
source "amazon-ebs" "webserver" {
region = var.aws_region
instance_type = var.instance_type
ami_name = var.ami_name
tags = var.tags
}Werte zuweisen
# Kommandozeile
packer build -var 'ami_name=webserver-v1' .
# Variablen-Datei
packer build -var-file=production.pkrvars.hcl .
# Umgebungsvariable
export PKR_VAR_aws_region=eu-west-1
packer build .# production.pkrvars.hcl
aws_region = "eu-central-1"
instance_type = "t3.small"
ami_name = "webserver-production"Post-Processors
Manifest
build {
sources = ["source.amazon-ebs.webserver"]
post-processor "manifest" {
output = "manifest.json"
strip_path = true
}
}Vagrant
post-processor "vagrant" {
output = "builds/{{.BuildName}}.box"
}Compress
post-processor "compress" {
output = "builds/image.tar.gz"
}Shell-Local
post-processor "shell-local" {
inline = [
"echo 'Image ID: {{.ArtifactId}}'",
"aws sns publish --message 'Image built: {{.ArtifactId}}'"
]
}Multiple Builds
Parallele Builds
source "amazon-ebs" "aws" {
ami_name = "webserver-aws-{{timestamp}}"
region = "eu-central-1"
instance_type = "t3.micro"
# ...
}
source "hcloud" "hetzner" {
snapshot_name = "webserver-hetzner-{{timestamp}}"
server_type = "cx11"
# ...
}
build {
sources = [
"source.amazon-ebs.aws",
"source.hcloud.hetzner"
]
provisioner "shell" {
inline = ["sudo apt-get update", "sudo apt-get install -y nginx"]
}
}Bedingte Builds
build {
sources = ["source.amazon-ebs.webserver"]
# Nur für bestimmten Builder
provisioner "shell" {
only = ["amazon-ebs.webserver"]
inline = ["echo 'AWS specific'"]
}
# Ausschließen
provisioner "shell" {
except = ["docker.ubuntu"]
inline = ["systemctl enable nginx"]
}
}Praktische Templates
Base Image
# base-image.pkr.hcl
source "amazon-ebs" "base" {
ami_name = "base-ubuntu-{{timestamp}}"
instance_type = "t3.micro"
region = "eu-central-1"
source_ami_filter {
filters = {
name = "ubuntu/images/*ubuntu-jammy-22.04-amd64-server-*"
}
most_recent = true
owners = ["099720109477"]
}
ssh_username = "ubuntu"
tags = {
Name = "Base Image"
OS = "Ubuntu 22.04"
}
}
build {
sources = ["source.amazon-ebs.base"]
# System-Updates
provisioner "shell" {
inline = [
"sudo apt-get update",
"sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y"
]
}
# Basis-Pakete
provisioner "shell" {
inline = [
"sudo apt-get install -y curl wget vim htop"
]
}
# Cleanup
provisioner "shell" {
inline = [
"sudo apt-get clean",
"sudo rm -rf /var/lib/apt/lists/*",
"sudo rm -rf /tmp/*"
]
}
}Webserver Image
# webserver.pkr.hcl
variable "base_ami" {
type = string
}
source "amazon-ebs" "webserver" {
ami_name = "webserver-{{timestamp}}"
instance_type = "t3.micro"
region = "eu-central-1"
source_ami = var.base_ami
ssh_username = "ubuntu"
tags = {
Name = "Webserver"
Role = "web"
}
}
build {
sources = ["source.amazon-ebs.webserver"]
provisioner "shell" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y nginx"
]
}
provisioner "file" {
source = "files/nginx.conf"
destination = "/tmp/nginx.conf"
}
provisioner "shell" {
inline = [
"sudo mv /tmp/nginx.conf /etc/nginx/nginx.conf",
"sudo systemctl enable nginx"
]
}
}Golden Image Pipeline
# golden-image.pkr.hcl
locals {
timestamp = formatdate("YYYYMMDD-hhmmss", timestamp())
}
source "amazon-ebs" "golden" {
ami_name = "golden-${local.timestamp}"
instance_type = "t3.small"
region = "eu-central-1"
source_ami_filter {
filters = { name = "ubuntu/images/*ubuntu-jammy-22.04-amd64-server-*" }
most_recent = true
owners = ["099720109477"]
}
ssh_username = "ubuntu"
ami_regions = ["eu-west-1", "us-east-1"]
tags = {
Name = "Golden Image"
Version = local.timestamp
ManagedBy = "Packer"
}
}
build {
sources = ["source.amazon-ebs.golden"]
# CIS Hardening
provisioner "ansible" {
playbook_file = "ansible/hardening.yml"
}
# Monitoring Agent
provisioner "shell" {
script = "scripts/install-monitoring.sh"
}
# Security Patches
provisioner "shell" {
inline = [
"sudo apt-get update",
"sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y"
]
}
# Cleanup
provisioner "shell" {
script = "scripts/cleanup.sh"
}
post-processor "manifest" {
output = "manifest.json"
}
}CI/CD Integration
GitHub Actions
name: Build Image
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Packer
uses: hashicorp/setup-packer@main
- name: Init
run: packer init .
- name: Validate
run: packer validate .
- name: Build
run: packer build .
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}Zusammenfassung
| Befehl | Funktion | |--------|----------| | packer init | Plugins laden | | packer validate | Validieren | | packer build | Image erstellen | | packer fmt | Formatieren |
| Builder | Plattform | |---------|-----------| | amazon-ebs | AWS AMI | | hcloud | Hetzner Cloud | | virtualbox-iso | VirtualBox | | docker | Docker Image | | azure-arm | Azure | | googlecompute | GCP |
| Provisioner | Verwendung | |-------------|------------| | shell | Shell-Skripte | | file | Dateien kopieren | | ansible | Ansible Playbooks | | chef-solo | Chef Cookbooks |
Fazit
Packer ermöglicht reproduzierbare Machine Images für beliebige Plattformen. Die einheitliche Konfiguration spart Zeit bei Multi-Cloud-Deployments. Mit Provisioning-Integration (Shell, Ansible) werden Images vollständig automatisiert erstellt. In CI/CD-Pipelines integriert, bildet Packer die Grundlage für Immutable Infrastructure. Die Kombination mit Terraform ist besonders mächtig.