В качестве системы виртуализации на всех локациях мы в HOSTKEY используем платформу Ovirt, основанную на технологии KVM. Поскольку изначально основным продуктом нашей компании была сдача в аренду bare-metal серверов, у нас уже была наработана экспертиза по установке операционных систем по сети (о чем рассказывали ранее в статье Linux LiveCD на базе CentOS и техники его использования в PXE-загрузке через Foreman).
При внедрении виртуализации логичным решением стало создание новых виртуальных машин с установкой операционных систем по сети - аналогично физическим серверам. Такой вариант изначально показался простым и эффективным решением. Практика же показала, что это был костыль, превратившийся со временем в узкое место деплоя и головную боль для сотрудников компании.
По мере роста количества виртуальных машин и расширения парка нод виртуализации (уже было около 100 нод и 10 локаций - Россия, Нидерланды, США, Турция, Германия, Исландия, Франция, Финляндия, Великобритания, Испания), поддерживать и обновлять локальные шаблоны стало проблематично и накладно по затратам рабочего времени. Необходимо было найти способ автоматизировать процесс управления темплейтами.
Также это должно было быть альтернативное решение и мы остановились на развертывании операционных систем через темплейты. Забегая вперед - результат нас полностью устроил: новые машины разворачиваются практически мгновенно, а процедура переустановки ОС занимает несколько минут.
Что мы решили?
Итоговым решением стала интеграция компонента OpenStack Glance в существующую инфраструктуру oVirt. Glance — это служба управления образами, позволяющая пользователям находить, извлекать и регистрировать образы виртуальных машин (VM) и образы контейнеров.
Мы начали с изучения встроенных возможностей oVirt. Архитектурные особенности этой платформы, а именно «один дата-центр = одна нода», позволяли только размещать основные темплейты на каждой ноде. Так и сделали, заготовили темплейты, написали автоматику по раскатке образов и по использованию cloud-init и жили дальше.

Процесс развертывания выглядел так: Go-приложение создавало новую виртуальную машину на основе шаблона, затем система автоматически запускала машину и инициировала Jenkins-задачу. Jenkins подключался к виртуальной машине по SSH и выполнял финальную настройку под требования клиента: устанавливал пароли, изменял размеры дисков, применял необходимые конфигурации. При необходимости дополнительно устанавливались панели управления из нашего маркетплейса.
Старая схема деплоя:

Новая схема деплоя:

Новый деплой по шагам
Давайте разберем алгоритм деплоя подробнее.
Этап 1. Добавление Glance репозитория на oVirt
-
Добавление провайдера. Переходим в раздел Administration >> Providers в интерфейсе oVirt:
Нажимаем Add и заполняем поля конфигурации:
-
Импорт шаблонов
Для добавления шаблонов из Glance репозитория переходим в Storage >> Domains:
Процесс импорта включает:
- Выбор нужного шаблона;
- Указание Data Center и кластера для загрузки;
- Установку чекбокса Import as Template;
- Добавление в имени шаблона суффикса с названием ноды (как показано на скриншоте);
- Подтверждение операции кнопкой OK:
Этап 2. Автоматизация процесса
Для автоматизации процесса написан Ansible playbook, который решает основные задачи: развертывание OpenStack Glance, монтирование NFS, импорт образов, управление жизненным циклом шаблонов. Учитывая масштаб инфраструктуры, ручное управление точно не подходило. Для удобства управления настроена Jenkins-задача.
Управляющий сервер для развертывания и управления образами в Glance:



Этап 3. Изменения кода приложения
После внедрения Glance репозитория потребовалось адаптировать приложение для автоматического развертывания виртуальных машин. Ранее это приложение работало с локальными шаблонами, и мы переключили его на получение шаблонов из Glance репозитория.
Для этого немного поправили программу — теперь нужно брать шаблоны не локально, а с Glance:
sdsService := conn.SystemService().StorageDomainsService()
sdGlance := sdsService.List().
Search("name=glance-local").
MustSend().
MustStorageDomains().
Slice()[0]
// Поиск сервиса управления Glance storage domain
sdService := sdsService.StorageDomainService(sdGlance.MustId())
// Поиск сервиса управления образами в storage domain
imagesService := sdService.ImagesService()
imageSlice := imagesService.List().MustSend().MustImages()
Алгоритм работы остался прежним: ищем нужные шаблоны и разворачиваем из них диски, далее всё так же — создаем машину, прикрепляем новый диск, включаем, дергаем Jenkins-таск, который заходит на машину и готовит ее под клиента. Задает пароль, ресайзит диски и так далее, при необходимости ставит панель из маркетплейса.
Важные операции постконфигурации:
- Изменение размера дисков;
- Замена паролей;
- Проброс SSH-ключей;
- Выполнение пользовательских скриптов.


Этап 4. Управление шаблонами
Различные операции внутри шаблонов выполняются с помощью virt-customize, далее шаблон сжимается с помощью virt-sparsify и закидывается в папку /mnt/repopool/ovirt-images на центральном сервере хранения шаблонов, откуда уже разливается по глансам при помощи соответствующей задачи. Playbook делает бекап прошлого шаблона в папку /mnt/ovirt-images_build/backup на центральном сервере — в случае проблем можно откатиться на него.

Этап 5. Конвертация в формат qcow2
Конвертация темплейтов виртуальных машин в формат qcow2 позволяет существенно уменьшить размер файлов образов, экономя дисковое пространство без ущерба для функциональности. Ниже представлена инструкция по проведению конвертации.
- Заходим на хост с темплейтами под пользователем root.
-
Настраиваем окружение:
-
Переходим в папку пользователя: cd /home/ipausers/<имя_пользователя>/
-
Запускаем скрипт настройки переменного окружения командой
source ./admin-openrc.
-
-
Смотрим доступные темплейты командой
glance image-list.
Затем находим нужный темплейт в списке (например, Ubuntu 24.04).
-
Скачиваем темплейт:
glance image-download --file ubuntu_24_04 a121c666-b73f-4723-913a-535a0eeff60f --progress
-
Дожидаемся завершения скачивания, затем проверяем наличие файла командой Is.
-
Подготавливаем сервер для конвертации. Для этого подключаемся к отдельному серверу, где можно выполнить команду конвертации. При необходимости можно увеличить размер файла подкачки командами (назначается место файла подкачки, выполнить команду необходимо на сервере конвертации):
TMPDIR=/mnt/tmp2 export LIBGUESTFS_BACKEND=direct
-
Выполняем копирование файла на сервер конвертации. Необходимо использовать команду scp для копирования:
scp <имя_файла> user@server:/path/to/destination/
Например:
scp [email protected]:/root/<имя_пользователя>
-
Выполняем конвертацию файла. На сервере конвертации для этого необходимо выполнить команду:
virt-sparsify <имя_исходного_файла> --convert qcow2 <имя_конвертированного_файла>.qcow2
Например:
virt-sparsify ubuntu_24_04 --convert qcow2 ubuntu_24_04.qcow2
-
Выполняем копирование конвертированного файла обратно на исходный сервер:
scp <имя_сервера_конвертации><путь_к_файлу><имя_файла>.qcow2 /home/ipausers/<имя_пользователя> ls -la
Например:
scp [email protected]:/root/<имя_пользователя>/ubuntu_24_04.qcow2 /home/ipausers/<имя_пользователя> ls -la
-
Проверяем размер файлов. Сравнить размеры исходного и конвертированного файлов можно командой ls -lh.
-
Обновляем темплейт в Glance.
a. Удаляем старый темплейт командой:
glance image-delete <ID_старого_темплейта>
Например:
glance image-delete a121c666-b73f-4723-913a-535a0eeff60f
b. Создаем новый темплейт командой:
glance image-create --name <имя_файла> --disk-format qcow2 --container-format bare --file <имя_файла>.qcow2 --progress --min-ram 1 --os-distro <имя_дистрибутива> --architecture amd64 --min-disk 10
Например:
glance image-create --name ubuntu_24_04 --disk-format qcow2 --container-format bare --file ubuntu_24_04.qcow2 --progress --min-ram 1 --os-distro ubuntu24 --architecture amd64 --min-disk 10
-
Проверяем результат, выполнив команду для проверки списка темплейтов:
glance image-list
Примечание: все команды следует выполнять с соответствующими правами доступа. Перед началом работы необходимо убедиться в наличии достаточного места на дисках серверов для выполнения операций.
Этап 6. Процесс создания шаблонов виртуальных машин
Создание шаблонов в нашей инфраструктуре включает несколько ключевых этапов: подготовку базового образа, настройку cloud-init, оптимизацию через libguestfs-tools и распространение через Glance. Рассмотрим процесс подробнее. В качестве примера будем использовать Ubuntu 25.04.
1. Установка базового образа
Процесс начинается с установки ОС из ISO-образа на выбранную виртуальную машину. В настройках VM необходимо подключить ISO-образ через меню Boot options:

2 Настройка LVM-разметки
Критически важным моментом является корректная настройка LVM-разметки. При установке требуется создать кастомную разметку вместо стандартной:


Необходимо размонтировать корневой раздел и удалить логический том для переименования:


Затем переименовать группу томов и создать новый логический том с корректными именами:



После установки важно проверить корректность разметки и отключить CD:

3 Настройка cloud-init
В Ubuntu 25.04 требуется ручная активация сервисов cloud-init и настройка SSH-доступа:
systemctl enable cloud-init-{local,main,network}.service # активация необходимых сервисов
rm -rf /etc/cloud/cloud-init.disabled # очистка конфликтующих конфигураций
cloud-init clean --machine-id # сброс состояния для подготовки к клонированию
Также требуется настройка SSH для root-доступа в /etc/ssh/sshd_config.
4 Постобработка с libguestfs-tools
После базовой установки образ проходит постобработку с использованием libguestfs-tools для удаления пользовательских данных и оптимизации:
virt-edit -a template.qcow2 /etc/ssh/sshd_config # Редактирование конфигурационных файлов
virt-customize -a template.qcow2 --run-command "userdel username" # Удаление временных пользователей
virt-customize -a template.qcow2 --run-command "apt update && apt install -y package" # Установка дополнительных пакетов при необходимости
5 Распространение через Glance
Готовые шаблоны размещаются в централизованном хранилище и распространяются на все локации через Jenkins-задачу. Этот процесс обеспечивает консистентность образов во всей инфраструктуре и автоматизирует развертывание новых шаблонов.
Финальный этап включает регистрацию шаблона в системе инвентаризации с соответствующими тегами для автоматического использования в процессах развертывания.
Заключение
Внедрение OpenStack Glance позволило изменить и улучшить подход к развертыванию виртуальной инфраструктуры. Переход от локальных шаблонов на каждой ноде к централизованному хранилищу образов решил ключевые проблемы масштабирования и управляемости: увеличилась скорость развертывания ПО, устранена необходимость поддерживать локальные шаблоны на ~100 нодах в 10 локациях, получен полностью автоматизированный процесс обновления и распространения шаблонов, как следствие - снижение трудозатрат сотрудников компании.
Интеграция Glance с существующей инфраструктурой oVirt позволила сохранить привычные рабочие процессы, при этом значительно упростив административные задачи. Ansible playbooks и Jenkins-задачи обеспечивают бесшовное управление жизненным циклом образов, а система резервного копирования гарантирует возможность быстрого отката в случае проблем.
Использование формата qcow2 и инструментов virt-sparsify дополнительно оптимизировало использование дискового пространства, что полезно при работе с большим количеством образов различных операционных систем.
Централизованная система шаблонов упростила не только техническое администрирование, но и процессы разработки. Теперь команда может быстро предоставлять клиентам новые операционные системы, оперативно внедрять обновления и управлять инфраструктурой даже с привлечением начинающих специалистов.
В дальнейшем мы планируем продолжить работу по переходу на полноценную OpenStack-инфраструктуру. Этот переход позволит нам полностью отказаться от использования oVirt и получить гибкую систему управления виртуализацией.