Зачем «золотой» образ и почему Packer + cloud-init
«Золотой» образ — это эталонная, воспроизводимая и проверенная сборка ОС с базовой настройкой, драйверами виртуализации, агентами мониторинга и минимально необходимыми пакетами. Из такого образа можно быстро поднимать новые VDS без дрейфа конфигурации и долгих провижинингов на первом запуске. Ключевые выгоды: одинаковая база для всех окружений, ускорение развёртываний, предсказуемая безопасность и понятная процедура обновлений.
В связке packer и cloud-init роли разделены идеально: packer отвечает за сборку и воспроизводимость образа, а cloud-init — за первичную настройку экземпляра при первом буте (пользователь, SSH-ключи, пакеты, переменные окружения, growpart/resizefs и т. п.). Такой дуэт позволяет вынести тяжёлую работу в этап сборки, а оставшееся — автоматизировать на старте каждой виртуалки.
Цели и требования к «золотому» образу
- Совместимость с гипервизором (обычно KVM):
virtio
-драйверы,qemu-guest-agent
, консоль поttyS0
. - Установленный
cloud-init
с корректным порядком модулей и настроенной стратегиейdatasource
. - Безопасные настройки SSH (ключи вместо паролей, запрет root-входа паролем), обновления безопасности и базовые sysctl.
- Авторасширение раздела (growpart) и файловой системы на первом старте.
- Чистота перед снимком: удалены временные файлы, сброшены
machine-id
,ssh_host_keys
, логи, кеши пакетных менеджеров. - Маркировка версии образа (лейбл/файл с версией) и чёткая процедура обновления.
Архитектура пайплайна
Схема проста: packer поднимает временную виртуалку под qemu/kvm, устанавливает/настраивает ОС, добавляет cloud-init и агенты, оптимизирует и чистит систему, затем делает снапшот готового диска (например, qcow2
). Далее образ публикуется в вашем каталоге шаблонов, откуда он используется при создании новых VDS. При первом запуске экземпляр считывает user-data через cloud-init и выполняет заключительную настройку, специфичную для проекта.
Если вы как раз уходите с общего хостинга, пригодится практическое «руководство по переезду на VDS» — смотрите материал «Переезд с общего хостинга на VDS» по ссылке в нашей базе знаний: Переезд с общего хостинга на VDS.
Минимальный HCL-шаблон packer (qemu)
Ниже — упрощённый пример. Он ставит базовую систему из ISO, включает cloud-init
и qemu-guest-agent
, добавляет настройки и делает очистку перед снимком. Параметры ISO/пользователя/пароля вынесены в переменные.
packer {
required_version = ">= 1.10.0"
}
variable "image_version" { default = "2025.01" }
variable "vm_name" { default = "golden-debian" }
variable "iso_url" { description = "Path to local ISO or mirror" }
variable "iso_checksum" { description = "sha256:..." }
variable "ssh_username" { default = "packer" }
variable "ssh_password" { default = "packer" }
source "qemu" "base" {
accelerator = "kvm"
headless = true
iso_url = var.iso_url
iso_checksum = var.iso_checksum
output_directory = "output/${var.vm_name}-${var.image_version}"
vm_name = "${var.vm_name}-${var.image_version}"
disk_size = "12G"
format = "qcow2"
http_directory = "http"
ssh_username = var.ssh_username
ssh_password = var.ssh_password
ssh_timeout = "30m"
shutdown_command = "sudo systemctl poweroff"
boot_wait = "5s"
boot_command = [
"<esc><wait>",
"auto priority=critical preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg",
"<enter>"
]
}
build {
name = "golden-image"
sources = ["source.qemu.base"]
provisioner "shell" {
inline = [
"sudo apt-get update",
"sudo apt-get install -y cloud-init qemu-guest-agent",
"sudo systemctl enable qemu-guest-agent"
]
}
provisioner "file" {
destination = "/tmp/99_fastfox.cfg"
content = <<EOT
# cloud-init baseline config
preserve_hostname: false
ssh_pwauth: false
users:
- default
system_info:
default_user:
lock_passwd: true
gecos: Golden Image Default User
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
shell: /bin/bash
groups: ["adm", "sudo"]
resize_rootfs: true
growpart:
mode: auto
devices: ["/"]
ignore_growroot_disabled: false
EOT
}
provisioner "shell" {
inline = [
"sudo mkdir -p /etc/cloud/cloud.cfg.d",
"sudo mv /tmp/99_fastfox.cfg /etc/cloud/cloud.cfg.d/99_fastfox.cfg",
"echo 'GRUB_CMDLINE_LINUX=\"console=ttyS0,115200n8\"' | sudo tee /etc/default/grub.d/50-serial.cfg",
"sudo update-grub || true"
]
}
provisioner "shell" {
inline = [
"sudo apt-get -y dist-upgrade",
"sudo apt-get -y autoremove --purge",
"sudo apt-get clean"
]
}
provisioner "shell" {
inline = [
"sudo systemctl stop rsyslog || true",
"sudo rm -f /etc/ssh/ssh_host_*",
"sudo truncate -s0 /etc/machine-id",
"sudo rm -f /var/lib/dbus/machine-id",
"sudo ln -s /etc/machine-id /var/lib/dbus/machine-id || true",
"sudo cloud-init clean --logs",
"sudo find /var/log -type f -exec truncate -s0 {} +",
"sudo rm -rf /tmp/* /var/tmp/*"
]
}
}
В боевом окружении вы можете заменить установку из ISO на использование инфраструктурного «базового» cloud-образа и донастройку через packer. Главное — обеспечить в итоге установленный cloud-init
и корректный набор драйверов и сервисов для гипервизора.
Полезные настройки cloud-init в образе
Часть настроек имеет смысл «запекать» в образ, чтобы первичный user-data для отдельных проектов был минималистичным. Речь о поведении growpart
, дефолтном пользователе, блокировке паролей, политике ssh_pwauth
, а также приоритизации источников метаданных (datasource).
# /etc/cloud/ds-identify.cfg
policy: enabled
# Явно укажем список источников метаданных, чтобы cloud-init не тратил время на лишнее
datasource_list: [ NoCloud, ConfigDrive ]
Если в инфраструктуре используется иной источник (например, собственная служба метаданных), добавьте его в список. Так вы ускорите бут и избежите таймаутов ожидания неподходящих источников.
Минимальный user-data для проекта
На момент развёртывания конкретного VDS обычно достаточно передать небольшой user-data с ключом SSH, пакетом необходимых пакетов и парой команд:
#cloud-config
users:
- name: deploy
gecos: Deploy User
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
groups: ["adm", "sudo"]
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAA... comment
package_update: true
packages:
- htop
- curl
- ca-certificates
write_files:
- path: /etc/motd.d/00-image-version
content: |
Golden image version: 2025.01
runcmd:
- [ systemctl, enable, --now, qemu-guest-agent ]
- [ sh, -c, "echo 'Deploy bootstrap OK'" ]
Чем меньше логики на этапе первого запуска — тем надёжнее. Сложные сценарии лучше переносить в этап сборки packer или в последующий слой конфигурации (например, через Ansible после старта экземпляра).
Проверка локально: быстрый цикл отладки
Перед публикацией образа удобно проверять его локально. Стандартный подход: поднять VM из собранного qcow2
и подложить seed с user-data в формате NoCloud. После загрузки проверить логи cloud-init и убедиться, что авторасширение диска, доступ по SSH и агенты отрабатывают корректно.
- Проверить статус:
cloud-init status --long
. - Посмотреть логи:
journalctl -u cloud-init -u cloud-init-local -u cloud-config -u cloud-final
. - Убедиться, что создан пользователь, подставлены ключи, включён
qemu-guest-agent
, а файловая система расширена.
Версионирование и неизменяемость
Рекомендуется встраивать версию образа в артефакт и внутрь файловой системы (например, /etc/image-build
или файл в /etc/motd.d
). Правила именования: год.месяц.пателевел
или семантическая версия с build-метаданными. Не меняйте образ «на месте» — публикуйте новые версии и проводите по ним канареечные развёртывания. Это и есть практика неизменяемых серверов (immutable), которая резко снижает дрейф конфигураций.
Безопасность по умолчанию
- Запрет паролей по SSH:
ssh_pwauth: false
, проверкаPermitRootLogin prohibit-password
вsshd_config
. - Удаление
ssh_host_keys
на этапе сборки и регенерация при первом старте. - Включение автоматических обновлений безопасности дистрибутива.
- Сужение списка datasources в
ds-identify.cfg
, чтобы исключить ложные срабатывания. - Логи и временные файлы — под ноль перед снимком;
machine-id
— обнулить.
Оптимизации времени старта
- Серийная консоль в GRUB:
console=ttyS0
для быстрых диагностик. - Порядок модулей cloud-init: первым
cloud-init-local
для сети, затемcloud-config
иcloud-final
. - Ограничение
datasource_list
— нет лишних таймаутов. - Предварительная установка необходимых пакетов в образ вместо установки на user-data.
Очистка перед публикацией
Список полезных команд для финального шага уже был в примере packer, но повторим общую идею:
- Удалить host-ключи SSH и обнулить
machine-id
. - Очистить кеши пакетов (
apt
/dnf
), логи в/var/log
, временные файлы. - Запустить
cloud-init clean --logs
. - Остановить машину штатно (
systemctl poweroff
) для корректной фиксации файловой системы.
От ISO к «базовому» cloud-образу
Есть два подхода:
- Установка из ISO. Полный контроль, но больше времени на сборку: автопостановка через preseed/kickstart, затем установка cloud-init и пакетов.
- Кастомизация готового cloud-образа. Быстрее: cloud-init и разметка уже присутствуют, вы лишь вносите корпоративные настройки и агенты. В packer это реализуется через запуск образа в qemu и дальнейшие provisioners.
Выбор зависит от требований к прозрачности цепочки поставки и длительности сборки. Нередко в инфраструктуре применяется смешанный подход: собственный «базовый» образ, собранный из ISO, а затем — проектные кастомизации поверх. Если требуется удобная панель управления, пригодится обзор: сравнение панелей для VDS.
Тест-кейсы до выката
- Валидность образа: поднимается ли VM, корректно ли работает сеть и консоль.
- Отработка user-data на первом старте, включая
packages
,write_files
,runcmd
. - Регенерация
ssh_host_keys
и доступ по публичному ключу. - Расширение раздела и файловой системы на полный размер диска.
- Версионирование: присутствует ли файл/лейбл версии внутри системы.
Релизы и обновления: процесс
Фиксируйте HCL и cloud-config в репозитории, используйте теги для релизов, храните артефакты с контрольными суммами. Держите две ветки: stable и next. Новая версия проходит автоматические тесты (проверка загрузки, доступности по SSH, отработки cloud-init), далее — канареечный выпуск на часть новых VDS. Только после стабилизации — перевод в stable.

Типичные ошибки и отладка
- Долгая загрузка cloud-init. Проверьте
datasource_list
и сетевые таймауты. В логах часто видно ожидание ненужных источников. - Нет авторасширения диска. Убедитесь, что включены
growpart
иresize_rootfs
, а корневой раздел размечён подходящим образом. - Не генерируются SSH host keys. Проверьте наличие соответствующих
systemd
-юнитов и отсутствие предзапечённых ключей в образе. - Проблемы с агентом гостя. Убедитесь, что установлен и включён
qemu-guest-agent
, проверьте сокеты и разрешения. - Случайные конфликты сети. Если вы используете netplan или NetworkManager, проверьте, не мешает ли им конфигурация от cloud-init. Иногда полезно делегировать сети только одной системе.
Расширение пайплайна: Ansible, тесты, подписи
packer легко дополняется Ansible-провижинингом для более сложных ролей (например, предустановка агента мониторинга, логгера, базовых правил auditd
). Автотесты на стадии сборки можно реализовать через скрипты проверки сервисов и логов. Для целостности добавляйте контрольные суммы и подписи артефактов.
Итог: packer даёт воспроизводимый артефакт, cloud-init — быстрый и минималистичный провижининг на первом старте. Вместе они формируют надёжную основу для массовых развёртываний VDS, сводя ручные действия к нулю и повышая безопасность.
Чек-лист перед публикацией образа
- cloud-init установлен, модули выполняются,
datasource_list
настроен. - Включён
qemu-guest-agent
, консоль доступна поttyS0
. - SSH-пароли отключены, ключи не запечены в образе.
- Готовность к росту диска на первом старте.
- Версия образа зафиксирована внутри системы и в имени артефакта.
- Проведены локальные тесты и проверка логов cloud-init.
- Очистка выполнена: логи, кеши,
machine-id
,ssh_host_keys
.
Заключение
Собственный «золотой» образ — это инструмент, который окупается с первого десятка развёртываний. Связка packer + cloud-init обеспечивает быстрое масштабирование и предсказуемые релизы. Начните с минимальной базы (драйверы, cloud-init, агент гостя, безопасность по умолчанию), выстройте процесс версионирования и канареек, и через пару итераций у вас появится стабильный, хорошо документированный образ, который экономит часы на каждом новом сервере.