Когда у вас десятки одинаковых виртуалок и контейнеров, ставить на каждой пакеты руками или дописывать cloud-init на лету - путь к зоопарку, где две машины с одним именем ведут себя по-разному. Packer решает обратную задачу: один раз собрать готовый образ диска (golden image), в который уже впечатаны ОС, пакеты, конфиги и патчи, а потом разливать его сколько угодно раз - в облако, в KVM, в Docker. В этом уроке разберем, как устроен HCL-шаблон Packer, что такое builders, provisioners и post-processors, почему иммутабельный образ надежнее живого сервера, и как Packer стыкуется с Terraform и Vagrant.

Как это работает
Packer (проект HashiCorp, актуальная ветка 1.12+ на 2026) - это движок, который автоматизирует ровно ту последовательность, что вы делали бы вручную: поднять временную машину из базового образа, дождаться SSH или WinRM, прогнать на ней настройку, выключить и снять с диска готовый артефакт. Сам Packer ничего не настраивает - он оркестрирует три типа плагинов.
Builder отвечает за платформу: где и как поднять временную сборочную машину. Для KVM это qemu (через libvirt или напрямую), для облаков - amazon-ebs, googlecompute, azure-arm, для локали - virtualbox-iso, vmware-iso, для контейнеров - docker. Один и тот же шаблон может содержать несколько билдеров и собрать образ под все платформы за один прогон - это и есть ценность Packer: identical image across providers.
Provisioner - это то, чем вы наполняете машину внутри: shell-скрипты, Ansible, Chef, Puppet, копирование файлов. Провижнеры выполняются по порядку поверх уже загруженной временной ВМ. Post-processor работает уже с готовым артефактом: сжимает образ, заливает в реестр, конвертирует в Vagrant box, считает контрольные суммы, пушит метаданные в HCP Packer registry.
Ключевая идея за всем этим - иммутабельность. Вместо того чтобы патчить работающий сервер (mutable infrastructure, где состояние дрейфует), вы пересобираете образ заново с нуля и выкатываете новые инстансы, а старые гасите. Образ - это артефакт с версией, его можно протестировать в CI, откатить, подписать. Это и называется golden image: эталон, прошедший проверку, из которого штампуются все рабочие машины.
Начиная с Packer 1.7 основной язык шаблонов - HCL2 (файлы .pkr.hcl), старый JSON-формат считается legacy и в 2026 для новых проектов не используется. Плагины с версии 1.7 ставятся отдельно через блок required_plugins и команду packer init - монолитный бинарник со всеми билдерами внутри остался в прошлом.
Команды и примеры
Установка. Репозиторий HashiCorp подключается одинаково по логике, но пакетные менеджеры разные.
Код: Выделить всё
# Debian 13 / Ubuntu 24.04
wget -O- https://apt.releases.hashicorp.com/gpg | \
sudo 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" | \
sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install packer
# RHEL 10 / Fedora 41+
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager addrepo \
--from-repofile=https://rpm.releases.hashicorp.com/fedora/hashicorp.repo
sudo dnf install packer
Код: Выделить всё
packer version
packer plugins installed
Код: Выделить всё
packer {
required_plugins {
qemu = {
source = "github.com/hashicorp/qemu"
version = "~> 1.1"
}
}
}
variable "version" {
type = string
default = "1.0.0"
}
source "qemu" "debian" {
iso_url = "https://cdimage.debian.org/.../debian-13-amd64-netinst.iso"
iso_checksum = "sha256:АБ12..."
output_directory = "output-debian"
vm_name = "debian13-golden-${var.version}.qcow2"
format = "qcow2"
accelerator = "kvm"
disk_size = "10G"
memory = 2048
cpus = 2
ssh_username = "packer"
ssh_password = "packer"
ssh_timeout = "20m"
headless = true
shutdown_command = "echo packer | sudo -S shutdown -P now"
boot_command = ["<esc><wait>auto preseed/url=http://{{.HTTPIP}}:{{.HTTPPort}}/preseed.cfg<enter>"]
http_directory = "http"
}
build {
sources = ["source.qemu.debian"]
provisioner "shell" {
inline = [
"sudo apt-get update",
"sudo apt-get -y install nginx qemu-guest-agent",
"sudo systemctl enable nginx qemu-guest-agent",
]
}
provisioner "ansible" {
playbook_file = "./site.yml"
}
post-processor "checksum" {
checksum_types = ["sha256"]
output = "output-debian/{{.BuildName}}.sha256"
}
}
Код: Выделить всё
packer init . # подтянуть плагины из required_plugins
packer fmt . # привести HCL к канону
packer validate . # проверить синтаксис и ссылки
packer build -var version=2.1.0 golden.pkr.hcl
PACKER_LOG=1 packer build . # подробный лог при отладке
Post-processor для Vagrant превращает образ в box:
Код: Выделить всё
post-processor "vagrant" {
output = "debian13-${var.version}.box"
}
Код: Выделить всё
post-processor "manifest" {
output = "manifest.json"
}
Частые грабли
- Забыли packer init после правки required_plugins - получаете ошибку про отсутствующий плагин. В отличие от старых монолитных версий, билдеры теперь не встроены.
- accelerator = kvm без прав на /dev/kvm - сборка валится или ползет на софтовой эмуляции часами. Пользователь должен быть в группе kvm, модуль kvm_intel/kvm_amd загружен.
- Слишком короткий ssh_timeout - на медленной установке ОС Packer бросает машину раньше, чем она загрузилась, и пишет загадочное Waiting for SSH.
- boot_command с неверными задержками wait - инсталлятор не успевает отрисовать меню, нажатия улетают в пустоту. Это самая хрупкая часть iso-билдеров, отлаживайте с headless = false.
- Секреты прямо в HCL и в Git. Пароли и токены - только через переменные окружения PKR_VAR_* или vault, не в шаблоне.
- Образ собрали, но забыли cloud-init/sysprep - все клоны получают одинаковый machine-id, SSH host keys и иногда MAC. Перед финальным shutdown чистите machine-id и host keys.
- Путают provisioner и post-processor. Провижнер меняет машину изнутри до выключения, пост-процессор работает с уже готовым файлом артефакта.
- Установите Packer из репозитория HashiCorp для вашего семейства (apt или dnf), проверьте packer version.
- Создайте каталог проекта, напишите golden.pkr.hcl с одним source qemu для Debian 13 и блоком required_plugins на плагин qemu.
- Выполните packer init, затем packer fmt и packer validate - добейтесь чистого прохода без ошибок.
- Добавьте provisioner shell, ставящий nginx и qemu-guest-agent, и provisioner для очистки machine-id (truncate /etc/machine-id) перед выключением.
- Запустите packer build с headless = false, понаблюдайте за boot_command и установкой ОС.
- Добавьте post-processor checksum и manifest, пересоберите, проверьте manifest.json и .sha256 в output-каталоге.
- Конвертируйте результат в Vagrant box через post-processor vagrant и поднимите его командой vagrant box add + vagrant up.
- В чем разница между builder, provisioner и post-processor и на каком этапе жизненного цикла образа работает каждый?
- Почему иммутабельный golden image надежнее, чем патчинг работающего сервера, и какие риски это снимает?
- Какую роль играет packer init в современной версии Packer и почему его нельзя пропускать?
- Как разделяются зоны ответственности Packer и Terraform и каким способом передать ID собранного образа в Terraform?
- Что нужно вычистить из образа перед финальным выключением, чтобы клоны не конфликтовали в сети, и почему?
- Чем provisioner shell отличается по применимости от provisioner ansible и когда выбрать каждый?