Ориентировочно с 2000-го по 2005-й я много баловался со всеми попадающими под руки системами виртуализации. Кроме готовых решений вроде "Parallels", "Microsoft PC" и "VMware" меня в то время заинтересовали системы эмуляции "Bochs" и "QEmu", последняя из которых постепенно вышла на уровень, когда в ней можно было запускать неадаптированные для этого специально произвольные операционные системы. После дополнения "QEmu" модулем аппаратной виртуализации и покупки проекта компанией "RedHat" система стала называться KVM (Kernel-based Virtual Machine) и уже в 2010-м году я полностью отказался от всех остальных систем виртуализации на этапах тестирования, а в 2012-м перевёл около сотни физических и виртуальных серверов на эту платформу.
Так получилось, что активное использование "Qemu/KVM" для меня началось ещё до выхода на стабильный уровень таких библиотек управления, как "LibVirt" - потому до определённого момента управление виртуальными машинами было реализовано через простейшие BASH-скрипты, которые я здесь и привожу. Это не "продакшн"-решение! Это просто немного истории.
Так получилось, что активное использование "Qemu/KVM" для меня началось ещё до выхода на стабильный уровень таких библиотек управления, как "LibVirt" - потому до определённого момента управление виртуальными машинами было реализовано через простейшие BASH-скрипты, которые я здесь и привожу. Это не "продакшн"-решение! Это просто немного истории.
Первым делом проверяем, включена ли в процессоре и BIOS поддержка аппаратной виртуализации (обидно бывает, знаете ли, затеять перестройку на удалённой по сети машине и узнать о невозможности её завершить):
# egrep -c '(vmx|svm)' /proc/cpuinfo
Понятно, что если что-то насчиталось - то есть надежда на светлое будущее.
Особо обращаю внимание на то, что поддержки процессором инструкций аппаратной виртуализации маловато - как правило, следует дополнительно активировать возможность использовать таковые в BIOS компьютера (на моей практике в половине случаев поддержка виртуализации была отключена в BIOS).
Устанавливаем непосредственно средство виртуализации:
# aptitude install kvm
Не уловил точный момент, но вроде бы после "QEMU v2" пакет стал называться уже иначе:
# aptitude install qemu-kvm
Разработчики Qemu-KVM предусмотрели скрипт инициализации поддержки аппаратного ускорения при запуске системы; он нормально отрабатывает, важно только не пытаться стартовать виртуальные машины до момента инициирования модуля "kvm" - для этого достаточно установить соответствующую очерёдность скриптов "/etc/init.d/" в дальнейшем.
Используйте KSM (Kernel Samepage Merging), если процессоры несущего сервера достаточно производительны, что бы на оптимизацию использования областей памяти позволительно было бы оттянуть 5-10% ресурсов CPU. KSM в ядре Linux доступно с версии 2.6.32 и поддерживается KVM начиная с версии 0.12.
# uname -a
Linux 3.2.0-4-amd64 SMP
# kvm -version
В "Debian Wheeze":
QEMU version 1.1.2
В "Debian Jessie" уже следующий минорный релиз:
QEMU emulator version 2.1.2, Copyright (c) 2003-2008 Fabrice Bellard
Если комплекс системного и прикладного ПО удовлетворяет требованиям, то включаем KSM (неплохо бы ознакомится с документацией, но в целом подсистема работоспособна и полезна сразу после включения). В "Linux Debian Wheezy" для этого всё уже готово:
# echo 1 > /sys/kernel/mm/ksm/run
KVM будет использовать появившуюся возможности KSM как только таковая появится - то есть какой-то особой дополнительной активации на стороне несущей системы не требуется (насколько я понял, KSM и разрабатывался в рамках оптимизации процессов виртуализации).
В дистрибутивах специально доработанных для использования в роли виртуализаторов имеются подсистемы контроля за использованием памятью несущей и гостевыми машинами, анализирующие общую картину и принимающие решения о включении, выключении и настройке средств оптимизации таковой в зависимости от ситуации. В "Linux Debian" ничего этого пока нет - будем просто включать KSM, если процессоры реально мощные, а оперативной памяти маловато (мой случай - куча двух-процессорных "HP DL380 G3/G4/G5/G6" имеющих на борту от 4-ёх до 8-ми гигабайт ОЗУ с тремя-пятью виртуальными машинами на каждом сервере). Далее мы автоматизируем активацию KSM в соответствующем скрипте подготовки окружения виртуализатора.
Устанавливаем набор утилит для организации "прозрачного" сетевого взаимодействия виртуальной машины с реальным окружением:
# aptitude install bridge-utils uml-utilities libcap2-bin socat
Сразу после установки пакетов окружения запретим автоматическое инициирование "виртуального коммутатора", которое производится по умолчанию скриптом пакета "uml-utilities", путём правки конфигурационного файла "/etc/default/uml-utilities":
# /etc/init.d/uml-utilities stop
# vi /etc/default/uml-utilities
....
UML_SWITCH_START="false"
....
UML_SWITCH_START="false"
....
Рассказываем Linux-у, что мы хотим создать "виртуальные мосты" (network bridge), к которым после будем прикреплять tap-интерфейсы виртуальных машин. Необходимо заранее определится с количеством виртуальных "мостов" и интерфейсов, которые будут к этим "мостам" прикреплены:
# vi /etc/network/interfaces
....
# Первым делом объявляем состояние готовности для всех задействуемых в схеме интерфейсов
# The primary network interface
auto eth0
iface eth0 inet manual
# The primary network interface
auto eth1
iface eth1 inet manual
....
# После инициирования задействованных интерфейсов описываем непосредственно "мосты"
# The primary bridge network interface
auto br0
iface br0 inet static
#
# Перед созданием "моста" явно останавливаем задействованные в нём сетевые интерфейсы
pre-up ifconfig eth0 0.0.0.0 down
#
# При введении сетевого интерфейса в "мост" перевод в режим "promisc" производится автоматически, но на всякий случай можно это продублировать
pre-up ifconfig eth0 promisc
#
# Блок описания параметров создаваемого "моста"
bridge_stp off
bridge_maxwait 0
bridge_fd 0
bridge_ports eth0
#
# Блок описания IP-параметров интерфейса, для обеспечения его доступности по сети, плюс к функционалу "моста"
address 10.10.3.10
netmask 255.255.255.248
network 10.10.3.8
broadcast 10.10.3.15
gateway 10.10.3.9
# The primary bridge network interface
auto br1
iface br1 inet manual
#
# Перед созданием "моста" явно останавливаем задействованные в нём сетевые интерфейсы
pre-up ifconfig eth1 0.0.0.0 down
#
# При введении сетевого интерфейса в "мост" перевод в режим "promisc" производится автоматически, но на всякий случай можно это продублировать
pre-up ifconfig eth1 promisc
#
# Блок описания параметров создаваемого "моста"
bridge_stp off
bridge_maxwait 0
bridge_fd 0
bridge_ports eth1
....
# Первым делом объявляем состояние готовности для всех задействуемых в схеме интерфейсов
# The primary network interface
auto eth0
iface eth0 inet manual
# The primary network interface
auto eth1
iface eth1 inet manual
....
# После инициирования задействованных интерфейсов описываем непосредственно "мосты"
# The primary bridge network interface
auto br0
iface br0 inet static
#
# Перед созданием "моста" явно останавливаем задействованные в нём сетевые интерфейсы
pre-up ifconfig eth0 0.0.0.0 down
#
# При введении сетевого интерфейса в "мост" перевод в режим "promisc" производится автоматически, но на всякий случай можно это продублировать
pre-up ifconfig eth0 promisc
#
# Блок описания параметров создаваемого "моста"
bridge_stp off
bridge_maxwait 0
bridge_fd 0
bridge_ports eth0
#
# Блок описания IP-параметров интерфейса, для обеспечения его доступности по сети, плюс к функционалу "моста"
address 10.10.3.10
netmask 255.255.255.248
network 10.10.3.8
broadcast 10.10.3.15
gateway 10.10.3.9
# The primary bridge network interface
auto br1
iface br1 inet manual
#
# Перед созданием "моста" явно останавливаем задействованные в нём сетевые интерфейсы
pre-up ifconfig eth1 0.0.0.0 down
#
# При введении сетевого интерфейса в "мост" перевод в режим "promisc" производится автоматически, но на всякий случай можно это продублировать
pre-up ifconfig eth1 promisc
#
# Блок описания параметров создаваемого "моста"
bridge_stp off
bridge_maxwait 0
bridge_fd 0
bridge_ports eth1
....
Выше я помогаю, если можно так выразится, системному скрипту, создающему сетевую конфигурацию отрабатывая конфигурационный файл "/etc/network/interfaces", явно указывая операндом "pre-up" проделать особо важные операции, такие как предварительная остановка и изменение режима работы интерфейсов, задействуемых в "мостах". Оно вроде как и само собой подразумевается (и делается обычно автоматически, без особого на то указания), однако на практике у меня были проблемы в "много-мостовых" схемах, которые, как я подозреваю, были вызваны не совсем верной последовательностью сборки "моста" системными скриптами.
И да, полагаю, что важно объявить задействованные в "мостах" сетевые интерфейсы до описания "мостов", в которых они применяются. Интерфейсы, которые присоединены к "мосту", переводятся в режим "promiscuous" и лишаются некоторых обычных для полноценных сетевых интерфейсов атрибутов. Если оставить описание сетевого интерфейса, задействованного в "мосте", ниже описания "моста" как такового, запросто может случится так, что он будет выведен из режима "promiscuous" и его работа в составе "моста" будет невозможна.
# /etc/init.d/networking restart
# dmesg
....
Bridge firewalling registered
device eth0 entered promiscuous mode
br0: port 1(eth0) entering forwarding state
device br0 entered promiscuous mode
....
device eth1 entered promiscuous mode
br1: port 1(eth1) entering forwarding state
device br1 entered promiscuous mode
....
Bridge firewalling registered
device eth0 entered promiscuous mode
br0: port 1(eth0) entering forwarding state
device br0 entered promiscuous mode
....
device eth1 entered promiscuous mode
br1: port 1(eth1) entering forwarding state
device br1 entered promiscuous mode
....
# ifconfig -a
br0 Link encap:Ethernet HWaddr MAC
inet addr:10.10.3.10 ....
....
br1 Link encap:Ethernet HWaddr MAC
....
eth0 Link encap:Ethernet HWaddr MAC
....
eth1 Link encap:Ethernet HWaddr MAC
....
inet addr:10.10.3.10 ....
....
br1 Link encap:Ethernet HWaddr MAC
....
eth0 Link encap:Ethernet HWaddr MAC
....
eth1 Link encap:Ethernet HWaddr MAC
....
Создаём окружение виртуализации:
# mkdir -p /etc/kvm/fnc.d
# mkdir -p /usr/local/etc/kvm/cnf.d
# mkdir -p /var/lib/kvm
# mkdir -p /var/log/kvm
# mkdir -p /mnt/storage0/kvm
# mkdir -p /usr/local/etc/kvm/cnf.d
# mkdir -p /var/lib/kvm
# mkdir -p /var/log/kvm
# mkdir -p /mnt/storage0/kvm
Группа "kvm" уже создана в системе, с инсталляцией дистрибутива Qemu-KVM. Если нет, то создаём её:
# groupadd kvm
Добавляем пользователя, от имени которых будет работать система виртуализации:
# useradd --system --shell /bin/false --home-dir /var/lib/kvm --gid kvm kvm
Обращаю внимание на то, что я лишил пользователя kvm возможности работать в так называемой "оболочке", иначе говоря, от имени пользователя можно запустить приложение, но нельзя будет работать в "командной строке". Это создаёт некоторые неудобства при тестировании работы приложений, но добавляет лишний час спокойного сна.
Явно добавляем пользователя kvm в группу kvm, что бы после для группы сделать разрешение на работу с системой виртуализации:
# usermod --append --groups kvm kvm
Явно добавляем пользователя kvm в группу disk, что бы предоставить ему возможность работать с дисковыми разделами:
# usermod --append --groups disk kvm
Проще всего определить ряд переменных, которые предположительно будут неизменными от машины к машине, в одном конфигурационном файле, расположив её в месте, определённом политикой Debian:
# vi /etc/default/qemu-kvm-custom
# Default configuration file for /etc/init.d/qemu-kvm-control
# Адрес директории динамически включаемых в тело скрипта управления описаний функций
FNCDIR="/etc/kvm/fnc.d"
# Адрес директории индивидуальных конфигураций виртуальных машин
CNFDIR="/usr/local/etc/kvm/cnf.d"
# Адрес директории временных ресурсов виртуальных машин
TMPDIR="/tmp/kvm"
# Адрес файла журнала
LOG="/var/log/kvm/kvm.log"
# Имя и группа пользователя, запускающего виртуальные машины
USER="kvm"
GROUP="kvm"
# Время ожидания (в секундах) между запуском виртуальных машин в пакетном режиме (необходимое для корректного выделения ОЗУ, спада первоначального потока IO-запросов и тому подобного)
RUNTIMEOUT="30"
# Адрес директории динамически включаемых в тело скрипта управления описаний функций
FNCDIR="/etc/kvm/fnc.d"
# Адрес директории индивидуальных конфигураций виртуальных машин
CNFDIR="/usr/local/etc/kvm/cnf.d"
# Адрес директории временных ресурсов виртуальных машин
TMPDIR="/tmp/kvm"
# Адрес файла журнала
LOG="/var/log/kvm/kvm.log"
# Имя и группа пользователя, запускающего виртуальные машины
USER="kvm"
GROUP="kvm"
# Время ожидания (в секундах) между запуском виртуальных машин в пакетном режиме (необходимое для корректного выделения ОЗУ, спада первоначального потока IO-запросов и тому подобного)
RUNTIMEOUT="30"
Вообще, по тщательности проработки окружения Qemu-KVM заметно, что среда виртуализации находится в разработке - многие важные мелочи оставлены "на потом". Например, при инсталляции создаётся соответствующая группа для пользователей подсистемы, однако на субъекты подсистемы право использования таковых этой группой не распространяются - хотя, казалось бы, что может быть логичнее?
Исправим это дополнительным скриптом, необходимость в котором возможно скоро отпадёт:
# vi /etc/init.d/qemu-kvm-preset && chmod ugo+rx /etc/init.d/qemu-kvm-preset
#!/bin/bash
### BEGIN INIT INFO
# Provides: qemu-kvm-preset
# Required-Start: $local_fs $syslog $network qemu-kvm
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Script management of virtual machines
# Description: Script management of virtual machines
### END INIT INFO
# Подключаем первичный конфигурационный файл
source "/etc/default/qemu-kvm-custom"
[ ${?} -ne 0 ] && { echo "Fatal error: missing default custom Qemu-KVM configuration file. Operation aborted."; exit 1; }
# Создаём директорию временных файлов, если таковая отсутствует, и задаём правила доступа
mkdir -p "${TMPDIR}"
chown ${USER}:${GROUP} "${TMPDIR}"
chmod ug+rw "${TMPDIR}"
chmod o-rw "${TMPDIR}"
# Проверяем поддержку аппаратной виртуализации и сетевого виртуального моста путём проверки наличия соответствующих символьных устройств
if [ -c /dev/kvm -a -c /dev/net/tun ]; then
# Подправляем разрешения для доступа к ресурсам подсистемы виртуализации
chown root:${GROUP} /dev/kvm
chmod ug+rw /dev/kvm
chown root:${GROUP} /dev/net/tun
chmod ug+rw /dev/net/tun
else
echo "Fatal error. Missing Qemu-KVM hardware acceleration device or Qemu-KVM virtual device network bridge (TUN). Operation aborted." | tee -a "${LOG}"
exit 1
fi
# Предоставляем обычному пользователю право запускать утилиты манипулирования сетевыми интерфейсами от имени суперпользователя (прикрепляя утилите suid-бит)
# !!! (слишком грубо, следует использовать setcap) !!!
# # find /sbin /usr/sbin -name "ifconfig" -print0 | xargs --null chmod ug+s
# # find /sbin /usr/sbin -name "tunctl" -print0 | xargs --null chmod ug+s
# # find /sbin /usr/sbin -name "brctl" -print0 | xargs --null chmod ug+s
#
# # find /sbin /bin /usr/sbin /usr/bin -name "kill" -print0 | xargs --null chmod ug+s
# Добавляем KVM возможностей с помощью расширения стандарта POSIX "capabilities"
setcap CAP_NET_ADMIN=ep /usr/bin/kvm
#
setcap CAP_KILL=ep /bin/kill
# Включаем, если таковая доступна, поддержку несущей системой KSM (Kernel Samepage Merging)
[ -f /sys/kernel/mm/ksm/run ] && { echo 1 > /sys/kernel/mm/ksm/run; }
exit 0
### BEGIN INIT INFO
# Provides: qemu-kvm-preset
# Required-Start: $local_fs $syslog $network qemu-kvm
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Script management of virtual machines
# Description: Script management of virtual machines
### END INIT INFO
# Подключаем первичный конфигурационный файл
source "/etc/default/qemu-kvm-custom"
[ ${?} -ne 0 ] && { echo "Fatal error: missing default custom Qemu-KVM configuration file. Operation aborted."; exit 1; }
# Создаём директорию временных файлов, если таковая отсутствует, и задаём правила доступа
mkdir -p "${TMPDIR}"
chown ${USER}:${GROUP} "${TMPDIR}"
chmod ug+rw "${TMPDIR}"
chmod o-rw "${TMPDIR}"
# Проверяем поддержку аппаратной виртуализации и сетевого виртуального моста путём проверки наличия соответствующих символьных устройств
if [ -c /dev/kvm -a -c /dev/net/tun ]; then
# Подправляем разрешения для доступа к ресурсам подсистемы виртуализации
chown root:${GROUP} /dev/kvm
chmod ug+rw /dev/kvm
chown root:${GROUP} /dev/net/tun
chmod ug+rw /dev/net/tun
else
echo "Fatal error. Missing Qemu-KVM hardware acceleration device or Qemu-KVM virtual device network bridge (TUN). Operation aborted." | tee -a "${LOG}"
exit 1
fi
# Предоставляем обычному пользователю право запускать утилиты манипулирования сетевыми интерфейсами от имени суперпользователя (прикрепляя утилите suid-бит)
# !!! (слишком грубо, следует использовать setcap) !!!
# # find /sbin /usr/sbin -name "ifconfig" -print0 | xargs --null chmod ug+s
# # find /sbin /usr/sbin -name "tunctl" -print0 | xargs --null chmod ug+s
# # find /sbin /usr/sbin -name "brctl" -print0 | xargs --null chmod ug+s
#
# # find /sbin /bin /usr/sbin /usr/bin -name "kill" -print0 | xargs --null chmod ug+s
# Добавляем KVM возможностей с помощью расширения стандарта POSIX "capabilities"
setcap CAP_NET_ADMIN=ep /usr/bin/kvm
#
setcap CAP_KILL=ep /bin/kill
# Включаем, если таковая доступна, поддержку несущей системой KSM (Kernel Samepage Merging)
[ -f /sys/kernel/mm/ksm/run ] && { echo 1 > /sys/kernel/mm/ksm/run; }
exit 0
Прописываем наш скрипт для нужных уровней исполнения в системах Linux Debian Squeeze/Wheezy:
# update-rc.d qemu-kvm-preset start 10 2 3 4 5 . stop 10 0 1 6 .
Для "Linux Debian Jessie" и Fedora с Systemd способ инициализирования автозапуска уже иной:
# systemctl enable qemu-kvm-preset
Synchronizing state for qemu-kvm-preset.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d qemu-kvm-preset defaults
Executing /usr/sbin/update-rc.d qemu-kvm-preset enable
Executing /usr/sbin/update-rc.d qemu-kvm-preset defaults
Executing /usr/sbin/update-rc.d qemu-kvm-preset enable
В стандарте POSIX имеется механизм так называемых "возможностей" (capabilities), более или менее полноценно реализованный в Linux начиная с ядра "2.6.24", который позволяет наделять приложения, работающие с правами обычных пользователей, рядом привилегий root, не давая им полных прав суперпользователя. Смысл заключается в том, чтобы позволить определенным приложениям, запускаемым с правами обычных пользователей и не имеющим серьезных полномочий в системе, совершать серьёзные и несущие угрозу действия, на которые раньше был способен только root. Такая возможность позволяет отказаться от использования SUID-бита для приложений и в то же время оставить за ним право на выполнение каких-либо привилегированных действий. Поэтому, если приложение когда-нибудь окажется скомпрометированным, злоумышленник не сможет нанести особого вреда системе.
Индивидуальные конфигурации виртуальных машин будем описывать не прямым указанием переменных среды командного интерпретатора BASH, а в файлах с расширением ".cnf", строками формата сходного с тем, в котором задаются системные переменные (sysctl):
# vi /usr/local/etc/kvm/cnf.d/mashine0.cnf
Содержимое конфигурационных файлов будем пополнять поэтапно, по мере написания соответствующих функций, а пока внесём в него условное имя виртуальной машины, которое в дальнейшем будет служить идентификатором в исполняемых скриптах:
....
# Условное имя виртуальной машины, используется в дальнейшем как идентификатор (без пробелов, избегать спецсимволов)
name=mashine0
....
# Условное имя виртуальной машины, используется в дальнейшем как идентификатор (без пробелов, избегать спецсимволов)
name=mashine0
....
За несколько лет развития BASH-скрипт разросся до невоспринимаемых в один подход размеров и был разбит на автоматически включаемые в основной каркас нижеследующие логические блоки:
Вспомогательные функции.
Функция предварительной проверки возможности и необходимости запуска виртуальной машины.
Функция предварительной инициализации окружения виртуализации.
Функция инициализации виртуальных устройств хранения данных.
Функция инициализации виртуальных сетевых интерфейсов.
Функция инициализации средств управления виртуальной машиной.
Функция запуска виртуальной машины.
Функция остановки виртуальной машины.
Функция предварительной проверки возможности и необходимости запуска виртуальной машины.
Функция предварительной инициализации окружения виртуализации.
Функция инициализации виртуальных устройств хранения данных.
Функция инициализации виртуальных сетевых интерфейсов.
Функция инициализации средств управления виртуальной машиной.
Функция запуска виртуальной машины.
Функция остановки виртуальной машины.
Пишем каркасный скрипт автоматизации процедур управления виртуальными машинами:
# vi /etc/init.d/qemu-kvm-control && chmod ugo+rx /etc/init.d/qemu-kvm-control
#!/bin/bash
### BEGIN INIT INFO
# Provides: qemu-kvm-control
# Required-Start: $local_fs $syslog $network qemu-kvm-preset
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Script management of virtual machines
# Description: Script management of virtual machines
### END INIT INFO
# На случай, если скрипт управления запускается от непривелигированного пользователя, объявляем расширенный набор путей для поиска запрашиваемых ресурсов
export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
# Переопределяем "маску" для создаваемых в рамках управления виртуальными машинами файлами, предписывая доступ на чтение и запись как запускающему скрипт пользователю, так и членам его группы
umask 0007
# Подключаем первичный конфигурационный файл
source "/etc/default/qemu-kvm-custom"
[ ${?} -ne 0 ] && { echo "Fatal error: missing default custom Qemu-KVM configuration file. Operation aborted."; exit 1; }
# Проверяем наличие директории для временных файлов, создавая её в случае необходимости
[ ! -d "${TMPDIR}" ] && { mkdir -p "${TMPDIR}"; }
# Проверяем поддержку аппаратной виртуализации и сетевого окружения путём проверки наличия соответствующих символьных устройств
[ ! -c /dev/kvm -o ! -c /dev/net/tun ] && { echo "Fatal error. Missing Qemu-KVM hardware acceleration device or Qemu-KVM virtual device network bridge (TUN). Operation aborted." | tee -a "${LOG}"; exit 1; }
# Принимаем в переменные с "говорящими" именами входящие аргументы
INSTANCE=${0}
OPERATION=${1}
TARGET=${2}
DATE=`date +"%Y-%m-%d %H:%M:%S"`
# Проверяем, имеется ли конфигурационный файл для целевой виртуальной машины
[ "${TARGET}" != "" -a ! -f "${CNFDIR}/${TARGET}.cnf" ] && { echo "${DATE}: Отсутствует конфигурационный файл ${CNFDIR}/${TARGET}.cnf целевой виртуальной машины ${TARGET}." | tee -a "${LOG}"; exit 1; }
# Перебираем в цикле все объекты (по маске) в целевой директории динамически подключаемых описаний функций
cd "${FNCDIR}"
# for OBJECT in *\.fnc ; do
for OBJECT in $(ls --format=single-column | grep --extended-regexp "*\.fnc$") ; do
# Включаем в тело скрипта текст обнаруженных фрагментов описаний функций
[ -f "${FNCDIR}/${OBJECT}" ] && source "${FNCDIR}/${OBJECT}"
done
# Описываем функцию формирования опций окружений виртуальной машины и непосредственного её запуска
function start() {
# Получаем условное имя виртуальной машины
NAME=`grep --ignore-case "^name=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие условного имени виртуальной машины, которое в дальнейшем будет использоваться как её идентификатор
if [ "${NAME}" == "" ]; then
echo "${DATE}: Отсутствует условное имя запускаемой виртуальной машины. Операция прервана." | tee -a "${LOGT}"
return 1
fi
# Вызываем функцию предварительной проверки возможности и необходимости запуска
declare -f start-precheck >/dev/null || { echo "${DATE}: Fatal error! Не определена функция предварительной проверки возможности и необходимости запуска. Operation aborted." | tee -a "${LOGT}"; return 1; }
#
start-precheck
if [ ${?} -ne 0 ]; then
echo "${DATE}: Виртуальная машина ${NAME} не будет запущена. Операция прервана." | tee -a "${LOGT}"
return 0
fi
# Вызываем функцию предварительной инициализации
declare -f start-preset >/dev/null || { echo "${DATE}: Fatal error! Не определена функция предварительной инициализации. Operation aborted." | tee -a "${LOGT}"; return 1; }
#
start-preset
if [ ${?} -ne 0 ]; then
echo "${DATE}: Ошибка предварительной инициализации виртуальной машины ${NAME}." | tee -a "${LOGT}"
return 1
fi
# Вызываем функцию инициализации виртуальных устройств хранения данных
declare -f start-hdd >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция инициализации виртуальных дисков. Disk drives will not be available." | tee -a "${LOGT}"
else
start-hdd
if [ ${?} -ne 0 ]; then
echo "${DATE}: Ошибка инициализации виртуальных дисков машины ${NAME}." | tee -a "${LOGT}"
return 1
fi
fi
# Вызываем функцию инициализации виртуальных сетевых интерфейсов
declare -f start-network >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция инициализации виртуальных сетевых интерфейсов. Virtual network devices will not be available." | tee -a "${LOGT}"
else
start-network
if [ ${?} -ne 0 ]; then
echo "${DATE}: Ошибка инициализации виртуальных сетевых интерфейсов машины ${NAME}." | tee -a "${LOGT}"
return 1
fi
fi
# Вызываем функцию инициализации средств управления виртуальной машиной
declare -f start-management >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция инициализации средств управления виртуальной машиной. Controls the virtual machine will not be available." | tee -a "${LOGT}"
else
start-management
if [ ${?} -ne 0 ]; then
echo "${DATE}: Ошибка инициализации средств управления виртуальной машиной ${NAME}." | tee -a "${LOGT}"
return 1
fi
fi
# Вызываем функцию запуска виртуальной машины
declare -f start-run >/dev/null || { echo "${DATE}: Fatal error! Не определена функция запуска виртуальной машины. Operation aborted." | tee -a "${LOGT}"; return 1; }
#
start-run
if [ ${?} -ne 0 ]; then
echo "${DATE}: Ошибка запуска виртуальной машины ${NAME}." | tee -a "${LOGT}"
return 1
fi
return ${?}
}
# Определяем функцию остановки виртуальной машины
function stop() {
# Получаем условное имя виртуальной машины
NAME=`grep --ignore-case "^name=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие условного имени виртуальной машины, которое в дальнейшем будет использоваться как её идентификатор
if [ "${NAME}" == "" ]; then
echo "${DATE}: Отсутствует условное имя останавливаемой виртуальной машины. Операция прервана." | tee -a "${LOGT}"
return 1
fi
# Вызываем функцию предварительной проверки возможности и необходимости остановки
declare -f stop-precheck >/dev/null || { echo "${DATE}: Fatal error! Не определена функция предварительной проверки возможности и необходимости остановки. Operation aborted." | tee -a "${LOGT}"; return 1; }
#
stop-precheck
if [ ${?} -ne 0 ]; then
return 0
fi
# Вызываем функцию остановки виртуальной машины путём подачи сигнала ACPI
declare -f stop-acpi >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция остановки виртуальной машины путём подачи сигнала ACPI." | tee -a "${LOGT}"
else
stop-acpi
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Ошибка остановки виртуальной машиной ${NAME} путём подачи сигнала ACPI. Продолжаем попытки остановить виртуальную машину." | tee -a "${LOGT}"
else
return 0
fi
fi
# Вызываем функцию остановки виртуальной машины путём подачи сигнала SMB
declare -f stop-smb >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция остановки виртуальной машины путём подачи сигнала SMB." | tee -a "${LOGT}"
else
stop-smb
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Ошибка остановки виртуальной машиной ${NAME} путём подачи сигнала SMB. Продолжаем попытки остановить виртуальную машину." | tee -a "${LOGT}"
else
return 0
fi
fi
# Вызываем функцию остановки виртуальной машины путём уничтожения исполняемого процесса (KILL)
declare -f stop-kill >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция остановки виртуальной машины путём подачи сигнала KILL." | tee -a "${LOGT}"
else
stop-kill
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Ошибка остановки виртуальной машиной ${NAME} путём уничтожения исполняемого процесса." | tee -a "${LOGT}"
return 1
else
return 0
fi
fi
return ${?}
}
# Определяем функцию предварительной инициализации конфигурационного файла запускаемой виртуальной машины
function start-init() {
# Выжидаем время, необходимое для корректного выделения ОЗУ, спада первоначального потока IO-запросов и тому подобного, в случае если виртуальные машины запускаются в пакетном режиме и эта - не первая в очереди
if [ ${RUNNING} -ne 0 ] ; then
echo "${DATE}: Information. Wait ${RUNTIMEOUT} seconds after running previous virtual mashine..." | tee -a "${LOGT}"
sleep ${RUNTIMEOUT};
fi
# Создаём временный файл журнала событий
LOGT=$(mktemp "${TMPDIR}/log.XXXXXXXX")
# Получаем время старта
DATE=`date +"%Y-%m-%d %H:%M:%S"`
# Передаём управление функции непосредственного запуска виртуальной машины
start
# Если запуск виртуальной машины завершился неудачно, то отсылаем об этом отчёт администратору
if [ "${?}" -ne "0" ] ; then
send-report "Error. Virtual mashine running problem!" "$(cat ${LOGT})"
fi
# Включаем временный журнал событий в основной и удаляем временный
cat "${LOGT}" >> "${LOG}"
rm --force "${LOGT}"
return ${?}
}
# Определяем функцию предварительной инициализации конфигурационного файла останавливаемой виртуальной машины
function stop-init() {
# Создаём временный файл журнала событий
LOGT=$(mktemp "${TMPDIR}/log.XXXXXXXX")
# Получаем время остановки
DATE=`date +"%Y-%m-%d %H:%M:%S"`
# Передаём управление функции непосредственной остановки виртуальной машины
stop
# Если запуск виртуальной машины завершился неудачно, то отсылаем об этом отчёт администратору
if [ ${?} -ne 0 ]; then
echo "${DATE}: Critical error. Работа виртуальной машины ${NAME} не была прекращена. Все попытки остановки были безуспешными." | tee -a "${LOGT}"
send-report "Error. Virtual mashine stoping problem!" "$(cat ${LOGT})"
else
# Если виртуальная машина успешно остановлена, то выключаем её виртуальный сетевой интерфейс
declare -f stop-network >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Warning! Не определена функция деактивации виртуальных сетевых интерфейсов." | tee -a "${LOGT}"
else
stop-network
fi
fi
# Включаем временный журнал событий в основной и удаляем временный
cat "${LOGT}" >> "${LOG}"
rm --force "${LOGT}"
return ${?}
}
# Блок выбора варианта исполнения скрипта
case "$1" in
"start"|"install")
# Инициируем переменную счётчика успешно запущенных виртуальных машин
RUNNING="0"
if [ "${TARGET}" == "" ]; then
# Переходим в директорию конфигурационных файлов
cd "${CNFDIR}"
# Перебираем в цикле все объекты (по маске) в целевой директории
for OBJECT in $(ls --format=single-column | grep --extended-regexp "*\.cnf$") ; do
# Определяем путь к очередному конфигурационному файлу
CNF="${CNFDIR}/${OBJECT}"
# Передаём управление функции подготовки окружения переменных виртуальной машины
start-init
done
else
# Определяем путь к конфигурационному файлу
CNF="${CNFDIR}/${TARGET}.cnf"
# Передаём управление функции подготовки окружения переменных виртуальной машины
start-init
fi
;;
"stop")
if [ "${TARGET}" == "" ]; then
# Переходим в директорию конфигурационных файлов
cd "${CNFDIR}"
# Перебираем в цикле все объекты (по маске) в целевой директории
for OBJECT in $(ls --format=single-column | grep --extended-regexp "*\.cnf$") ; do
# Определяем путь к очередному конфигурационному файлу
CNF="${CNFDIR}/${OBJECT}"
# Передаём управление функции подготовки окружения переменных виртуальной машины
stop-init
done
else
# Определяем путь к конфигурационному файлу
CNF="${CNFDIR}/${TARGET}.cnf"
# Передаём управление функции подготовки окружения переменных виртуальной машины
stop-init
fi
;;
"restart")
echo "${DATE}: Received command to restart the virtual machine." | tee -a "${LOG}"
stop-init
echo "${DATE}: Wait..." | tee -a "${LOG}"
sleep 1
start-init
;;
*)
echo "Usage $0 {install|start|stop|restart} [target]" >&2
exit 1
;;
esac
exit 0
### BEGIN INIT INFO
# Provides: qemu-kvm-control
# Required-Start: $local_fs $syslog $network qemu-kvm-preset
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Script management of virtual machines
# Description: Script management of virtual machines
### END INIT INFO
# На случай, если скрипт управления запускается от непривелигированного пользователя, объявляем расширенный набор путей для поиска запрашиваемых ресурсов
export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
# Переопределяем "маску" для создаваемых в рамках управления виртуальными машинами файлами, предписывая доступ на чтение и запись как запускающему скрипт пользователю, так и членам его группы
umask 0007
# Подключаем первичный конфигурационный файл
source "/etc/default/qemu-kvm-custom"
[ ${?} -ne 0 ] && { echo "Fatal error: missing default custom Qemu-KVM configuration file. Operation aborted."; exit 1; }
# Проверяем наличие директории для временных файлов, создавая её в случае необходимости
[ ! -d "${TMPDIR}" ] && { mkdir -p "${TMPDIR}"; }
# Проверяем поддержку аппаратной виртуализации и сетевого окружения путём проверки наличия соответствующих символьных устройств
[ ! -c /dev/kvm -o ! -c /dev/net/tun ] && { echo "Fatal error. Missing Qemu-KVM hardware acceleration device or Qemu-KVM virtual device network bridge (TUN). Operation aborted." | tee -a "${LOG}"; exit 1; }
# Принимаем в переменные с "говорящими" именами входящие аргументы
INSTANCE=${0}
OPERATION=${1}
TARGET=${2}
DATE=`date +"%Y-%m-%d %H:%M:%S"`
# Проверяем, имеется ли конфигурационный файл для целевой виртуальной машины
[ "${TARGET}" != "" -a ! -f "${CNFDIR}/${TARGET}.cnf" ] && { echo "${DATE}: Отсутствует конфигурационный файл ${CNFDIR}/${TARGET}.cnf целевой виртуальной машины ${TARGET}." | tee -a "${LOG}"; exit 1; }
# Перебираем в цикле все объекты (по маске) в целевой директории динамически подключаемых описаний функций
cd "${FNCDIR}"
# for OBJECT in *\.fnc ; do
for OBJECT in $(ls --format=single-column | grep --extended-regexp "*\.fnc$") ; do
# Включаем в тело скрипта текст обнаруженных фрагментов описаний функций
[ -f "${FNCDIR}/${OBJECT}" ] && source "${FNCDIR}/${OBJECT}"
done
# Описываем функцию формирования опций окружений виртуальной машины и непосредственного её запуска
function start() {
# Получаем условное имя виртуальной машины
NAME=`grep --ignore-case "^name=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие условного имени виртуальной машины, которое в дальнейшем будет использоваться как её идентификатор
if [ "${NAME}" == "" ]; then
echo "${DATE}: Отсутствует условное имя запускаемой виртуальной машины. Операция прервана." | tee -a "${LOGT}"
return 1
fi
# Вызываем функцию предварительной проверки возможности и необходимости запуска
declare -f start-precheck >/dev/null || { echo "${DATE}: Fatal error! Не определена функция предварительной проверки возможности и необходимости запуска. Operation aborted." | tee -a "${LOGT}"; return 1; }
#
start-precheck
if [ ${?} -ne 0 ]; then
echo "${DATE}: Виртуальная машина ${NAME} не будет запущена. Операция прервана." | tee -a "${LOGT}"
return 0
fi
# Вызываем функцию предварительной инициализации
declare -f start-preset >/dev/null || { echo "${DATE}: Fatal error! Не определена функция предварительной инициализации. Operation aborted." | tee -a "${LOGT}"; return 1; }
#
start-preset
if [ ${?} -ne 0 ]; then
echo "${DATE}: Ошибка предварительной инициализации виртуальной машины ${NAME}." | tee -a "${LOGT}"
return 1
fi
# Вызываем функцию инициализации виртуальных устройств хранения данных
declare -f start-hdd >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция инициализации виртуальных дисков. Disk drives will not be available." | tee -a "${LOGT}"
else
start-hdd
if [ ${?} -ne 0 ]; then
echo "${DATE}: Ошибка инициализации виртуальных дисков машины ${NAME}." | tee -a "${LOGT}"
return 1
fi
fi
# Вызываем функцию инициализации виртуальных сетевых интерфейсов
declare -f start-network >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция инициализации виртуальных сетевых интерфейсов. Virtual network devices will not be available." | tee -a "${LOGT}"
else
start-network
if [ ${?} -ne 0 ]; then
echo "${DATE}: Ошибка инициализации виртуальных сетевых интерфейсов машины ${NAME}." | tee -a "${LOGT}"
return 1
fi
fi
# Вызываем функцию инициализации средств управления виртуальной машиной
declare -f start-management >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция инициализации средств управления виртуальной машиной. Controls the virtual machine will not be available." | tee -a "${LOGT}"
else
start-management
if [ ${?} -ne 0 ]; then
echo "${DATE}: Ошибка инициализации средств управления виртуальной машиной ${NAME}." | tee -a "${LOGT}"
return 1
fi
fi
# Вызываем функцию запуска виртуальной машины
declare -f start-run >/dev/null || { echo "${DATE}: Fatal error! Не определена функция запуска виртуальной машины. Operation aborted." | tee -a "${LOGT}"; return 1; }
#
start-run
if [ ${?} -ne 0 ]; then
echo "${DATE}: Ошибка запуска виртуальной машины ${NAME}." | tee -a "${LOGT}"
return 1
fi
return ${?}
}
# Определяем функцию остановки виртуальной машины
function stop() {
# Получаем условное имя виртуальной машины
NAME=`grep --ignore-case "^name=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие условного имени виртуальной машины, которое в дальнейшем будет использоваться как её идентификатор
if [ "${NAME}" == "" ]; then
echo "${DATE}: Отсутствует условное имя останавливаемой виртуальной машины. Операция прервана." | tee -a "${LOGT}"
return 1
fi
# Вызываем функцию предварительной проверки возможности и необходимости остановки
declare -f stop-precheck >/dev/null || { echo "${DATE}: Fatal error! Не определена функция предварительной проверки возможности и необходимости остановки. Operation aborted." | tee -a "${LOGT}"; return 1; }
#
stop-precheck
if [ ${?} -ne 0 ]; then
return 0
fi
# Вызываем функцию остановки виртуальной машины путём подачи сигнала ACPI
declare -f stop-acpi >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция остановки виртуальной машины путём подачи сигнала ACPI." | tee -a "${LOGT}"
else
stop-acpi
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Ошибка остановки виртуальной машиной ${NAME} путём подачи сигнала ACPI. Продолжаем попытки остановить виртуальную машину." | tee -a "${LOGT}"
else
return 0
fi
fi
# Вызываем функцию остановки виртуальной машины путём подачи сигнала SMB
declare -f stop-smb >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция остановки виртуальной машины путём подачи сигнала SMB." | tee -a "${LOGT}"
else
stop-smb
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Ошибка остановки виртуальной машиной ${NAME} путём подачи сигнала SMB. Продолжаем попытки остановить виртуальную машину." | tee -a "${LOGT}"
else
return 0
fi
fi
# Вызываем функцию остановки виртуальной машины путём уничтожения исполняемого процесса (KILL)
declare -f stop-kill >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Не определена функция остановки виртуальной машины путём подачи сигнала KILL." | tee -a "${LOGT}"
else
stop-kill
if [ ${?} -ne 0 ]; then
echo "${DATE}: Notice. Ошибка остановки виртуальной машиной ${NAME} путём уничтожения исполняемого процесса." | tee -a "${LOGT}"
return 1
else
return 0
fi
fi
return ${?}
}
# Определяем функцию предварительной инициализации конфигурационного файла запускаемой виртуальной машины
function start-init() {
# Выжидаем время, необходимое для корректного выделения ОЗУ, спада первоначального потока IO-запросов и тому подобного, в случае если виртуальные машины запускаются в пакетном режиме и эта - не первая в очереди
if [ ${RUNNING} -ne 0 ] ; then
echo "${DATE}: Information. Wait ${RUNTIMEOUT} seconds after running previous virtual mashine..." | tee -a "${LOGT}"
sleep ${RUNTIMEOUT};
fi
# Создаём временный файл журнала событий
LOGT=$(mktemp "${TMPDIR}/log.XXXXXXXX")
# Получаем время старта
DATE=`date +"%Y-%m-%d %H:%M:%S"`
# Передаём управление функции непосредственного запуска виртуальной машины
start
# Если запуск виртуальной машины завершился неудачно, то отсылаем об этом отчёт администратору
if [ "${?}" -ne "0" ] ; then
send-report "Error. Virtual mashine running problem!" "$(cat ${LOGT})"
fi
# Включаем временный журнал событий в основной и удаляем временный
cat "${LOGT}" >> "${LOG}"
rm --force "${LOGT}"
return ${?}
}
# Определяем функцию предварительной инициализации конфигурационного файла останавливаемой виртуальной машины
function stop-init() {
# Создаём временный файл журнала событий
LOGT=$(mktemp "${TMPDIR}/log.XXXXXXXX")
# Получаем время остановки
DATE=`date +"%Y-%m-%d %H:%M:%S"`
# Передаём управление функции непосредственной остановки виртуальной машины
stop
# Если запуск виртуальной машины завершился неудачно, то отсылаем об этом отчёт администратору
if [ ${?} -ne 0 ]; then
echo "${DATE}: Critical error. Работа виртуальной машины ${NAME} не была прекращена. Все попытки остановки были безуспешными." | tee -a "${LOGT}"
send-report "Error. Virtual mashine stoping problem!" "$(cat ${LOGT})"
else
# Если виртуальная машина успешно остановлена, то выключаем её виртуальный сетевой интерфейс
declare -f stop-network >/dev/null
if [ ${?} -ne 0 ]; then
echo "${DATE}: Warning! Не определена функция деактивации виртуальных сетевых интерфейсов." | tee -a "${LOGT}"
else
stop-network
fi
fi
# Включаем временный журнал событий в основной и удаляем временный
cat "${LOGT}" >> "${LOG}"
rm --force "${LOGT}"
return ${?}
}
# Блок выбора варианта исполнения скрипта
case "$1" in
"start"|"install")
# Инициируем переменную счётчика успешно запущенных виртуальных машин
RUNNING="0"
if [ "${TARGET}" == "" ]; then
# Переходим в директорию конфигурационных файлов
cd "${CNFDIR}"
# Перебираем в цикле все объекты (по маске) в целевой директории
for OBJECT in $(ls --format=single-column | grep --extended-regexp "*\.cnf$") ; do
# Определяем путь к очередному конфигурационному файлу
CNF="${CNFDIR}/${OBJECT}"
# Передаём управление функции подготовки окружения переменных виртуальной машины
start-init
done
else
# Определяем путь к конфигурационному файлу
CNF="${CNFDIR}/${TARGET}.cnf"
# Передаём управление функции подготовки окружения переменных виртуальной машины
start-init
fi
;;
"stop")
if [ "${TARGET}" == "" ]; then
# Переходим в директорию конфигурационных файлов
cd "${CNFDIR}"
# Перебираем в цикле все объекты (по маске) в целевой директории
for OBJECT in $(ls --format=single-column | grep --extended-regexp "*\.cnf$") ; do
# Определяем путь к очередному конфигурационному файлу
CNF="${CNFDIR}/${OBJECT}"
# Передаём управление функции подготовки окружения переменных виртуальной машины
stop-init
done
else
# Определяем путь к конфигурационному файлу
CNF="${CNFDIR}/${TARGET}.cnf"
# Передаём управление функции подготовки окружения переменных виртуальной машины
stop-init
fi
;;
"restart")
echo "${DATE}: Received command to restart the virtual machine." | tee -a "${LOG}"
stop-init
echo "${DATE}: Wait..." | tee -a "${LOG}"
sleep 1
start-init
;;
*)
echo "Usage $0 {install|start|stop|restart} [target]" >&2
exit 1
;;
esac
exit 0
Прописываем наш скрипт для нужных уровней исполнения в системах "Debian Squeeze/Wheezy":
# update-rc.d qemu-kvm-control start 10 2 3 4 5 . stop 10 0 1 6 .
Прописываем наш скрипт для нужных уровней исполнения в системе "Debian Jessie" и Fedora с Systemd:
# systemctl enable qemu-kvm-control
Естественно, перед тем, как запускать виртуальную машину, нужно иметь дисковый раздел, LVM-том или файл, который будет использоваться как виртуальное дисковое устройство.
Запускаем процедуру создания файла виртуального диска:
# dd if=/dev/zero of=/mnt/storage0/kvm/mashine0/mashine0.raw bs=1G count=100
В ядре Линукса от v2.6.23 включён патч для поддержки нового системного вызова "fallocate", который позволяет запросить непрерывный кусок пространства в файловой системе для избежания фрагментации. Для начала эта возможность доступна в "ext4" и XFS. Таким образом в некоторых случаях виртуальный RAW-диск можно создать мгновенно:
# truncate -s 100G /mnt/storage0/kvm/mashine0/mashine0.raw
...или так:
# fallocate --length 100GiB /mnt/storage0/kvm/mashine0/mashine0.raw
Как правило, операционные системы семейства "MS Windows" (до 2003 точно) не распознают раздел, если его тип явно не указан заранее, например как FAT или NTFS - потому важно сделать это до начала установки её на виртуальную машину.
Заранее генерируем MAC для нашего виртуального интерфейса (далее его будем использовать везде при запуске целевой виртуальной машины):
$ MACADDR="52:54:$(dd if=/dev/urandom count=1 2>/dev/null | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\).*$/\1:\2:\3:\4/')"; echo $MACADDR
Должно получится что-то вроде этого:
52:54:0c:b8:71:b0
После запуска виртуальной машины можно удостоверится в том, что KVM прослушивает порты, предназначенные для доступа к VNC-серверу и к контрольной консоли KVM:
# netstat -apn
tcp 0 0 0.0.0.0:5901 0.0.0.0:* LISTEN 5398/kvm
unix 2 [ ACC ] STREAM LISTENING 41146 5398/kvm /tmp/kvm/monitor/mashine0.socket
unix 2 [ ACC ] STREAM LISTENING 41147 5398/kvm /tmp/kvm/monitor/mashine0.local.socket
unix 2 [ ACC ] STREAM LISTENING 41146 5398/kvm /tmp/kvm/monitor/mashine0.socket
unix 2 [ ACC ] STREAM LISTENING 41147 5398/kvm /tmp/kvm/monitor/mashine0.local.socket
И да, подключив текущий STDIO к "сокету", на который прицеплена контрольная консоль виртуальной машиной, можно таковой поуправлять:
$ socat STDIO UNIX-CONNECT:/tmp/kvm/monitor/mashine0.socket
QEMU 0.12.5 monitor - type 'help' for more information
(qemu) info version
0.12.5 (qemu-kvm-0.12.5)
(qemu) ^C
(qemu) info version
0.12.5 (qemu-kvm-0.12.5)
(qemu) ^C
Нужно понимать, что "локальный сокет" не допускает конкурентных подключений, так что, если есть необходимость обращаться к управляющему "монитору" с разных скриптов, то стоит завести для них несколько точек входа, что мы и сделали, инициировав два "сокета".
Также можно будет увидеть, что в списке активных интерфейсов появились новые, через которые виртуальные машины подключатся к "мосту", привязанному, в свою очередь, к физическому интерфейсу eth0:
# dmesg
....
tun: Universal TUN/TAP device driver, 1.6
device tap0 entered promiscuous mode
br0: port 2(tap0) entering forwarding state
device tap1 entered promiscuous mode
br0: port 3(tap1) entering forwarding state
....
tun: Universal TUN/TAP device driver, 1.6
device tap0 entered promiscuous mode
br0: port 2(tap0) entering forwarding state
device tap1 entered promiscuous mode
br0: port 3(tap1) entering forwarding state
....
Связи между реальными и виртуальными интерфейсами можно уточнить с помощью соответствующей утилиты:
# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.9c8e99f9c7b2 no eth0
tap0
br1 8000.9c8e99f9c7b4 no eth1
tap1
br0 8000.9c8e99f9c7b2 no eth0
tap0
br1 8000.9c8e99f9c7b4 no eth1
tap1
Сетевые интерфейсы, созданные для наших виртуальных машин не имеет IP-адресов, так как предназначены только для трансляции трафика между "мостом" и виртуальной машиной:
# ifconfig -a
....
tap0 Link encap:Ethernet HWaddr MAC
....
tap1 Link encap:Ethernet HWaddr MAC
....
tap0 Link encap:Ethernet HWaddr MAC
....
tap1 Link encap:Ethernet HWaddr MAC
....
Для того, что бы не получать сильно разросшиеся файлы журналов, настроим их ротацию.
Устанавливаем приложение ротации текстовых файлов с одновременным их сжатием:
# aptitude install logrotate gzip
Создаём конфигурационный файл ротации журнальных файлов для Lotus Notes Domino:
# mkdir -p /etc/logrotate.d
# vi /etc/logrotate.d/kvm
# vi /etc/logrotate.d/kvm
# шаблон указывающий объекты подлежащие "ротации"
/var/log/kvm/*.log {
# размер журнального файла, после которого он обрабатывается утилитой
size 1M
# отсутствие файла не вызывает ошибку
missingok
# количество хранимых отработанных резервных копий
rotate 10
# указание сжимать отрабатываемые резервные копии
compress
# указание не сжимать первую резервную копию, делать это при повторном проходе
delaycompress
# указание не отрабатывать пустые файлы
notifempty
# добавлять к наименованию файла резервной копии даты в формате "-YYYYMMDD"
dateext
# задать права доступа, владельца и группу создаваемого журнального файла
create 660 kvm kvm
}
/var/log/kvm/*.log {
# размер журнального файла, после которого он обрабатывается утилитой
size 1M
# отсутствие файла не вызывает ошибку
missingok
# количество хранимых отработанных резервных копий
rotate 10
# указание сжимать отрабатываемые резервные копии
compress
# указание не сжимать первую резервную копию, делать это при повторном проходе
delaycompress
# указание не отрабатывать пустые файлы
notifempty
# добавлять к наименованию файла резервной копии даты в формате "-YYYYMMDD"
dateext
# задать права доступа, владельца и группу создаваемого журнального файла
create 660 kvm kvm
}
Проверяем корректность конфигурационного файла:
# logrotate -d /etc/logrotate.d/kvm
Наводим порядок с правами доступа к ресурсам:
# chown -R kvm:kvm /usr/local/etc/kvm /var/lib/kvm /var/log/kvm /mnt/storage0/kvm
# chmod -R ug+rw /usr/local/etc/kvm /var/lib/kvm /var/log/kvm /mnt/storage0/kvm
# chmod -R o-rwx /usr/local/etc/kvm /var/lib/kvm /var/log/kvm /mnt/storage0/kvm
# chmod -R ug+rw /usr/local/etc/kvm /var/lib/kvm /var/log/kvm /mnt/storage0/kvm
# chmod -R o-rwx /usr/local/etc/kvm /var/lib/kvm /var/log/kvm /mnt/storage0/kvm