Всем привет! С вами снова Иван Протченко — инженер из команды Cloud.ru. Как вы знаете, cloud native образы помогают обеспечить предсказуемость, масштабируемость и отзывчивость приложений в облаке. В этой статье я по шагам покажу процесс подготовки таких образов с помощью Packer и QEMU в сочетании с мощным CI/CD-решением — GitLab CI.
Почему именно эти инструменты? Packer от HashiCorp поможет автоматизировать процесс создания конфигурируемых и воспроизводимых образов, QEMU обеспечит гибкость и производительность за счет эмуляции и виртуализации, а интеграция с GitLab CI позволит настроить надежный и повторяющийся пайплайн, который в разы упростит процесс сборки образов. Welcome!

Шаг 1. Подготовим работу с GitLab CI
Перед началом работы:
Клонируйте репозиторий:
https://gitlab.com/IvanProtchenko/packer-cloud-images.git
Создайте новый проект в Settings -> CI/CD -> Runners -> New project runner. Выберите нужные настройки и кликните Create runner:

Выберите операционную систему (в моем случае — Ubuntu 22). Затем сохраните команду для регистрации раннера с токеном и переходите к конфигурированию сервера.

Отключите шаред-раннеры GitLab — переведите переключатель в положение х рядом с пунктом Enable instance runners for this project:

Теперь переходите к установке и подключению раннера (я сделал это на одноплатном компьютере, потому что он пылился без дела).
Сгенерируйте данные для регистрации:
apt update
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
apt install gitlab-runner
Выполните команду подключения раннера:
gitlab-runner register --url https://gitlab.com --token <ваш токен>
Ответьте на вопросы о конфигурации и выберите Shell Runner. Мои ответы выглядят вот так:
root@krd-sr-01:~# gitlab-runner register --url https://gitlab.com --token <ваш токен>
Runtime platform arch=amd64 os=linux pid=3319 revision=12030cf4 version=17.5.3
Running in system-mode.
Enter the GitLab instance URL (for example, https://gitlab.com/):
[https://gitlab.com]:
Verifying runner... is valid runner=t_zfvXth
Enter a name for the runner. This is stored only in the local config.toml file:
[krd-sr-01]:
Enter an executor: ssh, docker-windows, kubernetes, docker-autoscaler, docker, docker+machine, instance, custom, shell, parallels, virtualbox:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"
systemctl status gitlab-runner
systemctl enable gitlab-runner
systemctl restart gitlab-runner
Убедитесь, что раннер доступен. Для этого в Settings -> CI/CD -> Runners проверьте, что рядом с названием раннера отображается зеленый индикатор:

Если раннер недоступен, проверьте работоспособность сервиса (доступен ли GitLab с сервера, на котором настраивается сервис ранера, и запущен ли он).
Добавьте в Settings -> CI/CD -> Variables две переменные:
LIN_PASS — пароль в открытом виде;
LIN_HASH — хешированный пароль, можно сгенерировать как
openssl passwd -6 'your_pass'
.
Также в пункте Visible выберите Masked, а в пункте Flags снимите все чекбоксы, чтобы сохранить значение переменной:

Готово, вы настроили GitLab CI.
Шаг 2. Установим и настроим QEMU
Перед началом работы убедитесь, что включена виртуализация CPU. Если это не так, включите ее через BIOS сервера или ВМ (в зависимости от того, где создаете стенд).
Поскольку HashiCorp блокирует доступ для российских IP, я перенес бинарный файл packer в репозиторий gitlab bin/packer. Установите нужные пакеты командой:
apt install -y qemu-kvm virt-manager libvirt-daemon-system virtinst libvirt-clients bridge-utils
Сконфигурируйте QEMU на работу с packer и проверьте сеть:
root@krd-sr-01:~# virsh net-info --network default
Name: default
UUID: 7c25630e-c3c6-4653-8597-a0cc8c22b217
Active: yes
Persistent: yes
Autostart: yes
Bridge: virbr0
Создайте файл:
mkdir /etc/qemu
echo 'allow virbr0' > /etc/qemu/bridge.conf
Выдайте пользователю gitlab-runner права на использование kvm и подготовьте каталог, в котором будут готовые образы:
chmod u+s /usr/lib/qemu/qemu-bridge-helper
usermod -a -G kvm gitlab-runner
mkdir /opt/packer
chown gitlab-runner:gitlab-runner /opt/packer
Сделайте резервирование в DHCP. Если этого не сделать, то после перезагрузки некоторые ОС могут получить другой адрес и packer не сможет подключится к собираемой ОС. Для этого добавьте одну строку в default сеть:
Отредактировать можно командой:
virsh net-edit --network default
..................
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
<host mac='52:54:00:12:34:56' ip='192.168.122.2'/>
</dhcp>
..................
Сервер и CI подготовлен, теперь можно переходите непосредственно к настройке Packer.
Шаг 3. Запустим пайплайн
Чтобы сделать сборку, выберите одну переменную и нажмите New pipeline:

Запустите пайплайн. Вот что должно происходить при запуске (по ссылкам примеры рабочих конфигураций):
Готово, теперь ваши образы в каталоге /opt/packer.
Дополнительно
Хочу отдельно выделить пару важных моментов.
Про шаблон packer
Хоть для плагина Qemu Packer и есть официальная документация, в которой подробно описан каждый параметр (ну, почти), хочу отдельно остановиться на boot_command:

В документации для него нет примеров и я потратил немало времени, чтобы разобраться. Так что вот вам мой пример — в нем через 10 секунд после включения происходит эмулирование ввода с клавиатуры, дописывание параметров для автоустановки и загрузка ОС.
Про файлы автоответов
В моем примере Ubuntu 24 использует cloud-init autoinstall, который подкладывается как CD-устройство. Если у вас другая ОС, используйте те методы автоустановки, которые они предоставляют:
для Debian — подготовьте preceed.cfg и подберите подходящие boot_command;
для RHEL-подобных — придерживайтесь формата kickstart;
для ALT Linux — используйте формат scm.
Заключение
Это всё, чем я хотел поделиться. Надеюсь, теперь вы тоже будете собирать и поддерживать базовые образы как код. А еще раскроете весь потенциал автосборки и автопубликации и сможете выделить больше времени на другие задачи.
В этой части статьи я показал, как настроить стенд для сборки, а в следующей хочу разобрать подготовку файлов автоответов для разных ОС. Задавайте ваши вопросы в комментариях, а также пишите, для какой ОС хотели бы увидеть шаблон. Если под статьей не будет вариантов, я планирую рассказывать про Astra Linux 😉