Proxmox & packer: автоматизация подготовки VM
Зачем всё это нужно
Полный ответ на этот вопрос лежит в подходе IaC, что заслуживает отдельной статьи. В рамках же данного материала будет продемонстрирован пример создания шаблона ВМ через packer. Наличие такого образа позволяет заранее установить внутрь всё необходимое единожды, тем самым сократить время развертывания целевой системы.
Packer
Hashicorp разработала packer для возможности создания универсального образа операционной системы (golden image), в который заранее упаковывается всё необходимое. На основе этого образа далее уже развёртываются виртуальные машины с нужным набором софта. В терминологии packer так называемый builder выполняет непосредственно сборку, а с помощью плагинов билдера указывается целевая платформа: в основном это облачные провайдеры типа AWS, Yandex, но есть и локальные QEMU, Proxmox и даже Docker или Ansible. Все дальнейшие инструкции будут применимы к плагину Proxmox.
Packer подключается к облаку или локальному гипервизору, создаёт ВМ с указанными параметрами и выполняет установку ОС на основе файла автоматической установки (например, для rpm-based используется kickstart, для deb-based – Subiquity в случае с Ubuntu). А после установки формирует эталонный шаблон из которого клонируются ВМ.
cloud-init
Казалось бы, packer позволил автоматизировать ручной этап. Но при этом добавил новых ручных действий, которые придётся выполнить. Например, какой IP-адрес, хостнейм или ssh-key назначать ВМ, создаваемой из шаблона? Ведь при создании каждой новой ВМ придётся это как-то вручную указывать и менять “эталонный” образ… Или же другой случай: после установки ОС необходимо каждый раз накатывать какой-то типовой софт через ansible, что тоже отнимает время на ручные действия. Тут в игру вступает cloud-init – разработка от компании Canonical.
С помощью cloud-init можно выполнить инициализацию ОС до её финальной загрузки. Такое часто можно встретить в облаках. После создания ВМ из подготовленного шаблона с помощью packer запускается cloud-init, который считывает конфигурационный файл и выполняет указанные там действия. Это может быть как создание нужных юзеров, ключей, установка софта или запуск плейбуков и т.д.
Как это работает
Все действия выполнены на Proxmox 7 и Ubuntu 22.04, в которой используется установщик Subiquity.
1. Packer.
Конфигурационные файлы могут быть в формате HCL или JSON. В рамках данной статьи будет использоваться HCL. В секции source описывается ВМ с нужными параметрами. Тут есть важный параметр boot_command: в нём указывается, как запустить операционную систему. Причём в boot_command указаны комбинации, эмулирующие нажатие физических клавиш, как если бы это делалось вручную в консоли гипервизора. В данном случае packer переключается в консоль grub, указывает ядро для загрузки и добавляет параметры, которые необходимо выкачать по http. Веб-сервером выступает сам packer – установщик ОС подключается к машине, с которой packer был запущен. С помощью этих параметров, полученных через http, будет произведена автоустановка Ubuntu.
Про wsl и сеть. Если packer запущен в WSL, то в идеале он должен получать адрес из той же подсети, в которой создаются VM в Proxmox, чтобы они могли подключиться к веб-серверу Packer. В противном случае будет возникать ошибка на этапе “Reached target Host and Network Name Lookups” или на этапе загрузки AppArmor. Ещё подойдет как вариант portproxy
.
2. cloud-init.
Для работы cloud-init потребуется два YAML файла: user-data
и meta-data
. В user-data указываются необходимые модули, с помощью которых можно выполнить те или иные действия – создать файлы, пользователей и т.д. Примеры различных конфигов user-data можно посмотреть в документации.
Для автоматической установки Ubuntu используется также конфигурация cloud-init:
- Во время установки subiquity создаст конфиг для
cloud-init
по пути/target/var/lib/cloud/seed/nocloud-net/user-data
. - Если секция
user-data
включена в autoinstall, то subiquity соединит содержимое user-data с файлом, который был создан ранее по пути/target/var/lib/cloud/seed/nocloud-net/user-data
- При первой загрузке ОС cloud-init прочитает этот файл и выполнит из него необходимые действия (создаст первого пользователя или установит нужный софт).
То есть проще говоря, конфиг для автоустановки Ubuntu такой же, как и для cloud-init, но в начале файла используется специфичная конструкция autoinstall
для инсталлятора subiquity, а далее уже используются нативные модули cloud-init.
Разбор конфигурационных файлов
Чтобы безмолвно не выкладывать весь конфиг целиком, стоит разобрать некоторые моменты.
- Структура репозитория следующая:
. ├── README.md └── ubuntu-server-jammy ├── http │ ├── meta-data │ └── user-data ├── ubuntu-server-jammy.pkr.hcl └── variables.pkr.hcl
- Имя основного конфигурационного файла для пакера должно заканчиваться *.pkr.hcl.
- В файле variables.pkr.hcl можно определить значения переменных, но мне удобнее это делать через переменные окружения (ещё можно указать значения при запуске packer). Для примера я оставил этот файл в репе с закомментированными значения.
- Основные настройки cloud-init указываются в user-data, а в meta-data указывается служебная информация. Важно! Файл meta-data должен быть, даже если он пустой.
Теперь что касается самого конфигурационного файла ubuntu-server-jammy.pkr.hcl. Для его понимания стоит изучить HCL, тем кто работал с terraform будет проще, т.к. там по сути тоже самое. Основные моменты, на которые стоит обратить внимание:
- В переменных packer для подключения к гипервизору указывается значения из переменных окружения посредством функции
default = env("PROXMOX_API_URL")
- Имя, vm_id и описание шаблона можно выбрать любое, но удобно использовать число для vm_id из диапазона, который не пересекается с ID будущих ВМ.
- Предварительно необходимо скачать локально на гипервизор Proxmox ISO-образ с нужным дистрибутивом, указав его в
iso_file = "local:iso/ubuntu-22.04.1-live-server-amd64.iso"
. Можно также скачать через пакер, указав адрес дистрибутива, но логичнее подготовить образ заранее, чтобы ускорить процесс сборки. - Оперативную память для установки
ubuntu-22.04
лучше выставить в 2 Гб - В разделе boot_command важно указать последовательность загрузки образа. В данном случае дополнительно выставлены таймауты для задержки после команд, т.к. при сборке packer не успевал ввести и выполнить одну команду, как начинал следующую.
- При сборке шаблона packer выступает в роли веб-сервера, к которому подключается установщик Ubuntu. Соответственно, на машине, с которой происходит запуск пакера, должен быть открыть соответствующий порт… который явно по умолчанию не указан и может быть в разных диапазонах, поэтому в конфиге стоит явно задать этот диапазон http_port_min и http_port_max.
- После установки шаблонной системы и её запуска, пакер должен подключиться по SSH к этой ОС и выполнить некоторые команды. Для этого подключения необходимо указать в конфигурационном файле логин\пароль (или ssh ключ). Важный момент: если на хосте, откуда запускается packer, настроен ssh-агент и в конфиге пакера включен
ssh_disable_agent_forwarding
, то подключение к серверу будет осуществлено под локальным пользователем с пробросом агента. В данном же случае в качестве примера указан более простой случай, когда используется простой логин\пароль вида ubuntu\ubuntu - После того, как шаблонная система запущена и к ней осуществлено подключение по ssh, необходимо выполнить некие действия по финальной настройке образа. За это отвечает раздел provisioner. В данном случае это простой shell с некоторыми командами:пока cloud-init не создаст файл /var/lib/cloud/instance/boot-finished, packer будет ждать
- после этого необходимо прибрать после себя, запустив команду cloud-init clean
- а также удалив всю информацию о ssh подключениях и сбросить machine-id, чтобы не клонировать виртуалки с одинаковыми значениями
- в конце явно выполнить sync, чтобы данные записались на диск
- есть примеры готовых скриптов по очистке шаблона тут
Также стоит пробежаться по файлу cloud-init, на основе которого выполняется автоматическая установка ОС:
- Понять суть файла несложно, там всё стандартно: локаль, раскладка, установка SSH…
- Дополнительно ставится пакет sudo (и агент гипервизора), чтобы packer мог в shell provisioner выполнить команды от рута
И самый главный момент – создание пользователей. Здесь уже будут создаваться юзеры и пароли как для packer (с помощью которого он будет выполнять shell-команды из своего конфига), а также конечный пользователь для подключения по SSH после клонирования и запуска ВМ. Здесь важно указать хеш пароля (создав командой mkpasswd –method=SHA-512 –rounds=4096 например). Также можно указать публичный ключ, что будет намного удобнее. Примеры можно посмотреть тут.
Как это запустить
Для сборки и установки шаблона необходимо:
- Установить packer версии не менее 1.8
- Склонировать репозиторй с гитхаб выше
- В файле с переменными нужно указать данные своего гипервизора (можно сделать отдельного юзера с отдельными правами). Или же экспортировать переменные окружения:
export PROXMOX_API_TOKEN_ID='root@pam!packer' export PROXMOX_API_TOKEN_SECRET="7bf07389-db62-4985-aef3-58640dff3beb" export PROXMOX_API_URL="https://192.168.3.100:8006/api2/json"
- Указать в user-data свой приватный ключ или пароль для своего юзера (а юзера ubuntu оставить или переименовать при необходимости – через него будет подключаться сам пакер)
- Запустить сборку и дождаться установки, после чего шаблонная ОС будет автоматически выключена при успешной установке
packer build ubuntu-server-jammy.pkr.hcl
После того, как шаблон появится в гипервизоре, для быстрого создания новой ВМ необходимо выполнить клонирование:
После указать имя ВМ:
Теперь важно указать корректные значения для новой ВМ. Для этого надо скорректировать в разделе cloud-init IP-адрес, маску и шлюз + остальные параметры при необходимости и перегенировать образ уже клонированной ВМ, после чего выполнить запуск:
Подключиться к запущенной ВМ можно по указанному выше IP-адресу (если он задан статически) и с логином\паролем или ssh-ключом, который указан в файле user-data.