Новый проект - новый сервер. Установка системы, настройка сети, патчи, мониторинг, нужный софт. Три часа - и готово. Через неделю ещё один, и всё заново. Через месяц выясняется, что машины настроены немного по-разному, и никто уже не помнит, чем именно.
Шаблон решает ровно эту задачу: один раз собрали эталонную машину, а дальше разворачиваете копии за минуту, и все они одинаковые. Разберём, как это устроено на практике - как снять шаблон, что обязательно стереть перед снятием, чем готовят Linux и чем Windows, и почему клон иногда получает тот же IP-адрес, что и его сосед.
Слова часто путают, а разница принципиальная.
Снимок (snapshot) - точка возврата для одной конкретной машины. Он привязан к ней, живёт рядом с её диском и нужен, чтобы откатиться после неудачного обновления. Разворачивать из снимка новые машины нельзя.
Клон - копия существующей машины со всем её содержимым, включая уникальные идентификаторы. Если склонировать работающий сервер и запустить копию в той же сети, вы получите два хоста с одинаковым именем, одинаковыми ключами SSH и, скорее всего, конфликт адресов.
Шаблон - специально подготовленная машина, из которой уникальное вычищено заранее. Она не запускается сама и существует только как заготовка. В Proxmox шаблон ещё и защищён от случайного старта: после превращения в шаблон машину нельзя включить, пока не сделаешь из неё клон.
Разница между снимком и шаблоном - это разница между «вернуться назад» и «размножить вперёд». Подробнее про первое - в материале про снимки виртуальных машин и их подводные камни.
Дальше нужно решить, сколько всего класть внутрь.
Полностью укомплектованный шаблон (в англоязычных материалах его называют golden image) несёт систему со всем нужным софтом. Разворачивается быстро, машина сразу готова к работе. Минус в обновлениях: вышел патч - надо пересобирать сам шаблон, иначе каждая новая машина рождается устаревшей.
Минимальный шаблон несёт только систему, а всё остальное доставляется при первом запуске - через cloud-init или систему управления конфигурацией. Разворачивается дольше, зато шаблон почти не устаревает: свежие пакеты приезжают в момент установки.
На практике большинство останавливается посередине: в шаблон кладут систему, базовые утилиты и агент гипервизора, а прикладной софт ставят после развёртывания. Это разумный компромисс, и именно его стоит брать по умолчанию, если у вас десяток-другой машин.
В Proxmox клон бывает двух видов, и выбор влияет и на скорость, и на то, что вы потом сможете с машиной делать.
| Полный клон | Связанный клон | |
|---|---|---|
| Что копируется | весь диск целиком | только изменения поверх шаблона |
| Время создания | минуты, зависит от размера | секунды |
| Место на диске | как у шаблона | сначала почти ноль |
| Зависимость от шаблона | нет | шаблон удалять нельзя |
| Перенос на другой узел | свободно | только если хранилище общее |
| Где работает | на любом хранилище | не работает на обычном LVM и iSCSI |
Связанный клон читает неизменённые блоки прямо из диска шаблона, а записывает только свои. Отсюда и скорость, и экономия места, и главное ограничение: шаблон становится частью инфраструктуры навсегда. Удалите его - и все связанные клоны умрут.
Связанные клоны работают на хранилищах, которые умеют такую схему: файловые в формате qcow2, vmdk и raw (локальные и по NFS), LVM-thin, ZFS и Ceph. На «толстом» LVM и на iSCSI без прослойки их сделать нельзя - там доступен только полный клон.
Практическое правило простое. Тестовые и временные машины - связанные клоны, там ценна скорость. Всё, что будет жить долго и переезжать между узлами, - полный клон.
Порядок такой: собрать эталонную машину, вычистить из неё уникальное (об этом ниже), выключить и превратить в шаблон.
qm template 9000
Операция необратимая: обратно в обычную машину шаблон не превращается, поэтому эталон обычно держат отдельно или делают копию до превращения.
Дальше из шаблона делают машины. Полный клон:
qm clone 9000 105 --name web-03 --full
Здесь 9000 - шаблон, 105 - идентификатор новой машины. Без флага --full получится связанный клон - и работает это только если источник действительно шаблон, а не обычная выключенная машина.
Отдельно стоит упомянуть готовые облачные образы. Производители дистрибутивов выкладывают их специально под такой сценарий: система уже установлена, урезана и подготовлена к персонализации. Подключается такой образ прямо диском:
qm set 9000 --scsi0 local-lvm:0,import-from=/root/noble-server-cloudimg-amd64.img
Облачные образы рассчитаны на текстовую консоль, поэтому им обычно добавляют последовательный порт:
qm set 9000 --serial0 socket --vga serial0
Как пользоваться этой консолью и как из неё выходить, разобрано в материале про управление виртуальными машинами Proxmox через CLI.
Это тот шаг, который пропускают чаще всего, а потом ловят странные аварии. Перед превращением машины в шаблон из неё нужно убрать всё, что должно быть у каждого хоста своим.
Ключи хоста SSH. Если их не удалить, все машины из шаблона будут представляться одним и тем же ключом. Клиенты не заметят подмены одного сервера другим, а это ровно то, от чего ключи защищают.
rm -f /etc/ssh/ssh_host_*
При следующей загрузке пакет sshd сгенерирует новые. Убедитесь, что в системе это действительно происходит автоматически - в минимальных сборках иногда приходится добавлять генерацию явно.
Идентификатор машины. Вот здесь кроется самая неприятная ловушка, потому что она проявляется не сразу и выглядит как проблема сети.
В Ubuntu 20.04 и новее клиент DHCP по умолчанию представляется не MAC-адресом, а содержимым /etc/machine-id. Если шаблон снят с непочищенной машины, все клоны отправляют один и тот же идентификатор - и DHCP-сервер честно выдаёт им один и тот же адрес. Выглядит это как «две машины дерутся за IP», а причина лежит в файле, о котором никто не думает.
Чистится он так:
truncate -s 0 /etc/machine-id
rm -f /var/lib/dbus/machine-id
ln -s /etc/machine-id /var/lib/dbus/machine-id
Обратите внимание: файл /etc/machine-id нужно именно опустошить, а не удалить. Если его удалить, при загрузке новый идентификатор не сгенерируется, и система будет вести себя непредсказуемо. Пустой файл - сигнал systemd «сгенерируй мне новый», отсутствующий файл такого сигнала не даёт.
Состояние cloud-init, если он установлен. Он запоминает, что уже отработал, и в клоне повторно настраивать ничего не станет:
cloud-init clean --logs
Всё остальное личное: история команд, временные файлы, кэш пакетного менеджера, журналы, авторизованные ключи технических учёток, если они выдавались персонально. Отдельно проверьте статические настройки сети - в шаблоне они почти всегда лишние, адрес должен приезжать снаружи.
Вычистить уникальное - половина дела. Вторая половина: новая машина должна получить своё имя, адрес, учётную запись и ключи. Вручную это делать бессмысленно, для этого есть cloud-init.
Работает он так: при первом запуске система ищет источник настроек, читает оттуда параметры и применяет их до того, как поднимутся основные службы. В Proxmox источником служит виртуальный диск, который гипервизор пересобирает при каждом старте машины из того, что вы задали в её настройках.
Подключается этот диск один раз, на этапе подготовки шаблона:
qm set 9000 --ide2 local-lvm:cloudinit
Дальше параметры делятся на две группы. То, что одинаково для всех будущих машин, задают один раз на самом шаблоне - клоны это унаследуют:
qm set 9000 --ciuser admin
qm set 9000 --sshkey ~/.ssh/id_rsa.pub
Первая строка создаёт учётную запись, вторая кладёт в неё открытый ключ.
А то, что у каждой машины своё, задают уже клону, после qm clone:
qm set 105 --ipconfig0 ip=10.0.10.105/24,gw=10.0.10.1
Вместо статики можно написать ip=dhcp - тогда адрес придёт от сервера, и как раз здесь важно, чтобы machine-id был почищен, иначе все клоны попросят один и тот же адрес.
Складывается это в такую последовательность: сделали клон, задали новой машине адрес, запустили - и через минуту получили готовый сервер с доступом по ключу. Пароли в такой схеме не нужны вовсе.
Для тех, кто хочет большего, cloud-init умеет ставить пакеты и выполнять произвольные команды при первом запуске через файл настроек в формате YAML. Но начинать стоит с трёх параметров выше - они закрывают подавляющее большинство задач.
У Windows своя процедура, и обойти её нельзя. Каждая установка несёт уникальный идентификатор безопасности (SID), на который завязаны учётные записи и членство в домене. Две машины с одинаковым SID в одном домене - это проблемы с правами, которые диагностируются мучительно.
Приводит систему в «обезличенное» состояние утилита sysprep, лежащая в C:\Windows\System32\Sysprep. Запускают её так:
sysprep.exe /generalize /oobe /shutdown
Разберём флаги, потому что каждый важен:
Отвечать на вопросы первичной настройки вручную для каждой машины никто не хочет, поэтому вместе с sysprep используют файл ответов unattend.xml: в нём заранее описаны имя, раскладка, часовой пояс, локальный администратор и ввод в домен.
Про Windows нужно знать одно ограничение, которое ломает планы. Обезличивание считается сбросом активации, а сбросов у системы конечное число - три. Четвёртый запуск sysprep на том же образе завершится ошибкой. Посмотреть остаток можно командой slmgr /dlv - в выводе есть счётчик оставшихся сбросов.
Отсюда практическое следствие: не отлаживайте шаблон запусками sysprep. Соберите эталон, сделайте снимок машины до обезличивания, и уже из этого снимка возвращайтесь для правок. Тогда счётчик расходуется только на финальные сборки. Обойти лимит параметром SkipRearm в файле ответов технически можно, но перед последним, «боевым» запуском его всё равно нужно вернуть в ноль, иначе машины поедут в работу с несброшенной активацией.
Отдельно про самую частую ошибку. Если sysprep обрывается с фатальной ошибкой, первым делом смотрите журнал в %WINDIR%\System32\Sysprep\Panther. В подавляющем большинстве случаев там будет строка вида «package ... was installed for a user, but not provisioned for all users».
Причина в приложениях из магазина Microsoft: если такое приложение обновилось или было поставлено под конкретной учётной записью, оно перестаёт считаться установленным «для всех», а /generalize этого не допускает. Найти виновника можно, сравнив два списка - что установлено у пользователей и что заготовлено в образе:
Get-AppxPackage -AllUsers | Select-Object Name, PackageFullName
Get-AppxProvisionedPackage -Online | Select-Object DisplayName, PackageName
Лишний пакет удаляют у пользователя и убирают из образа:
Remove-AppxPackage -Package
dism /online /Remove-ProvisionedAppxPackage /PackageName:
Отсюда практический вывод: эталонную машину не надо трогать магазином приложений и не надо заходить на неё лишними учётными записями. Чем меньше на ней жили, тем спокойнее проходит обезличивание.
Ещё две вещи, которые стоит сделать до sysprep: установить драйверы гипервизора (VirtIO для KVM и Proxmox, соответствующие службы интеграции для Hyper-V и VMware) и полностью обновить систему. Драйверы после обезличивания добавлять неудобно, а необновлённый шаблон означает, что каждая новая машина начинает жизнь с длинного цикла установки обновлений.
Короткий список, который экономит много времени потом.
Секреты. Пароли, ключи API, сертификаты, файлы подключения к базам. Всё это расходится по всем машинам разом, а отозвать потом придётся везде. Секреты доставляют при развёртывании или берут из хранилища секретов.
Агенты мониторинга, антивирус и клиенты резервного копирования. Они регистрируются на сервере по идентификатору, и клоны либо перезапишут друг друга, либо создадут кучу мусорных записей. Ставить их лучше после развёртывания.
Машина, введённая в домен. Ввод в домен делается на конкретную учётную запись компьютера. Шаблон готовят на машине вне домена, а ввод выполняют при первом запуске - через файл ответов в Windows или скриптом в Linux.
Лицензии, привязанные к железу или к имени хоста. После клонирования они превратятся в тыкву, причём молча.
Шаблон стареет с первого дня: выходят обновления безопасности, и машина, развёрнутая через полгода, приезжает с полугодовым отставанием.
Рабочая практика для небольшой инфраструктуры выглядит скромнее, чем принято писать в статьях про автоматизацию. Достаточно раз в квартал (а для систем с высокими требованиями к безопасности - раз в месяц) поднимать эталон из снимка, ставить обновления, проверять, что машина загружается и ключевые службы работают, и пересобирать шаблон. Версию удобно писать прямо в имени: ubuntu-2404-base-v3, win2022-std-v2. Старую версию не удаляют сразу - пусть полежит, пока не станет ясно, что новая не привезла сюрпризов.
Если машин много и шаблонов несколько, сборку автоматизируют. Packer от HashiCorp описывает сборку образа конфигурационным файлом и умеет собирать под разные платформы виртуализации из одного описания; Ansible и подобные системы доводят получившуюся машину до нужного состояния. Оба инструмента живут в системе контроля версий, и это даёт то, ради чего всё затевается: видно, кто, когда и что менял в образе.
Но соизмеряйте усилия с масштабом. Если у вас три шаблона и двадцать машин, конвейер сборки образов с автотестами и продвижением по средам будет дороже в поддержке, чем ручная пересборка раз в квартал. Автоматизация окупается, когда шаблонов десятки или когда их обязана пересобирать не одна и та же пара рук.
Шаблон сняли с работающего сервера. Самая частая. В образ уезжают ключи, идентификаторы, журналы и настройки конкретного хоста. Внешне всё работает, а через месяц выясняется, что половина парка представляется одним ключом SSH.
Диск шаблона раздут. Перед снятием стоит почистить кэш пакетного менеджера и временные файлы, а на тонких хранилищах - обнулить свободное место, чтобы удалённые данные не занимали место в копиях. Разница бывает в разы.
Не проверили загрузку клона. Шаблон снят, всё выглядит хорошо, а первая машина из него не поднимается: не сгенерировались ключи, не отработала персонализация, слетела сеть. Правило простое - развернуть из нового шаблона одну машину и убедиться, что она загрузилась и доступна, до того как шаблон уйдёт в общее пользование.
Никто не знает, что внутри. Через год шаблоном пользуются все, а что в нём стоит и почему, не помнит никто. Лечится текстовым файлом рядом с шаблоном: из какого образа собран, что установлено, когда обновлялся, кто отвечает.
Шаблон под каждый случай. Плодить отдельный шаблон под каждую роль соблазнительно, но обновлять потом придётся все. Лучше один базовый на дистрибутив плюс скрипты под роли: обновляется одно, ролей может быть сколько угодно.
Как перенести в виртуальную среду уже работающий физический сервер - в материале про конвертацию физических серверов в виртуальные. Сколько ресурсов закладывать будущим машинам - в разборе про планирование CPU, RAM и хранилища. Как переносить готовые машины между узлами - в статье про Ubuntu и Proxmox и миграцию виртуальных машин.
Подбираете сервер под виртуализацию?
Инженеры ITTELO помогут рассчитать, сколько ядер, памяти и дисков понадобится под ваше число виртуальных машин, подскажут по хранилищу под связанные клоны и снимки, соберут и протестируют конфигурацию под задачу перед отгрузкой, с гарантией и поддержкой после продажи. На рынке серверов 11+ лет.