README.md

Вводная

Проект libvirt4gitlab-ci предназначен для автоматизации развертывания виртуальных машин посредством ci/cd модуля gitlab. Проект базируется на кодовой базе https://docs.gitlab.com/runner/executors/custom_examples/libvirt.html с некоторыми доработками.

Архитектурно состоит из двух модулей:

  • автоматизированная установка шаблона ОС с iso + подготовка к интеграции с gitlab
  • генерация и управления машинами производными от шаблона

Структура каталогов:

  • backends/ сценарии запуска virt-inst при создании вм для шаблонов
  • scipts/ диалоги работы с инсталятором ос при создании шаблонов
  • roles/ роли применяемые при настройке шаблонов
  • osid/ параметры автоматической идентификации iso и специфические для дистрибутива параметры
  • hooks/ диалоги работы с консолю вм при ее загрузке, например кодовая фраза для luks (не доделано)
  • examples/ всякая вторичная мура
  • tests/ тестовые конвееры для проверенных дистрибутивов
  • base.sh сценарий с общими функциями
  • os-install/os-template - сценарии автоматизированной установки ос
  • driver.conf - общие переменные
  • prepare.sh - сценарий развертывания шаблона
  • run.sh - сценарий запуска скрипта задачи гитлаба
  • cleanup.sh - удаление вм после окончания конвеера
  • executor.sh - общие параметры специфичные для раннера custom
  • TESTED.md - проверенные дистрибутивы
  • TODO.md - задачи на доработку

Установка гипервизора на примере debian 11.4

Установить ОС в варианте с openssh (использовался debian-11.4.0-amd64-DVD-1.iso). Поставить необходимые пакеты:

apt install -y curl git acpid locales bridge-utils sudo attr libvirt-daemon libvirt-daemon-system apg expect virtinst libarchive-tools whois libosinfo-bin genisoimage

Создать сетевой мост. Сеть в которую подключаются машины должна раздавать параметры по dhcp. Как вариант, можно создать сеть типа NAT в самом libvirt, в таком случае имя интерфейса моста будет что-то типа virbr0.

# create bridge with interface to network
cat <<EOF>/etc/network/interfaces.d/br0
auto br0
iface br0 inet static
bridge_ports enp1s0
bridge_stp off
bridge_waitport 0
bridge_fd 0
address 192.168.100.168
netmask 255.255.0.0
gateway 192.168.1.1
EOF

Склонировать репу:

git clone ... /opt
cd /opt/libvirt4gitlab-ci/

Отредактировать [driver.conf] указав имя устройства моста по-умолчанию в параметре VM_BRIDGE_DEFAULT.

Установить пакет gitlab-runner. Добавить пользователя gitlab-runner в passwordless sudo:

cat <<EOF>/etc/sudoers.d/gitlab-runner
gitlab-runner ALL=(ALL) NOPASSWD: ALL
Defaults:gitlab-runner !requiretty

Зарегистрировать в гитлабе два раннера:

  • с тегом l4g-shell и типом shell. Раннер используется для подготовки шаблонов и прочих операций требующих контроля над гипервизором;
  • с тегом l4g и типом custom. Отвечает за управление производными виртуалками.

Поправить в конфиге /etc/gitlab-runner/config.toml секции указав параметры builds_dir,cache_dir и пути до сценариев prepare,run и cleanup. Например:

[[runners]]
  name = "test-arm"
  url = "http://mygitlab"
  token = "xxxxxxxxxxxx"
  executor = "custom"
  builds_dir = "/home/gitlab-runner/l4g/builds"
  cache_dir = "/home/gitlab-runner/l4g/cache"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
  [runners.custom]
    prepare_exec = "/opt/libvirt4gitlab-ci/prepare.sh"
    run_exec = "/opt/libvirt4gitlab-ci/run.sh"
    cleanup_exec = "/opt/libvirt4gitlab-ci/cleanup.sh"

[[runners]]
  name = "test-arm"
  url = "http://mygitlab"
  token = "xxxxxxxxxxxxxxx"
  executor = "shell"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]

Контрольный пример

Для проверки работоспособности произведем автоматизированную установку debian-11.4 и развертывание тестовых сред. В составе проекта идет уже готовый [.gitlab-ci.yml]

Установка ‘нулевого’ экземпляра ОС

На гипервизор выгрузить файл [debian-11.4.0-amd64-DVD-1.iso] (или похожий) и положить его [/var/lib/libvirt/images/]. Аналогично пакет с gitlab-runner положит в [/var/cache/gitlab-runner_amd64.deb].

Выполнить команду запуска установки:

ISO=/var/lib/libvirt/images/debian-11.4.0-amd64-DVD-1.iso VM_ID=d11 VM_BRIDGE=br0 ./os-install

Здесь ISO - путь до дистрибутива, VM_ID - имя будущего шаблона, VM_BRIDGE - имя разделяемого сетевого интерфейса. Задачу так-же можно выполнит запустив [os-install] из .gitlab-ci проекта. При выполнении задачи будут произведены следующие действия:

  • создан диск будушей вм в файле [/var/lib/libvirt/images/d11.qcow]
  • сгенерированы пароли для root и обычного пользователя (их можно задать через переменные принудительно)
  • определен тип дистрибутива (по имени тома iso через isoinfo -d -i ..iso). Если задана переменная OS_ID, автоопределение будет пропущено. Идентификатор будет записан в поле description вм в формате osid=.....
  • загружен набор параметров установки из файла [osid/$OS_ID] (параметры см. в [osid/README.md]
  • запущен expect сценарий, в данном случае [scripts/debian-11.4.exp]
    • запущен сценарий [backends/debian10]
      • запущен процесс консольного установки с указанного ISO командой [virt-install]
        • указанная команда создаст вм в которой консольный ввод-вывод дублируется в файл [/var/log/libvirt/qemu/d11.console.log]
        • serial вм замкнут на консоль, а инсталятор запущен в диалоговом режиме на этой консоли
    • сценарий expect произведет установку и перегрузит машину
    • осуществлен вход с правами root
      • открытый ключ [driver.conf]:ID_FILE_PUB будет прописан пользователю root шаблона
      • ядру прописаны параметры non-persistent network (именование интерфейсов в стиле eth0,eth1 итп что бы при клонировании вм не менялось имя интерфейса)
      • модифицирован [/etc/issue] для вывода на консольное приглашение адреса полученного виртуальной машиной (т.к. механизмы поставляемые libvirt работают не стабильно)
      • машина выключена
  • переподключен ISO (после установки он отключается инсталятором)
  • в конфиг вм добавлен spice-адаптер для работы графической консоли
  • создан снапшот installed

В итоге у нас получается виртуалка обладающая следующими свойствами:

  • при клонировании активный интерфейс всегда eth0 и получает адрес по dhcp
  • локальная консоль клонируется в лог-файл
  • адрес полученный интерфейсом eth0 выводится на консоль в формате IP: x.x.x.x
  • c ключом [gitlab-runner]:.ssh/id_ed25519 на нее можно зайти под root

Соответственно никто не мешает создать ее в ручную, т.к. операция не так что бы частая.

Финишная подготовка

Для бесшовного использования вм в гитлабе нужно иметь на ней установленный раннер (он не регистрируется как раннер и используется только при доставке реп и некоторых других операций). Для этого выполнить команду VM_ID=d11 ./os-template или задачу os-template из .gitlab-ci проекта.

В процессе подготовки будут выполнены следующие операции:

  • состояние вм сброшено до снапшота installed. Удален если есть снапшот templated.
  • определен OS_ID т.к. он требуется для выбора применяемой роли (параметр ROLES в [osid/name]. Определение либо из поля description заполненного во время [os-install] либо через переменную OS_ID
  • вм загружена и к ней применена ansible-роль [roles/TemplatesOS]
    • установлен пакет sudo
    • установлен пакет gitlab-runner
    • настроена локаль
    • ключ ID_FILE_PUB прописан пользователю gitlab-runner
    • пользователю gitlab-runner будет разрешен passwordless sudo
    • машина выключена
  • сделан снимок templated

таким образом, на выходе мы получаем машину у которой кроме прочего установлен раннер, разрешен вход не толко под root но и под gitlab-runner, а последний умееет sudo без пароля. Соответственно эти операции тоже можно произвести в ручную.

Локальные роли

Для упрощения кастомизации локальных установок [os-template] использует следующие переменные окружения:

  • LOCALROLES - путь в файловой системе гипервизора со структурой аналогичной [roles/];
  • LOCALROLESPRE - путь с ролями выполняемыми до всех остальных, безотносительно osid;
  • LOCALROLESPOST - путь с ролями выполняемыми после всех остальных, безотносительно osid.

В конце каждой применяемой роли необходимо производить выключение машины которая будет запущена заново перед выполнением следующей роли.

Работа с производными машинами

Для использования полученных образов необходимо в выбранный проект добавит задачи с тегом l4g и секцией script которые будут выполнятся на автосозданных виртуалках. В примере [.gitlab-ci.yml] проекта есть две задачи testvm и testvm_predeploy. Для выполнения нужно создать конвеер с VM_SOURCE=d11 и DEPLOY=1. При запуске происходит следующее:

  • выполняется сценарий prepare_exec который
    • клонирует диск машины указанный в VM_SOURCE или в переменной [driver.conf]:VM_SOURCE_DEFAULT. Если не взведена переменная VM_FULLCOPY новый диск создается с backend шаблона (режим copy-on-write). Если переменная задана, то происходит полное копирование.
    • если задан параметр VM_DISK_SIZE то образ ресайзится до указанного размера командой qemu-img resize (ресайз фс в контексте вм нужно делать самостоятельно)
    • вычисляются параметры VM_VCPU/VM_MEM/VM_BRIDGE (берутся из переменных задачи или дефолтный из [driver.conf]
    • вычисляется VM_ID (берется из переменной VM_TARGET задачи или генерится автоматически в виде pipe-X1-runner-X2-project-X3-job-X4)
    • запускается virt-install в режиме импорта. Ожидается появление ip-адреса интерфейса в консольном логе машины.
  • выполняется run_exec
    • проверяет наличие PREDEPLOY_SCRIPT
      • если в наличии, выполняет его на целевой машине с правами рута (внимание! обработка ошибок не производится, а переменные окружения gitlab не доступны!)
      • если задана переменная VM_REBOOT то машина перегружается
    • выполняет команды из секции script с правами gitlab-runner
  • выполняет cleanup_exec
    • выполняет удаление вм

Отличие задачи c переменной PREDEPLOY_SCRIPT заключается в том, что на виртуалке предварительно выполняется сценарий указанный в переменной и виртуалка перегружается, и только после этого передается управление секции script основного блока.

Примеры развертывания применяемые при тестировании проекта доступны в каталоге tests/.

Описание переменных

  • VM_SAVE - состояние машины после окончания задачи. Может быть online - оставлена включенной или offline- оставлена выклбченной. Если переменная не указана, машина будет удалена.
  • VM_SOURCE - имя вм с которой будет осуществлено клонирование для выполнения текущей задачи. Это может быть как вм созданная на постоянной основе (шаблон), так и другая вм созданная на предыдущем этапе и оставленная при обработке переменной VM_SAVE=poweroff
  • VM_TARGET - имя, которое будет назначено создаваемой вм. Используется для создания вм с известным именем, например для использования в качестве шаблона на следующих этапах
  • VM_FORCE - перезатировать вм если существует
  • VM_DISK_FULLCOPY - клонирование вм по-умолчанию осуществляется в режиме backing store. Указание данной переменной, отключает использование диска машины-источника и создает полную копию диска
  • PREDEPLOY_SCRIPT - выполнения командного файла на этапе подготовки вм, до передачи управления секции script. Выполнение сценария осуществляется под root, при этом проект доступен по пути /home/gitlab-runner/builds/{секция проекта}/{имя проекта}
  • VM_REBOOT - машина будет перегружена после выполнения predeploy сценария и до выполнения секции script. При этом в журнал задачи будет выгружен вывод загрузки ядра (снимается с файла в которое перенаправлена консоль vm)
  • VM_VCPU - число виртуальных процессоров (всего на макете их 24)
  • VM_MEM - память в мегабайтах, на макете доступно 128Гб
  • VM_DISK_SIZE - объем диска (напр. “64G”).
  • VM_BRIDGE - сетевой мост на который будет подсажен сетевой интерфейс. По-умолчанию задается в [driver.conf]
  • VM_NETWORK - наименование изолированной сети libvirt которая будет добавлена к создаваемой вм. Если сети не существует, она будет создана на базе шаблона examples/isolated-net.xml.
  • DEBUG - отладочный вывод сценариев раннера (добавлен для cleanup, который штатно в журналы гитлаба не выводится. с DEBUG сбрасывается set -x в [/tmp/$$.clean.log]).
  • VM_INTERACT - запуск инсталятора в интерактивном режиме (для подготовки новых expect-сценариев)
  • TEMPLATE_DISK_SIZE - размер диска при установке ОС (os-install).

Безопасность

Следует учитывать, что разработка раннера не предполагала противодействия внутреннему нарушителю, т.к. беспарольный sudo и отсутствие контроля управляющих переменных позволяют разработчикам конвеера получить полный контроль как над производными вм так и над гипервизором. Защита от внешнего нарушителя в рамках базовой установки + автогенерируемые пароли которые в процессе работы не используются и хранятся только в логах соответствующих задач.

Особые указания

AstraLinux 1.7

Для автоматической установки iso перепаковывается и укладывается рядом с исходным и расширением .repack, установка производится с него. Ядро 5.10. Пароль от grub ‘astralinux’.

AstraLinux 1.6

После установки инсталятор не прописывает в параметры ядра console=serial, как следствие, на консоли ничего не доступно. Нужно дополнять диалог редактированием загрузчика.

Ubuntu

Установка через переупаковку iso с cloud-init и без сети, т.к. иначе долго думает на обновлениях.

AltLinux 10.1

Автоустановка через механизм autoinstall с модификацией iso, т.к. режим text для инсталятора сломан и на него забили.

VM_SAVE

Управление выходным состоянием вм бывает необходимо когда вм преобразуется уже после состояния templated (например мы создаем вм с VM_TARGET=newtpl,VM_SAVE=offline, а на каком либо из следующих шагов делаем машины уже с VM_SOURCE=newtpl. Вариант с VM_SAVE=online может применятся как для развертывания постоянных машин так и для тестирования сложных комплектов, когда вм взаимодействуют друг с другом.

LOCALSCRIPTSPRE

Сценарий [os-install] кроме прочего проверяет наличие переменной LOCALSCRIPTSPRE, задающей каталог с исполняемыми файлами выполняемыми до всех операций установки. Это может понадобится для предварительной выгрузки в хранилище самой свежей версии iso или еще каких либо подготовительных действий.

Описание

libvirt executor for gitlab ci/cd integration

Конвейеры
0 успешных
0 с ошибкой