Автор — Султан Усманов, специалист отдела DevOps компании Hostkey
У нас в компании Hostkey возникла необходимость в переводе серверов со старой инфраструктуры oVirt Engine версии 4.2 на новую инфраструктуру oVirt Engine версии 4.5. Эта миграция не просто смена минорного релиза — изменилось очень многое:
- появились новые фичи и доработки в API;
- был осуществлен переход на EL8.
Из-за изменения платформы мы решили также обновить оборудование хостов виртуализации и провести обновление через миграцию машин на новую инсталляцию. О том, как проходил этот процесс, рассказываем в этой статье.
Рассматривалось и было применено несколько вариантов миграции:
- Работа с Export Domain в oVirt.
- Создание автоматизации для переноса машин через экспорт/импорт OVA через oVirt API.
Первый вариант относительно прост в реализации, однако автоматизировать процесс переноса серверов невозможно. Второй требует чуть больше времени, но позволяет автоматизировать миграцию. Соответственно, все зависит от того, какое количество оборудования необходимо перенести.
Export Domain — это домен хранения. В интернете достаточно много ресурсов, посвященных работе Export Domain в oVirt. Он в основном используется для переноса виртуальных машин в разные центры обработки данных или среды oVirt, и только когда создаются резервные копии виртуальных машин (этот домен может использовать исключительно протокол NFS). Работает на основе NFS и только один Export Domain может быть добавлен в DataCenter. Для добавления Export Domain в инфраструктуру необходимо предварительно установить сервер и развернуть на нем службу NFS, а также выдать необходимые права на каталог, который указывается в разделе Export Path в настройках.
Процедура добавления Export Domain на oVirt Engine сервере, с которого планируем экспортировать
Пример добавления Export Domain в web-интерфейсе oVirt:
В случае успешного добавления будет показан статус Active:
Для экспорта виртуального сервера на Export Domain необходимо пройти в раздел Compute – Virtual Machines:
Далее необходимо подготовить сервер к экспорту: найти и выключить его. Пример отключенного сервера:
После в этом же окне с правой стороны, под иконкой пользователя, следует кликнуть на «бургер» и выбрать раздел Export to Export Domain:
Далее требуется указать Export Domain, куда экспортировать сервер, и дождаться выполнения экспорта.
Затем необходимо перейти в раздел Compute — Data Centers, выбрать Data Centers, куда мы прикрепляли наш Export Domain, выбрать его и перевести в Maintenance-мод.
1. Выбираем нужный нам Data Center и заходим в него:
2. Выбираем наш Export Domain и в правом верхнему углу кликаем на кнопку Maintenance:
3. На рисунке ниже можно увидеть статус Export Domain переведенного в режим Maintenance:
4. Перейдя в Maintenance mode, кликаем на Detach и указываем причину, по которой мы отключаем диск:
5. После отключения в разделе Storage — Storage Domains на нашем Export Domain мы должны увидеть значок, указывающий на то, что раздел отключен:
Затем необходимо перейти в oVirt Engine, куда мы планируем перенести наш виртуальный сервер, и проделать те же действия, но уже прикрепить Export Domain.
Процедура добавления Export Domain на oVirt Engine сервере, на который планируем импортировать
1. Выбираем нужный нам Data Center, куда планируем импортировать наш сервер, переходим в раздел в Compute – Data Centers и кликаем на нужный:
2. В открывшемся окне в разделе Storage кликаем на Attach Export и выбираем Export Domain, нажимаем ОК:
3. Через 5–15 секунд (статус и время выполнения можно посмотреть в меню Task) в разделе Compute — Data Centers проходим в наш Data Centers, затем — в раздел Storages. Там должен появиться пункт Export Domain:
4. Кликаем на него и переходим в раздел VM Import:
5. Выбираем нужный виртуальный сервер и с правой стороны кликаем на Import:
6. Откроется окно импорта виртуальных серверов, в котором необходимо выбрать:
- Target Cluster;
- CPU Profile, если он был настроен.
После выбираем нужный сервер и нажимаем на кнопку Import:
7. Переходим в раздел Compute — Virtual Machines и дожидаемся окончания импорта виртуального сервера. После вносим правки, если они необходимы, и запускаем сервер.
На этом процедура экспорта и импорта виртуального сервера средствами Export Domain завершена.
Написание скрипта и Playbook’а в Ansible для миграции через OVA.
Поскольку экспорт серверов проводился с oVirt Engine версии 4.2 на более новые версии oVirt Engine 4.4 и 4.5, работать только с Ansible Playbook было нельзя: у версии 4.2 нет поддержки модулей экспорта виртуальных серверов в формате OVA. Мы решили экспортировать виртуальные серверы со старой версии Engine средствами API. Для этого мы написали соответствующие скрипты. Для нового сервера импорт делался средствами Ansible Playbook и готовыми скриптами Python.
Ниже — примеры написания скрипта на Bash и вариант Ansible Playbook’а.
Bash-скрипт в качестве входных данных получает следующую информацию:
- Engine-источник и назначения;
- Guest_id (имя виртуального сервера);
- host_name_dst (сервер назначения).
Полученные данные помещаются в файл с переменными vars/var.yml, которые в дальнейшем будут использоваться Ansible Playbook’ом.
Для экспорта необходимо предварительно установить сервер и развернуть на нем службу NFS. Выдать необходимые права на каталог (мы прописали — rw,async,no_subtree_check,anonuid=36,anongid=36,all_squash). В дальнейшем будем монтировать его в раздел /mnt/nfs на серверах Engine, а также на физическом хосте, с которого планируется экспорт виртуального сервера.
#!/usr/bin/env bash
##Source
engine_fqdn_src=enginesrc.test.local
engine_api_src="https://${engine_fqdn_src}/ovirt-engine/api"
guest_id=$1
##Destination
engine_fqdn_dst=enginedst.test.local
engine_api_dst="https://${engine_fqdn_dst}/ovirt-engine/api"
host_name_dst=$2
##Common vars
engine_user=admin@internal
engine_pass=pass
export_path=/mnt/nfs/
OVIRT_SEARCH() {
local engine_api=$1
local api_target=$2
local search
if [[ ! -z $3 ]]&&[[ ! -z $4 ]];then
local search="?search=$3=$4"
fi
curl -ks --user "$engine_user:$engine_pass" \
-X GET -H 'Version: 4' -H 'Content-Type: application/JSON' \
-H 'Accept: application/JSON' "${engine_api}/${api_target}${search}" |\
jq -Mc
}
##Sourc
vm_data=$(OVIRT_SEARCH $engine_api_src vms name $guest_id)
disk_data=$(OVIRT_SEARCH $engine_api_src disks name $guest_id)
vm_id=$(echo $vm_data | jq -r '.vm[].id')
host_id=$(echo $vm_data | jq -r '.vm[].host.id')
host_name_src=$(OVIRT_SEARCH $engine_api_src hosts/$host_id | jq -r '.name')
##Destination
host_data_dst=$(OVIRT_SEARCH $engine_api_dst hosts name $host_name_dst)
host_address_dst=$(echo $host_data_dst | jq -r '.host[].address')
cluster_id_dst=$(echo $host_data_dst | jq -r '.host[].cluster.id' )
data_center_id_dst=$(OVIRT_SEARCH $engine_api_dst clusters/$cluster_id_dst | jq -r '.data_center.id')
data_domain_name_dst=$(OVIRT_SEARCH $engine_api_dst datacenters/$data_center_id_dst/storagedomains | jq -r '.storage_domain[].name')
cluster_name_dst=$(OVIRT_SEARCH $engine_api_dst clusters/$cluster_id_dst | jq -r '.name')
post_data="<action><host><name>${host_name_src}</name></host><directory>${export_path}</directory><filename>${guest_id}</filename></action>"
post_data_shutdown="<action/>"
post_data_vmname="<vm><name>${guest_id}-</name></vm>"
##Shutdown vm
curl -ks --user "$engine_user:$engine_pass" \
-X POST -H 'Version: 4' \
-H 'Content-Type: application/xml' -H 'Accept: application/xml' \
--data $post_data_shutdown \
${engine_api_src}/vms/${vm_id}/shutdown
##Change vm name
curl -ks --user "$engine_user:$engine_pass" \
-X PUT -H 'Version: 4' \
-H 'Content-Type: application/xml' -H 'Accept: application/xml' \
--data $post_data_vmname \
${engine_api_src}/vms/${vm_id}
##Export vm
curl -ks --user "$engine_user:$engine_pass" \
-X POST -H 'Version: 4' \
-H 'Content-Type: application/xml' -H 'Accept: application/xml' \
--data $post_data \
${engine_api_src}/vms/${vm_id}/export
## Put result in var/var.yml
cat << EOF > vars/var.yml
url_src: htps://$engine_src/ovirt-engine/api
url_dst: https://$engine_dst/ovirt-engine/api
#username:
#password:
insecure: true
## VM Parameters:
cluster_name: $cluster_name_dst
disk_id: $disk_id
vm_id: $vm_id
disk_format: "qcow2"
data_domain: $cluster_name_dst
vm_name: $guest_id
EOF
Ansible Playbook для импорта виртуального сервера
Ниже приведен пример Playbook’а, который забирает данные с файла vars/var.yml и подставляет их согласно переменным, указанным в файле. Цель Playbook’а — создать конфигурационный файл, который используется engine сервером для импорта OVA-файла. После запускается Python-скрипт (upload_ova_as_vm_or_template.py), который импортирует сервер со всеми настройками. Далее зачищается конфиг-файл и файл с образом системы. После проводится настройка графического интерфейса и выполняется запуск сервера.
- hosts: engine_dst
tasks:
— name: Obtain SSO token
ovirt_auth:
url: "{{ url_dst }}"
username: "{{ username }}"
password: "{{ password }}"
insecure: "{{ insecure }}"
— name: Wait for export to be finished
include_tasks: tasks/check_ova_export_status.yml
— name: Ovirt config file creation
include_tasks: tasks/ovirt_conf_creation.yml
tags:
— ovirt_config_creation
— name: Upload disk image
command: "python3 /usr/share/doc/python3-ovirt-engine-sdk4/examples/upload_ova_as_vm_or_template.py -c engine --sd-name {{ data_domain }} /mnt/nfs/{{ vm_name }} --cluster-name {{ cluster_name }}"
— name: Remove config
file:
path: "/root/.config/ovirt.conf"
state: absent
— name: Remove disk image from NFS
file:
path: /mnt/nfs/{{vm_name}}
state: absent
— name: Change VM Name
ovirt_vm:
auth: "{{ ovirt_auth }}"
id: "{{ vm_id }}"
name: "{{ vm_name }}"
— name: Set console configuration for both Spice and VNC
ovirt_vm:
auth: "{{ ovirt_auth }}"
name: "{{ vm_name }}"
graphical_console:
protocol:
- spice
- vnc
— name: Run VM
ovirt_vm:
auth: "{{ ovirt_auth }}"
cluster: "{{ cluster_name }}"
state: running
name: "{{ vm_name }}"
— name: Revoke SSo token
ovirt_auth:
state: absent
ovirt_auth: "{{ ovirt_auth }}"
В Playbook’e также используются Task’и. Ниже приведено их описание:
1) Задача, которая проверяет статус экспорта виртуального сервера — check_ova_export_status.yml.
- name: Check ova export status
shell: |
counter=7
while true;do
if [[ $(ls -l /mnt/nfs/ | grep -Pc '\b{{ vm_name }}$\b') -eq 1 ]];then
let "successes+=1"
else
successes=0
fi
if [[ $successes -eq $counter ]];then
break
fi
sleep 10
done
2) Задача, которая отвечает за создание конфигурационного файла и используется в дальнейшем штатным скриптом Python oVirt для импорта виртуального сервера. В рамках этой задачи создается директория и в нее помещается подготовленный конфигурационный файл из шаблона, который находится в templates.
- name: create config directory if absent
file:
path: "/root/.config/"
state: directory
- name: ovirt config creation
template:
src: ../templates/ovirt.conf.j2
dest: "/root/.config/ovirt.conf"
owner: root
group: root
mode: '0644'
Пример конфигурационного файла ovirt.conf.j2 , размещенного в templates:
# Example configuration file.
#
# Example scripts will read this configuration from:
#
# ~/.config/ovirt.conf.
#
# You can keep multiple configuration sections in this file.
#
# To use configuration "engine1" use:
#
# ./example_script.py --config engine1 ...
# This engine configuration section shows all available options.
[engine]
# oVirt engine API URL (required).
engine_url = https://{{ engine_fqdn_dst }}
# oVirt engine API username (required).
username = {{ username }}
# oVirt enigne API password. If not specified the example script will get the
# password from stdin (optional).
password = {{ password }}
# Verify server certificate and host name (optional, default yes).
secure = yes
# CA certificated for verifying for verifying server. No need to specify if the
# certificate was added to the host trust store (optional, default empty).
cafile = /etc/pki/ovirt-engine/ca.pem
# This engine configuration section uses only the required options, and disable
# server certificates verification.
#[engine2]
#engine_url = https://engine2
#username = admin@internal
#secure = no
Эту автоматизацию можно вынести в Jenkins или в похожий сервис для миграции руками младших специалистов, а также расширить дополнительными действиями по обновлению базы данных оборудования и т. д. Через Ansible легко организовать добавление и проверку NFS-mount, например, через autofs, сделав механизм миграции подходящим для большого числа хостов виртуализации, как в нашем случае. Если же вы не знакомы с Ansible и написание Playbook вызывает сложности, для миграции можно использовать распространенный инструмент — Export Domain.
Данные варианты использовались для экспорта и импорта серверов. Описанный выше подход позволил нам разработать и применить удобную схему для перевода серверов со старой инфраструктуры oVirt Engine версии 4.2 на новую — oVirt Engine версии 4.5. Время переноса каждого сервера зависит от объема жесткого диска. Например, сервер с 9 Гб переносится за 11 минут, в это время включены все дополнительные настройки, которые могут потребоваться для миграции.