Menu
Szybki wybór
Hosting Domeny VPS SSL Kalkulator Porównania FAQ
Aktywne kody
Wszystkie kody rabatowe

Packer — budowanie spójnych obrazów VM dla VPS i chmury

Opublikowano: 10 kwietnia 2026 · Kategoria: VPS / DevOps

Wdrażanie na serwerze przez ręczne uruchamianie skryptów lub Ansible jest wolne i niespójne — każdy serwer może mieć subtelne różnice wynikające z kolejności operacji lub stanów pośrednich. Packer rozwiązuje ten problem przez koncepcję "złotego obrazu" (golden image): budujesz obraz VM raz, testujesz go, a potem tworzysz identyczne serwery z tego obrazu. Czas startu nowego serwera spada z minut (Ansible provisioning) do sekund (start z gotowego snapshotu). Ten artykuł pokazuje budowanie obrazów dla Hetzner Cloud i lokalne z QEMU.

Instalacja Packer

# Ubuntu / Debian — oficjalne repo HashiCorp
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 -y

# macOS
brew tap hashicorp/tap
brew install hashicorp/tap/packer

# Windows (Chocolatey)
choco install packer

# Sprawdź wersję
packer version
# Packer v1.10.x

# Zainstaluj pluginy (wymagane dla Packer >= 1.7)
packer plugins install github.com/hashicorp/hcloud
packer plugins install github.com/hashicorp/qemu

Struktura szablonu HCL2

Packer od wersji 1.7 używa HCL2 (ten sam język co Terraform). Szablon składa się z bloków source (builder + konfiguracja VM) i build (provisioners + post-processors). Oto struktura przykładowego projektu:

# Struktura projektu
# packer-ubuntu/
# ├── variables.pkr.hcl     # Zmienne wejściowe
# ├── ubuntu.pkr.hcl        # Główny szablon
# └── scripts/
#     ├── base.sh           # Hardening i narzędzia bazowe
#     ├── nginx.sh          # Nginx i PHP-FPM
#     └── cleanup.sh        # Czyszczenie cache apt, logi itp.

# variables.pkr.hcl
variable "hcloud_token" {
  type      = string
  sensitive = true
}

variable "image_name" {
  type    = string
  default = "ubuntu-22-04-lemp"
}

variable "server_type" {
  type    = string
  default = "cx11"  # Najtańszy do budowania
}

Builder dla Hetzner Cloud — snapshoty VPS

# ubuntu.pkr.hcl — pełny szablon
packer {
  required_plugins {
    hcloud = {
      version = ">= 1.2.0"
      source  = "github.com/hashicorp/hcloud"
    }
  }
}

# Source blok — konfiguracja VM do zbudowania
source "hcloud" "ubuntu-lemp" {
  token       = var.hcloud_token
  image       = "ubuntu-22.04"
  location    = "nbg1"
  server_type = var.server_type
  server_name = "packer-build-${formatdate("YYYYMMDD-hhmmss", timestamp())}"

  # Snapshot name po zakończeniu budowania
  snapshot_name   = "${var.image_name}-${formatdate("YYYYMMDD", timestamp())}"
  snapshot_labels = {
    "managed-by" = "packer"
    "os"         = "ubuntu-22.04"
    "stack"      = "lemp"
  }

  # SSH do provisioning
  communicator   = "ssh"
  ssh_username   = "root"

  # Timeout na uruchomienie VM
  ssh_timeout    = "10m"
}

# Build blok — co zrobić wewnątrz VM
build {
  name    = "lemp-ubuntu"
  sources = ["source.hcloud.ubuntu-lemp"]

  # Krok 1: aktualizacja systemu i hardening bazowy
  provisioner "shell" {
    scripts = [
      "./scripts/base.sh",
    ]
    # Czekaj na uruchomienie cloud-init
    execute_command = "cloud-init status --wait && {{.Vars}} bash '{{.Path}}'"
  }

  # Krok 2: instalacja LEMP przez Ansible
  provisioner "ansible" {
    playbook_file = "./ansible/lemp.yml"
    extra_arguments = [
      "--extra-vars", "env=production",
      "--ssh-extra-args", "-o StrictHostKeyChecking=no"
    ]
    ansible_env_vars = [
      "ANSIBLE_FORCE_COLOR=1",
    ]
  }

  # Krok 3: czyszczenie przed snapshot
  provisioner "shell" {
    inline = [
      "apt-get clean",
      "rm -rf /var/lib/apt/lists/*",
      "rm -f /root/.ssh/authorized_keys",
      "cloud-init clean --logs",
      "sync",
    ]
  }

  # Post-processor: zapisz metadata do pliku manifest
  post-processor "manifest" {
    output     = "manifest.json"
    strip_path = true
    custom_data = {
      builder = "hcloud"
      stack   = "lemp"
    }
  }
}

Skrypty provisioningu — base.sh

# scripts/base.sh — hardening bazowy Ubuntu
#!/bin/bash
set -euo pipefail

# Aktualizacja systemu
export DEBIAN_FRONTEND=noninteractive
apt-get update -q
apt-get upgrade -yq

# Narzędzia bazowe
apt-get install -yq \
  curl wget git unzip htop vim \
  fail2ban ufw \
  logrotate \
  software-properties-common \
  apt-transport-https ca-certificates gnupg

# Konfiguracja UFW — domyślnie blokuj wszystko
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable

# SSH hardening
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/#PermitRootLogin yes/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config

# Swap 2GB (dla małych VPS)
if [ ! -f /swapfile ]; then
  fallocate -l 2G /swapfile
  chmod 600 /swapfile
  mkswap /swapfile
  swapon /swapfile
  echo '/swapfile none swap sw 0 0' >> /etc/fstab
fi

# Parametry systemu
cat >> /etc/sysctl.d/99-server.conf <<'SYSCTL'
vm.swappiness=10
net.core.somaxconn=65535
net.ipv4.tcp_max_syn_backlog=65535
SYSCTL
sysctl -p /etc/sysctl.d/99-server.conf

Builder QEMU — lokalne obrazy dla on-premise

# Packer builder QEMU — dla Proxmox / KVM / lokalnych serwerów
source "qemu" "ubuntu-22-04" {
  iso_url      = "https://releases.ubuntu.com/22.04/ubuntu-22.04.4-live-server-amd64.iso"
  iso_checksum = "sha256:45f873de9f8cb637345d6e66a583762730bbea30277ef7b32c9c3bd6700a32b2"

  disk_size    = "20G"
  disk_image   = false
  format       = "qcow2"
  output_directory = "output-ubuntu"
  vm_name      = "ubuntu-22.04.qcow2"

  # Parametry VM podczas budowania
  cpus         = 2
  memory       = 2048
  headless     = true

  # Boot sequence — instalacja przez cloud-init
  boot_wait    = "5s"
  boot_command = [
    "<esc><wait>",
    "linux /casper/vmlinuz quiet autoinstall ds=nocloud;s=http://{{.HTTPIP}}:{{.HTTPPort}}/ --- <enter>",
    "initrd /casper/initrd <enter>",
    "boot <enter>",
  ]

  # HTTP server do udostępnienia preseed/user-data
  http_directory = "http"
  http_port_min  = 8080
  http_port_max  = 9090

  communicator  = "ssh"
  ssh_username  = "ubuntu"
  ssh_password  = "ubuntu"
  ssh_timeout   = "30m"

  shutdown_command = "sudo systemctl poweroff"
}

Budowanie i integracja z Terraform

# Inicjalizacja i walidacja szablonu
packer init ubuntu.pkr.hcl
packer validate ubuntu.pkr.hcl

# Budowanie obrazu
HCLOUD_TOKEN=xxx packer build ubuntu.pkr.hcl

# Lub przez zmienną
packer build \
  -var "hcloud_token=$HCLOUD_TOKEN" \
  -var "image_name=ubuntu-lemp-v2" \
  ubuntu.pkr.hcl

# Wynik: manifest.json z ID snapshotu
# {
#   "builds": [{
#     "artifact_id": "51234567",
#     "custom_data": { "stack": "lemp" }
#   }]
# }

# Terraform — użycie Packer snapshot przez data source
data "hcloud_image" "lemp" {
  with_selector = "managed-by=packer,stack=lemp"
  most_recent   = true
}

resource "hcloud_server" "web" {
  name        = "web-prod-01"
  image       = data.hcloud_image.lemp.id  # ID ze snapshotu Packer
  server_type = "cx21"
  location    = "nbg1"
  ssh_keys    = [data.hcloud_ssh_key.main.id]
}

Najczęstsze pytania

Do czego służy Packer i czym różni się od Dockera? +
Packer buduje niemodyfikowalne obrazy maszyn wirtualnych (VM images, snapshoty VPS, AMI w AWS). Różnica od Dockera: Docker tworzy kontenery (lekkie procesy współdzielące kernel hosta), Packer tworzy pełne obrazy VM z własnym systemem operacyjnym. Packer jest używany do tworzenia "złotych obrazów" — bazowych VM ze skonfigurowanym OS, narzędziami i konfiguracją bezpieczeństwa, z których następnie tworzy się nowe serwery przez Terraform.
Jakie buildery obsługuje Packer i który wybrać dla VPS? +
Packer obsługuje: amazon-ebs (AWS AMI), googlecompute (GCP), azure-arm, hcloud (Hetzner Cloud snapshots), digitalocean (DO snapshots), qemu (lokalne obrazy QEMU/KVM), vsphere (VMware), virtualbox-iso (VirtualBox OVF). Dla polskich VPS providerów najlepszy jest builder hcloud (Hetzner Cloud) lub qemu dla lokalnego budowania. Jeśli korzystasz z Mikrusa lub ProSerwera, możesz budować obrazy lokalnie QEMU i przesyłać je jako snapshoty.
Jak działa provisioner shell w Packer? +
Provisioner shell uruchamia skrypt powłoki wewnątrz budowanej maszyny. Możesz podać inline (lista komend), script (ścieżka do pliku .sh) lub scripts (lista plików). Packer uruchamia provisioner po uruchomieniu VM (przez SSH lub WinRM), wykonuje skrypt jako root, a potem zatrzymuje maszynę i tworzy snapshot/obraz. Packer obsługuje też provisioner ansible (wywołuje playbook), file (kopiuje pliki) i breakpoint (pauza do debugowania).
Czym są post-processors w Packer i kiedy ich używać? +
Post-processors działają po zbudowaniu obrazu: compress (kompresja do .tar.gz), manifest (JSON z metadata obrazu), checksum (weryfikacja integralności), shell-local (uruchamia lokalny skrypt — np. Ansible do konfiguracji DNS), vagrant (konwertuje do formatu Vagrant box). Manifest jest szczególnie przydatny w CI/CD — zapisuje ID snapshotu do pliku, który Terraform może odczytać przez data source.
Jak integrować Packer z Terraform w pipeline CI/CD? +
Typowy pipeline: (1) Packer buduje "złoty obraz" (snapshot VPS lub AMI) i zapisuje ID w manifest.json; (2) Terraform odczytuje ID obrazu przez data source lub zmienną; (3) Terraform tworzy nowe VM ze zaktualizowanym obrazem (rolling update). W GitHub Actions możesz wywołać `packer build` w jednym jobie, a `terraform apply` w drugim (dependency). Dzięki temu każde nowe wdrożenie startuje z czystego, aktualnego obrazu zamiast starego z akumulowanymi zmianami.

Sprawdź oferty pasujące do tego scenariusza

Poniżej masz szybkie przejścia do ofert i stron z kodami rabatowymi tam, gdzie są dostępne.