libvirt и qemu-kvm

Короче, я тут на днях беглым взглядом просматривал документацию на libvirt, на предмет того, как можно автоматизировать процесс извлечения ядра и initrd из файловой системы гостя. И оказалось, что libvirt несколько раз запускает файл с хуком, в строку запуска его отдавая параметры и xml’ку виртуальной машины. Для меня это стало, прямо скажем, открытием.

libvirt и qemu-kvm
И как мне видится принятие решений в RH

Во времена появления libvirt’а, модой на системы виртуализации правила VMware, фактически предлагавшая десктопную виртуализацию, отмасштабированную до размеров сервера. Основной целевой аудиторией были виндоводы, которым было лень иметь под не прожорливые сервисы отдельные физические машины. Да, в первую очередь речь шла именно о ссаживании на одной железке того, что нельзя мешать в рамках одной ОС, а не об обеспечении быстрой миграции и не о прочих «киллер-фичах виртуализации». Конечно этой публике нужно было управлять всем сервером виртуализации из GUI-гипервизора. Братия же адептов unix-систем, когда перед ними стояла задача организации изолированных вычислительных средств высокой плотности, тупо вкатывала Xen. Ибо уважающий себя одмен лучше любого GUI себе и сети, и пулы хранилищ запилит. Ну и гостевым системам нужна производительность, а не виртуализированный видеоадаптер.

У Xen’а тоже есть, конечно, недостатки. Но он предлагал правильную концепцию: в продакшне не надо полностью эмулировать PC для гостевых машин. Вероятность того, что в продакшн прилетит какая-то НЁХ, типа Colibri OS, которую не умеет загружать гипервизор без дополнительного загрузчика, близка к 0. Консоль виртуальной машины тоже доступна должна быть прямо из консоли сервера. Но у Xen’а была проблема с его архитектурой и с тем, что он не был включён в mainline…

Короче, этот подход я долго пытался реализовать в связке qemu (kvm) и libvirt. Проблема была в том, что как-то надо было автоматизированно забирать файлы ядра и initrd из файловой системы виртуальной машины. Как я только не извращался! Вплоть до написания собственной управляющей оснастки, работавшей вместо libvirt’а. Потом я устал и стал держать экземпляры ядер и рамдисков на ФС хост-машины. Идеальным было бы забирать ядро и рамдиск прямо перед запуском ВМ. Сделать это при автоматическом запуске виртуалок или при запуске через virt-manager не представлялось мне возможным. Но, внезапно, оказалось, что в libvirt есть механизм хуков: несколько раз при запуске и остановке вызывается скрипт, специфичный для целевого гипервизора, запускается он с разными параметрами, зависящими от момента запуска. На stdin скрипта отдаётся XML’ка с конфигом машины. Скрипт может быть на любом языке программирования, в т.ч. уже скомпилированный код.

За более подробной информацией вам стоит обратится на страницу с документацией. Для своих нужды я обрабатываю только вызовы

/etc/libvirt/hooks/qemu guest_name prepare begin -

и

/etc/libvirt/hooks/qemu guest_name release end -

Текст скрипта, собственно:

#!/usr/bin/perl

use strict;
use warnings;
use XML::Simple;
use File::Copy;
use File::Path qw(remove_tree);


my $mnt_root = "/mnt/qemu-boot";
my $tmp_root = "/tmp/qemu-boot";


if($#ARGV != 3) {
	die("Unexpected arguments count!\n");
}

my $stdin = "";
while(my $string = ) {
	$stdin .= $string;
}

my $xmlobj = XML::Simple->new(ForceArray => 1, KeepRoot => 1);
my $xmldat = $xmlobj->XMLin($stdin);

exit(0) unless(exists($xmldat->{'domain'}->[0]->{'os'}->[0]->{'cmdline'}));
exit(0) unless(ref($xmldat->{'domain'}->[0]->{'os'}->[0]->{'cmdline'}) eq "ARRAY");
exit(0) unless($#{$xmldat->{'domain'}->[0]->{'os'}->[0]->{'cmdline'}} == 0);
exit(0) unless($xmldat->{'domain'}->[0]->{'os'}->[0]->{'cmdline'}->[0] =~ /(^|\s)root=\/dev\/vda(\s|$)/);

if(!-d $mnt_root) {
	mkdir($mnt_root, oct(755)) or die("Can't create mnt_root dir: " . $! . "\n");
}

if(!-d $tmp_root) {
	mkdir($tmp_root, oct(755)) or die("Can't create tmp_root dir: " . $! . "\n");
}

my $mnt_dst = $mnt_root . "/" . $ARGV[0];
my $tmp_dst = $tmp_root . "/" . $ARGV[0];

if(!-d $mnt_dst) {
	mkdir($mnt_dst, oct(755)) or die("Can't create mnt_dst dir: " . $! . "\n");
}

if(!-d $tmp_dst) {
	mkdir($tmp_dst, oct(755)) or die("Can't create tmp_dst dir: " . $! . "\n");
}

my $target_dev = undef;
if(exists($xmldat->{'domain'}->[0]->{'devices'}->[0]->{'disk'})) {
	foreach my $disk (@{$xmldat->{'domain'}->[0]->{'devices'}->[0]->{'disk'}}) {
		if(($disk->{'type'} eq "block") and ($disk->{'target'}->[0]->{'dev'} eq "vda") and (exists($disk->{'source'}->[0]->{'dev'}))) {
			$target_dev = $disk->{'source'}->[0]->{'dev'};
		}
	}
}

die("Can't find target device in domain XML file!\n") unless($target_dev);

if(($ARGV[1] eq "prepare") and ($ARGV[2] eq "begin")) {
	system("mount", "-oro", $target_dev, $mnt_dst) and die("Can't mount target device (" . $target_dev . ")!\n");
	copy($mnt_dst . "/vmlinuz", $tmp_dst . "/vmlinuz") or die("Can't copy kernel file: " . $! . "\n");
	copy($mnt_dst . "/initrd.img", $tmp_dst . "/initrd.img") or die("Can't copy initrd file: " . $! . "\n");
	system("umount", $mnt_dst) and die("Can't umount target directory (" . $mnt_dst . ")!\n");
}
elsif(($ARGV[1] eq "release") and ($ARGV[2] eq "end")) {
	remove_tree($mnt_dst) or die("Can't directory " . $mnt_dst . ": " . $! . "!\n");
	remove_tree($tmp_dst) or die("Can't directory " . $tmp_dst . ": " . $! . "!\n");
}

exit(0);

Скрипт НЕ пытается производить никаких действий, если не найдёт в конфиге машины строку cmdline, содержащую параметр root=/dev/vda! То есть внедрение этого скрипта не ломает возможность работы с любыми гостями, лишь облегчая работу с заранее приготовленными для этого.

Конечно, логичным было бы и вовсе отказаться от qemu/kvm в пользу lxc. Там чрут с неймспейсами, зачем что-то большее, если все целевые гости — GNU/Linux-системы? Но нет. Пока нельзя обеспечить монтирование внутри контейнера ФС с разных блочных устройств с различными параметрами, — оно не надо. Ничто так не спасет сервер с Exim’ом от взлома, как /var/spool и /tmp, смонтированные с nodev,nosuid,noexec.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

− 2 = 1