Kvm перенос виртуальной машины на другой сервер
Теперь мы имеем все необходимое что бы побаловать себя живой миграцией. Я не буду тут описывать как создается и настраивается виртуальная машина. Лучше сразу предположим, что она у нас есть. Пусть это будет Debian 6.0.1a, размещенный на Logical Volume с именем «debian», соответственно путь до данного раздела у нас /dev/vg/debian, хотя это итак понятно. Итак на vm1 у нас виртуальная машина с именем «debian6» и мы ее сейчас будет мигрировать. Живая миграция требовательна к нюансам. Окружение вирутальной машины должно полностью совпадать у источника и приемника данной машины. Например, если виртуалкой используется раздел /dev/vg/debian, но на целевой системе этот раздел должен присутствовать. Если к машине подключены ISO образы, то и на целевой машине они так же должны быть, и по тому же самому пути, а лучше ISO образы вообще отключить на время миграции. Тоже самое и с сетевыми настройками: названия бриджа в который подключена виртуалка должны совпадать на источнике и приемнике. Вообщем капризная эта KVM, но если вы хотите живую миграцию — будьте так любезны. Допустим мы отключили все ISO и бридж приемника у нас имеет тоже самое название, теперь сделаем так, что бы root одной машины мог безприпятственно заходить по SSH в качестве root другой машины. Это вообщем то не обязательно, тем не менее желательно. По умолчанию пароль root в Ubuntu отсутствует, поэтому будем использовать ключи SSH. Для этого делаем следующее.
Обращаю пристальное внимание на то, что команды выполняются НА РАЗНЫХ машинах vm1 и vm2, если объяснить по простому, то мы просто генерируем SSH RSA ключ для пользователя root на машине vm1, после чего инсталируем его пользователю «user» машины vm2, а дальше на машине vm2 переносим последний добавленный ключ пользователя user, пользователю root. После этой процедуры пользователь root с vm1 будет входить по SSH как root@vm2 без запроса пароля. Такую же операцию проделываем и в обратную сторону. Теперь смотрим на нашу запущенную виртуалку на vm1
Значит машина запущена и работает, создаем на vm2 раздел того же размера что ни на vm1 и называем его так же, тоесть «debian», пусть у нас образ будет 8 Gb, на обеих хостах vm1 и vm2. Важно что бы раздел в который мигрирует виртаульная машина не был МЕНЬШЕ исходного.
После чего можно начать миграцию
Сразу скажу что переносимая виртуалка в процессе миграции резко теряет свою отзывчивость, и сеть между двумя хостами серьезно загружается. Так что имейте это в виду. Данная команда говорит о том что необходимо мигрировать, причем в живую (ключь —live), виртуалку с именем Debian6, и скопировать хранилище на удаленную машину (—copy-storage-all), если хранилище уже есть на хосте и достаточно свежо, то вместо копирования всего раздела, можно указать команду (—copy-storage-inc) и копирование будет инкиментальное, тоесть будет передана только измененная часть хранилища, что может существенно сэкономить время. Очень важно, так же не забыть ключь —live, потому как без него, система будет приостановлена, и запущена после миграции на другой системе. Вот собственно и вся наука.
Разбор полетов
В чем я увидел отличия, так это в том, что в первом случае размер диска vm2 сильно отличался от размера vm3:
Судя по всему, virt-clone при создании клона копирует только реально занятое виртуальной машиной место. Размер vm2.img лишь чуть-чуть больше, чем показания "df -h", выполненной в vm1, vm2 или vm3 (т.к. между клонированиями я ничего с гостями не делал, то и размер их не менялся относительно "предка" vm1).
Вообще, когда вы с чем-то сталкиваетесь впервые, старайтесь анализировать изменения после каждого шага. Сделали "sed -i /uuid/d /tmp/vm3-template.xml" - посмотрели. Сравнили с оригиналом. Потом склонировали vm2, на всякий случай просмотрите директорию, где у вас хранятся диски гостей. Создали новую сеть - поинтересуйтесь, был ли создан файл настроек сети в /etc/libvirt/qemu/networks. Ну и в таком роде.
Где бы вы что-то ни увидели, старайтесь все, что вам незнакомо, проверять до выполнения в консоли. Увидели в этой статье пример команды virt-clone, не поленитесь, сходите в интернет и поищите, кто еще так предлагает, что пишет man.
Старайтесь все ваши действия документировать, записывать, комментировать и тогда вы получите свой собственный how-to и будете его использовать, когда надо будет через год создать еще парочку гостей. Если вы не создаете виртуальные машины регулярно, то будет нормально, если вы забудете синтаксис, а history может быть очищена или заполнена таким количеством всяких команд, да еще и с очепятками, что вам опять придется разбираться по-новой.
Почтовый сервер Шифрование Squid 3proxy VPN Mikrotik Настройка сервера Виртуальные машины kvm Защита почты Резервное копирование Групповые политики SELinux WDS IPFW OpenVPN firewalld systemd Mobile libvirt Samba WiFi Iptables NAT Lightsquid Remote desktop Postfix Dovecot Удаление данных Софт Безопасность Настройка прокси Winbox User agent Хостинг Передача данных Онлайн сервисы Privacy LetsEncrypt VPN сервер RRDTool sendmail Rsync Linux SSH Система Windows Синхронизация Облако fail2ban FreeBSD
Резервное копирование виртуальных машин KVM без остановки
Есть хост CentOS 7, KVM, гость vm1 (Windows 7 x64, но что Windows, что Linux, отличаться по сути будет только установка драйвера внутри гостевой vm). Пока виртуальная машина работает, копировать ее диск - идея неправильная. Непонятно, что будет скопировано и непонятно, восстановится ли потом. Идея live backup (т.е. backup без остановки) состоит в следующем - делаем снапшот виртуальной машины, при этом чтение/запись начинает выполняться в этот снапшот. Сразу после создания снапшота исходный диск виртуальной машины уже ничем не занят - все новое копируется в файл снапшота. Можно спокойно скопировать этот диск. Это займет какое-то время, в течении которого vm работает, что-то записывается, удаляется. После того, как мы спокойно скопируем диск, нужно будет дать команду все из снапшота залить в основной диск vm. Снапшот нам нужен был только как временная мера.
Снепшот -> бекап диска -> объединение снепшота с бекапом диска:
Бекап диска vm1:
где vda - это устройство (сокет системного диска) vm1, на котором установлена гостевая OS:
Т.е. в работающей vm1 файл диска - /vms/vm1.iso, устройство - vda.
Но прямо с первой команды мы получим ошибку:
error: argument unsupported: QEMU guest agent is not configured
Для того, чтобы гостевую виртуальную машину можно было бекапить без ее остановки, нужно сделать несколько шагов:
- указать в конфиге vm агента QEMU
- в гостевой vm установить службу агента QEMU
- убедиться, что хост и агент взаимодействуют.
- делать бекапы.
Установка драйверов агента QEMU
. в гостевой виртуальной машине CentOS
. в гостевой виртуальной машине Debian/Ubuntu
. в гостевой виртуальной машине Windows (VirtIO Serial driver)
Установить virtio-win драйверы в гостевую vm:
Внутри гостевой виртуальной машины:
1) Установите CD-Drive:\guest-agent\qemu-ga-x64 (или x86).
2) В диспетчере устройств обновите драйвер внутри vm и перезагрузите ее (перезагрузка может и не потребоваться, но надо будет запустить службы QEMU Guest Agent и QEMU Guest Agent VSS Provider):
1-й вариант: утилита virt-clone:
Clone 'vm2' created successfully.
Т.к. команду мы выполняли от root, то virt-clone создала клон диска vm1.img с именем vm2.img с владельцем root:root, а не qemu:qemu. На скорость не влияет. Главное, убедиться, что диск создан:
Обратите внимание на domain-vm1 в тексте ошибки. Исправляем это (ищем domain-vm1 и заменяем на domain-vm2):
Добавить комментарий Отменить ответ
Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.
В данном небольшом how-to хотел бы поделиться с вами своим опытом использования утилиты lvmsync.
Данная утилита позволяет решить задачу переноса виртуальной машины с одного сервера KVM на другой, с минимальным простоем виртуальной машины, без использования общего хранилища (non-shared storadge).
Передавать мы будем весь раздел LVM, на который установлена виртуальная машина. Ну а уменьшить время простоя нам поможет магия работы LVM snapshot, информацию о которой вы с легкостью можете найти в интернете.
Вот как выглядит перенос виртуальной машины в кратком виде:
- Делаем снимок LVM раздела.
- Передаем основной LVM раздел по сети, не останавливая нашу VM.
- Когда закончится передача основного раздела, останавливаем VM.
- Запускаем lvmsync для передачи снимка по сети. Передается не весь снимок, а только измененные блоки.
- Подготавливаем и запускаем VM на новом сервере.
Подробнее о работе lvmsync, и дополнительных плюшках вы можете почитать на страничке проекта.
Далее предполагается что у нас есть права sudo в системе, ssh доступ настроен по ключам, а вход под рутом запрещен.
Приступим к переносу VM:
Установка:
Для работы lvmsync нам потребуется Ruby 1.8 (or later), ssh, и dmsetup.
Скачиваем lvmsync на локальный компьютер:
Копируем lvmsync в root PATH, например в /usr/bin/
Подготовка удаленного сервера (server2):
1) Скачиваем и устанавливаем lvmsync.
2) Создаем LVM раздел для копируемой VM.
Размер раздела должен быть равен исходному разделу (в принципе он может быть и больше исходного, но этот вариант мной не тестировался).
Подготовка локального сервера и перенос VM.
Далее все команды необходимо выполнять на сервере, с которого мы хотим переместить виртуальную машину (server1).
1) Создаем definition xml:
2) Делаем снимок раздела:
Обратите внимание, что размер снимка должен выбираться в зависимости от интенсивности использования VM. Т.к. все данные, пока мы переносим основной раздел, будут «сохраняться» на снимок.
И при полном заполнении снимка, он автоматически деактивируется.
3) Не останавливая VM, переносим основной раздел с помощью dd:
Здесь добавлено сжатие передаваемых данных гзипом, и отображение хода передачи данных с помощью pv.
4) Когда перенос будет закончен, останавливаем виртуальную машину:
5) И после полной остановки машины запускаем lvmsync для переноса снимка:
Эта операция не только перенесет снимок на новый сервер, но и смержит его сразу с основным разделом LVM.
6) Копируем definition xml на удаленный сервер:
Подготовка и запуск виртуальной машины на новом сервере:
1) Изменяем definition xml, если необходимо.
2) Создаем виртуальную машину на основе xml:
3) Запускаем нашу виртуальную машину, и прописываем ее в автостарт:
Вот и все, перенос виртуальной машины закончен!
Примечания
Утилита тестировалась на Centos 6.4. Про временя переноса сказать что-либо затрудняюсь, т.к. все зависит от интенсивности работы с виртуальной машиной во время ее переноса, и, соответственно, размера снапшота.
Продолжаем серию статей о виртуализации на базе KVM. В предыдущих статьях было рассказано об инструментарии, о настройке хост-машины и создании виртуальной машины. Сегодня мы поговорим о создании образа виртуальной машины и его клонировании.
Исследования вопроса дали удручающие результаты: информацию по созданию образов виртуальных машин в сети очень сложно найти, а та, что есть, качеством и полнотой не отличается.
Для получения образа виртуальной машины в минимальной системе в ней достаточно поменять всего пару файлов чтобы получить нормально работающую систему, но в случае Debian появляются небольшие сложности.
Для создания новой виртуальной машины на основе имеющейся системы нужно внести следующие изменения:
Большой находкой для меня оказалась библиотека libguestfs — она позволяет управлять дисками и оперировать файлами виртуальных машин как в интерактивном режиме, так и по заранее составленному сценарию.
Исследуем образ
Итак, приступим к работе.
Основным инструментом, с помощью которого мы будем работать с образом гостевой системы, является guestfish.
Попробуем произвести некоторые операции в интерактивном режиме:
Что очень здорово — все необходимые операции можно производить и в неинтерактивном режиме (по заранее составленному сценарию). Приведу пример скрипта, который редактирует файлы hosts, hostname и interfaces в системе:
$ guestfish add-drive debian_guest.img
run
mount-vfs rw ext3 /dev/vda1 /
upload - 127.0.0.1 localhost.localdomain localhost debian_guest.local debian_guest
10.10.10.100 debian_guest.local
END
upload - nameserver 8.8.8.8
END
upload - debian_guest.local
END
upload - auto lo
iface lo inet loopback
allow-hotplug eth0
iface eth0 inet static
address 10.10.10.100
gateway 10.10.10.10
netmask 255.255.255.0
network 10.10.10.0
broadcast 10.10.10.255
END
EOF
Использование heredoc оказалось очень удобным в данном контексте.
Secure Hell
Как видно из названия, я с этим вопросом достаточно долго промучился: в Debian/Ubuntu автоматической регенерации ключей при их удалении попросту нет. В других системах, которые я пробовал использовать, с этим всё в порядке, а для deb-based операционных систем с этим проблемы.
Я сделал вот так:
$ guestfish
> add-drive debian_guest.img
> run
> mount-vfs rw ext3 /dev/vda1 /
> download /etc/init.d/ssh /home/username/debian_5_etc_init_ssh
Далее были сделаны следующие изменения:
--- /home/username/debian_5_etc_init_ssh 2012-12-21 00:00:00.000000000 +0000
+++ /home/username/debian_5_etc_init_ssh_fixed 2012-12-21 00:00:00.000000000 +0000
@@ -32,6 +32,10 @@
([ "$previous" ] && [ "$runlevel" ]) || [ "$runlevel" = S ]
>
case "$1" in
start)
+ check_ssh_host_key
check_privsep_dir
check_for_no_start
check_dev_null
@@ -106,6 +111,7 @@
;;
restart)
+ check_ssh_host_key
check_privsep_dir
check_config
log_daemon_msg "Restarting OpenBSD Secure Shell server" "sshd"
Внимание, патч нерабочий, он приведён как пример необходимых изменений.
И для двух версий Debian/Ubuntu я сделал аналогичный файл с уже изменённым файлом ssh. Далее его можно просто загрузить в виртуальную машину.
> upload /home/username/debian_5_etc_init_ssh_fixed /etc/init.d/ssh
А теперь удалим ключи, чтобы они сгенерировались автоматически:
> glob rm /etc/ssh_host_*_key*
Удаление по маске не работает. Поскольку в API данный метод не реализован, префикс glob позволяет развернуть маску в список файлов.
Для FreeBSD и CentOS достаточно просто удалить ключи, при старте они сами сгенерируются.
Идентификация пользователей
Для начала стоит рассказать о том, как представлено хранение информации о пользователях в Linux/FreeBSD. Это будет немного занудно, но необходимо для понимания того, что мы всё-таки делаем. Хотя по минимуму достаточно информации только о shadow-файле.
Вся необходимая для аутентификации пользователей хранится в файлах /etc/passwd и /etc/shadow(/etc/master.passwd в FreeBSD).
Рассмотрим структуру файла /etc/passwd
Процитирую из вики порядок использования полей:
- регистрационное имя или логин
- хеш пароля (сейчас не используется, используется скрытый в shadow пароль)
- идентификатор пользователя
- идентификатор группы по умолчанию
- информационное поле GECOS
- начальный (он же домашний) каталог
- регистрационная оболочка, или shell
Рассмотрим структуру /etc/shadow
- имя пользователя
- хэш пароля
- дата последнего изменения пароля
- через сколько дней можно будет поменять пароль
- через сколько дней пароль устареет
- за сколько дней до того, как пароль устареет, начать напоминать о необходимости смены пароля
- через сколько дней после того, как пароль устареет, заблокировать учётную запись пользователя
- дата, при достижении которой учётная запись блокируется
- зарезервированное поле
Нам нужно изменить конкретно второе поле (хэш пароля). Его можно разбить на три части:
- 1 — тип шифрования md5, 2 — SHA512 (поправьте меня если я не прав)
- APv1HQOB — соль, через которую шифруется пароль
- HJQhYFq9JSnhusQ.1Ql10. — непосредственно хэш пароля с солью.
Хэш генерируется командой:
$ mkpasswd --method=md5 --salt="APv1HQOB" "$password"
$1$APv1HQOB$HJQhYFq9JSnhusQ.1Ql10.
Его нам и нужно подставить в файл /etc/shadow.
Я написал небольшой скрипт, который будет генерировать случайный пароль и соль длиной 8 символов, выводить его, генерировать хэш и подставлять его в нужный файл:
pwhash() salt=$1
password=$2
hash=`mkpasswd --method=md5 --salt=$salt $password`
echo $hash
>
guestfish add-drive debian_guest.img
run
mount-vfs rw ext3 /dev/vda1 /
download /etc/shadow $tempfile
! sed 's/^root:[^:]\+:/root:$hash_esc:/' $tempfile > $tempfile.new
upload $tempfile.new $shadow
EOF
Как вы наверняка заметили, мы использовали внешнюю команду внутри скрипта, в которой мы заменили содержимое первой секции на полученный в скрипте хэш. Для этого используется внешний оператор "! ": он очень удобен, когда нам нужно сделать какие-то небольшие операции, не прерывая процесс работы с guestfish (поскольку на запуск guestfish всё таки требуется некоторое время).
Подготовка мастер-образа
Поскольку образы требуется периодически обновлять (в случае выхода важных обновлений или исправления ошибки в образе), нам следует подготовить мастер-образы, в которых мы будет производить необходимые манипуляции. Для разворачивания мы будем готовить эти образы при помощи отдельного скрипта.
Что нам нужно убрать в нашем образе:
- Очистить логи
- Удалить следы пребывания в системе
- Удалить скачанные пакеты (актуально для Debian и Ubuntu, только они мусорят)
- Удалить файл с настройками сетевой карты
- Удалить ключи.
После этого нам нужно будет уменьшить размер файловой системы, уменьшить раздел и отрезать от образа лишнее.
Приложу небольшой участок кода, который выполняет первую часть необходимого действия:
Флаг "-" перед командой означает, что мы не должны выходить, если какая-то из команд вернёт -1. Это сделано специально, чтобы отсутствие каких-либо файлов не прерывало выполнение остальных команд; таким образом, кастомизация данного скрипта для различных дистрибутивов становится не нужной, хотя она и возможна.
А теперь приступим к уменьшению образа:
$ guestfish add-drive add-drive $/$_$_$.img
run
e2fsck-f /dev/vda1
resize2fs-M /dev/vda1
tune2fs /dev/vda1 | grep "Block count:" | sed -e 's/Block\ count:\ //g' -e 's/$/*4+2144/g' | bc > /tmp/block_count
EOF
$ foo=`cat /tmp/block_count`
$ guestfish allocate debian_guest_minimal.img $k
EOF
Цифра 2144 — это размер загрузчика и таблицы разделов.
Вкратце суть проделанного в следующем: мы ужимаем файловую систему до минимального размера, вычисляем, сколько она стала занимать (минимальное количество блоков), и умножаем их на 4, поскольку размер блока 4 кбайта, после чего создаём образ полученной величины.
После этого нам необходимо будет воспользоваться утилитой virt-resize из комплекта утилит libguestfs, чтобы перенести получившуюся файловую систему в новый, меньший образ.
$ virt-resize --shrink /dev/vda1 debian_guestl.img debian_guest_minimal.img
Следует сразу обговорить ограничения данного метода: это применимо только для файловых систем ext2-4, поскольку resize2fs работает только с ними. Для чего-то нестандартного можно легко допилить нужный функционал (правда, как я уже упоминал ранее, libguestfs очень сложно собрать). Для образца можно посмотреть мой патч для реализации resize2fs-M.
К сожалению с FreeBSD всё сильно сложнее, и пока нет никаких вариантов решения проблемы с ней кроме добавления в конфиг виртуальной машины ещё одного диска и его монтирования.
Теперь же мы должны, по желанию, конечно, упаковать получившийся образ при помощи xz (это долго, но результат стоит того):
$ xz -9 debian_guest.img
$ ls -lsha debian_guest.img.xz
107M -rw-r--r-- 1 username username 107M Dec 21 00:00 debian_guest.img.xz
Разворачивание образа
Итак, образ виртуальной машины мы получили, но образы — это не готовые рабочие системы. Чтобы получить рабочую систему, нам нужно произвести несколько операций:
- Аллоцировать образ на диск
- Скопировать загрузчик и таблицу разделов
- Перенести информацию из шаблона в образ виртуальной машины
- Расширить файловую систему
- Сменить пароль root
- прописать сетевые настройки
Для Linux всё элементарно: в составе libguestfs есть замечательная утилита, написанная на OCaml — virt-resize, пункты с 2 по 4 выполняются ею без проблем.
По ряду причин на '''guestfish''' реализовать изменение размера диска невозможно (копирование mbr в guestfish невозможно), посему нужно использовать более функциональные средства.
$ guestfish allocate debian_guest_clone.img 10G
EOF
$ virt-resize --expand /dev/vda1 debian_guest.img debian_guest_clone.img
Собственно, это все, что минимально требуется знать для осуществления клонирования образов виртуальных машин.
Клонирование виртуальных машин KVM
Смотрим список виртуальных машин:
Создадим копию vm1 и назовем ее vm2.
Для начала остановим оригинал:
или выключим его:
Дальше предлагаю рассмотреть два варианта:
2-й вариант (ручками)
Т.к. vm2 уже есть, создадим vm3 и сравним результаты.
Делаем дамп xml от vm1:
Удаляем UUID оригинала, libvirt добавит новые значения:
Переименовываем имена со старого на новое:
Создаем новую vm3:
Добавить комментарий Отменить ответ
Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.
В данном небольшом how-to хотел бы поделиться с вами своим опытом использования утилиты lvmsync.
Данная утилита позволяет решить задачу переноса виртуальной машины с одного сервера KVM на другой, с минимальным простоем виртуальной машины, без использования общего хранилища (non-shared storadge).
Передавать мы будем весь раздел LVM, на который установлена виртуальная машина. Ну а уменьшить время простоя нам поможет магия работы LVM snapshot, информацию о которой вы с легкостью можете найти в интернете.
Вот как выглядит перенос виртуальной машины в кратком виде:
- Делаем снимок LVM раздела.
- Передаем основной LVM раздел по сети, не останавливая нашу VM.
- Когда закончится передача основного раздела, останавливаем VM.
- Запускаем lvmsync для передачи снимка по сети. Передается не весь снимок, а только измененные блоки.
- Подготавливаем и запускаем VM на новом сервере.
Подробнее о работе lvmsync, и дополнительных плюшках вы можете почитать на страничке проекта.
Далее предполагается что у нас есть права sudo в системе, ssh доступ настроен по ключам, а вход под рутом запрещен.
Приступим к переносу VM:
Установка:
Для работы lvmsync нам потребуется Ruby 1.8 (or later), ssh, и dmsetup.
Скачиваем lvmsync на локальный компьютер:
Копируем lvmsync в root PATH, например в /usr/bin/
Подготовка удаленного сервера (server2):
1) Скачиваем и устанавливаем lvmsync.
2) Создаем LVM раздел для копируемой VM.
Размер раздела должен быть равен исходному разделу (в принципе он может быть и больше исходного, но этот вариант мной не тестировался).
Подготовка локального сервера и перенос VM.
Далее все команды необходимо выполнять на сервере, с которого мы хотим переместить виртуальную машину (server1).
1) Создаем definition xml:
2) Делаем снимок раздела:
Обратите внимание, что размер снимка должен выбираться в зависимости от интенсивности использования VM. Т.к. все данные, пока мы переносим основной раздел, будут «сохраняться» на снимок.
И при полном заполнении снимка, он автоматически деактивируется.
3) Не останавливая VM, переносим основной раздел с помощью dd:
Здесь добавлено сжатие передаваемых данных гзипом, и отображение хода передачи данных с помощью pv.
4) Когда перенос будет закончен, останавливаем виртуальную машину:
5) И после полной остановки машины запускаем lvmsync для переноса снимка:
Эта операция не только перенесет снимок на новый сервер, но и смержит его сразу с основным разделом LVM.
6) Копируем definition xml на удаленный сервер:
Подготовка и запуск виртуальной машины на новом сервере:
1) Изменяем definition xml, если необходимо.
2) Создаем виртуальную машину на основе xml:
3) Запускаем нашу виртуальную машину, и прописываем ее в автостарт:
Вот и все, перенос виртуальной машины закончен!
Примечания
Утилита тестировалась на Centos 6.4. Про временя переноса сказать что-либо затрудняюсь, т.к. все зависит от интенсивности работы с виртуальной машиной во время ее переноса, и, соответственно, размера снапшота.
Продолжаем серию статей о виртуализации на базе KVM. В предыдущих статьях было рассказано об инструментарии, о настройке хост-машины и создании виртуальной машины. Сегодня мы поговорим о создании образа виртуальной машины и его клонировании.
Исследования вопроса дали удручающие результаты: информацию по созданию образов виртуальных машин в сети очень сложно найти, а та, что есть, качеством и полнотой не отличается.
Для получения образа виртуальной машины в минимальной системе в ней достаточно поменять всего пару файлов чтобы получить нормально работающую систему, но в случае Debian появляются небольшие сложности.
Для создания новой виртуальной машины на основе имеющейся системы нужно внести следующие изменения:
Большой находкой для меня оказалась библиотека libguestfs — она позволяет управлять дисками и оперировать файлами виртуальных машин как в интерактивном режиме, так и по заранее составленному сценарию.
Исследуем образ
Итак, приступим к работе.
Основным инструментом, с помощью которого мы будем работать с образом гостевой системы, является guestfish.
Попробуем произвести некоторые операции в интерактивном режиме:
Что очень здорово — все необходимые операции можно производить и в неинтерактивном режиме (по заранее составленному сценарию). Приведу пример скрипта, который редактирует файлы hosts, hostname и interfaces в системе:
$ guestfish add-drive debian_guest.img
run
mount-vfs rw ext3 /dev/vda1 /
upload - 127.0.0.1 localhost.localdomain localhost debian_guest.local debian_guest
10.10.10.100 debian_guest.local
END
upload - nameserver 8.8.8.8
END
upload - debian_guest.local
END
upload - auto lo
iface lo inet loopback
allow-hotplug eth0
iface eth0 inet static
address 10.10.10.100
gateway 10.10.10.10
netmask 255.255.255.0
network 10.10.10.0
broadcast 10.10.10.255
END
EOF
Использование heredoc оказалось очень удобным в данном контексте.
Secure Hell
Как видно из названия, я с этим вопросом достаточно долго промучился: в Debian/Ubuntu автоматической регенерации ключей при их удалении попросту нет. В других системах, которые я пробовал использовать, с этим всё в порядке, а для deb-based операционных систем с этим проблемы.
Я сделал вот так:
$ guestfish
> add-drive debian_guest.img
> run
> mount-vfs rw ext3 /dev/vda1 /
> download /etc/init.d/ssh /home/username/debian_5_etc_init_ssh
Далее были сделаны следующие изменения:
--- /home/username/debian_5_etc_init_ssh 2012-12-21 00:00:00.000000000 +0000
+++ /home/username/debian_5_etc_init_ssh_fixed 2012-12-21 00:00:00.000000000 +0000
@@ -32,6 +32,10 @@
([ "$previous" ] && [ "$runlevel" ]) || [ "$runlevel" = S ]
>
case "$1" in
start)
+ check_ssh_host_key
check_privsep_dir
check_for_no_start
check_dev_null
@@ -106,6 +111,7 @@
;;
restart)
+ check_ssh_host_key
check_privsep_dir
check_config
log_daemon_msg "Restarting OpenBSD Secure Shell server" "sshd"
Внимание, патч нерабочий, он приведён как пример необходимых изменений.
И для двух версий Debian/Ubuntu я сделал аналогичный файл с уже изменённым файлом ssh. Далее его можно просто загрузить в виртуальную машину.
> upload /home/username/debian_5_etc_init_ssh_fixed /etc/init.d/ssh
А теперь удалим ключи, чтобы они сгенерировались автоматически:
> glob rm /etc/ssh_host_*_key*
Удаление по маске не работает. Поскольку в API данный метод не реализован, префикс glob позволяет развернуть маску в список файлов.
Для FreeBSD и CentOS достаточно просто удалить ключи, при старте они сами сгенерируются.
Идентификация пользователей
Для начала стоит рассказать о том, как представлено хранение информации о пользователях в Linux/FreeBSD. Это будет немного занудно, но необходимо для понимания того, что мы всё-таки делаем. Хотя по минимуму достаточно информации только о shadow-файле.
Вся необходимая для аутентификации пользователей хранится в файлах /etc/passwd и /etc/shadow(/etc/master.passwd в FreeBSD).
Рассмотрим структуру файла /etc/passwd
Процитирую из вики порядок использования полей:
- регистрационное имя или логин
- хеш пароля (сейчас не используется, используется скрытый в shadow пароль)
- идентификатор пользователя
- идентификатор группы по умолчанию
- информационное поле GECOS
- начальный (он же домашний) каталог
- регистрационная оболочка, или shell
Рассмотрим структуру /etc/shadow
- имя пользователя
- хэш пароля
- дата последнего изменения пароля
- через сколько дней можно будет поменять пароль
- через сколько дней пароль устареет
- за сколько дней до того, как пароль устареет, начать напоминать о необходимости смены пароля
- через сколько дней после того, как пароль устареет, заблокировать учётную запись пользователя
- дата, при достижении которой учётная запись блокируется
- зарезервированное поле
Нам нужно изменить конкретно второе поле (хэш пароля). Его можно разбить на три части:
- 1 — тип шифрования md5, 2 — SHA512 (поправьте меня если я не прав)
- APv1HQOB — соль, через которую шифруется пароль
- HJQhYFq9JSnhusQ.1Ql10. — непосредственно хэш пароля с солью.
Хэш генерируется командой:
$ mkpasswd --method=md5 --salt="APv1HQOB" "$password"
$1$APv1HQOB$HJQhYFq9JSnhusQ.1Ql10.
Его нам и нужно подставить в файл /etc/shadow.
Я написал небольшой скрипт, который будет генерировать случайный пароль и соль длиной 8 символов, выводить его, генерировать хэш и подставлять его в нужный файл:
pwhash() salt=$1
password=$2
hash=`mkpasswd --method=md5 --salt=$salt $password`
echo $hash
>
guestfish add-drive debian_guest.img
run
mount-vfs rw ext3 /dev/vda1 /
download /etc/shadow $tempfile
! sed 's/^root:[^:]\+:/root:$hash_esc:/' $tempfile > $tempfile.new
upload $tempfile.new $shadow
EOF
Как вы наверняка заметили, мы использовали внешнюю команду внутри скрипта, в которой мы заменили содержимое первой секции на полученный в скрипте хэш. Для этого используется внешний оператор "! ": он очень удобен, когда нам нужно сделать какие-то небольшие операции, не прерывая процесс работы с guestfish (поскольку на запуск guestfish всё таки требуется некоторое время).
Подготовка мастер-образа
Поскольку образы требуется периодически обновлять (в случае выхода важных обновлений или исправления ошибки в образе), нам следует подготовить мастер-образы, в которых мы будет производить необходимые манипуляции. Для разворачивания мы будем готовить эти образы при помощи отдельного скрипта.
Что нам нужно убрать в нашем образе:
- Очистить логи
- Удалить следы пребывания в системе
- Удалить скачанные пакеты (актуально для Debian и Ubuntu, только они мусорят)
- Удалить файл с настройками сетевой карты
- Удалить ключи.
После этого нам нужно будет уменьшить размер файловой системы, уменьшить раздел и отрезать от образа лишнее.
Приложу небольшой участок кода, который выполняет первую часть необходимого действия:
Флаг "-" перед командой означает, что мы не должны выходить, если какая-то из команд вернёт -1. Это сделано специально, чтобы отсутствие каких-либо файлов не прерывало выполнение остальных команд; таким образом, кастомизация данного скрипта для различных дистрибутивов становится не нужной, хотя она и возможна.
А теперь приступим к уменьшению образа:
$ guestfish add-drive add-drive $/$_$_$.img
run
e2fsck-f /dev/vda1
resize2fs-M /dev/vda1
tune2fs /dev/vda1 | grep "Block count:" | sed -e 's/Block\ count:\ //g' -e 's/$/*4+2144/g' | bc > /tmp/block_count
EOF
$ foo=`cat /tmp/block_count`
$ guestfish allocate debian_guest_minimal.img $k
EOF
Цифра 2144 — это размер загрузчика и таблицы разделов.
Вкратце суть проделанного в следующем: мы ужимаем файловую систему до минимального размера, вычисляем, сколько она стала занимать (минимальное количество блоков), и умножаем их на 4, поскольку размер блока 4 кбайта, после чего создаём образ полученной величины.
После этого нам необходимо будет воспользоваться утилитой virt-resize из комплекта утилит libguestfs, чтобы перенести получившуюся файловую систему в новый, меньший образ.
$ virt-resize --shrink /dev/vda1 debian_guestl.img debian_guest_minimal.img
Следует сразу обговорить ограничения данного метода: это применимо только для файловых систем ext2-4, поскольку resize2fs работает только с ними. Для чего-то нестандартного можно легко допилить нужный функционал (правда, как я уже упоминал ранее, libguestfs очень сложно собрать). Для образца можно посмотреть мой патч для реализации resize2fs-M.
К сожалению с FreeBSD всё сильно сложнее, и пока нет никаких вариантов решения проблемы с ней кроме добавления в конфиг виртуальной машины ещё одного диска и его монтирования.
Теперь же мы должны, по желанию, конечно, упаковать получившийся образ при помощи xz (это долго, но результат стоит того):
$ xz -9 debian_guest.img
$ ls -lsha debian_guest.img.xz
107M -rw-r--r-- 1 username username 107M Dec 21 00:00 debian_guest.img.xz
Разворачивание образа
Итак, образ виртуальной машины мы получили, но образы — это не готовые рабочие системы. Чтобы получить рабочую систему, нам нужно произвести несколько операций:
- Аллоцировать образ на диск
- Скопировать загрузчик и таблицу разделов
- Перенести информацию из шаблона в образ виртуальной машины
- Расширить файловую систему
- Сменить пароль root
- прописать сетевые настройки
Для Linux всё элементарно: в составе libguestfs есть замечательная утилита, написанная на OCaml — virt-resize, пункты с 2 по 4 выполняются ею без проблем.
По ряду причин на '''guestfish''' реализовать изменение размера диска невозможно (копирование mbr в guestfish невозможно), посему нужно использовать более функциональные средства.
$ guestfish allocate debian_guest_clone.img 10G
EOF
$ virt-resize --expand /dev/vda1 debian_guest.img debian_guest_clone.img
Собственно, это все, что минимально требуется знать для осуществления клонирования образов виртуальных машин.
Клонирование виртуальных машин KVM
Смотрим список виртуальных машин:
Создадим копию vm1 и назовем ее vm2.
Для начала остановим оригинал:
или выключим его:
Дальше предлагаю рассмотреть два варианта:
QEMU guest agent is not connected
error: Guest agent is not responding: QEMU guest agent is not connected
Если сделать дамп состояния vm1, то видно, что агент не подключен (disconnected):
Это может быть из-за:
- ошибки запуска агента в гостевой vm1 (например, виртуальная машина не была перезагружена или не были запущены службы QEMU Guest Agent и QEMU Guest Agent VSS Provider);
- из-за включенного SELinux на хосте (попробуйте отключить "setenforce 0 " и посмотреть, будет connected или нет)
- или еще из-за чего-нибудь.
Исправляем и пробуем наконец-то сделать бекап виртуальной машины без ее остановки:
Слияние снапшота с копией
Теперь данные будут записываться в основной диск vm1, а в backup будет копия диска и конфиг, восстановиться из которых можно с помощью virsh create.
Почтовый сервер Шифрование Squid 3proxy VPN Mikrotik Настройка сервера Виртуальные машины kvm Защита почты Резервное копирование Групповые политики SELinux WDS IPFW OpenVPN firewalld systemd Mobile libvirt Samba WiFi Iptables NAT Lightsquid Remote desktop Postfix Dovecot Удаление данных Софт Безопасность Настройка прокси Winbox User agent Хостинг Передача данных Онлайн сервисы Privacy LetsEncrypt VPN сервер RRDTool sendmail Rsync Linux SSH Система Windows Синхронизация Облако fail2ban FreeBSD
Готовим виртуальную машину к установке агента QEMU
Способ первый (с выключением гостевой виртуальной машины).
В конфигурацию гостевой vm нужно добавить специальное устройство - агент QEMU.
В параметре source указывается имя сокета, которое должно быть уникальным в рамках хоста. Также надо отметить, что при включенном SELinux могут быть проблемы с соединением к этому сокету, из-за чего может возникать ошибка "QEMU guest agent is not connected".
Внимание! Выключить и включить гостевую виртуальную машину (перезагрузка может не помочь).
Второй способ (без выключения виртуальной машины):
Этот способ я нашел позднее и не проверял лично, но, тем не менее, он указан в руководстве Red Hat.
Готовим файл agent-win7.xml:
Применительно к CentOS/RedHat указывать уникальный путь bind не требуется, поэтому шаблон может быть одинаковым для разных vm. Итак, готовим файл agent.xml:
Читайте также: