PV-GRUB (не путать с заведомо костыльным pygrub) является основанным на GRUB 0.9x загрузчиком для паравиртуальных машин Xen, позволяющим загружать ядро ОС непосредственно из DomU (гостевого) окружения, что добавляет процессу запуска гостевой системы существенной независимости от хост-системы. Одним на наиболее крупных внедрений можно считать облачный
В данном вопросе всё было бы исключительно позитивно, если бы не вполне обычная для свободного ПО проблема: разработка данной ветки GRUB уже как несколько лет полностью остановлена в пользу GRUB2, а сообщество разработчиков Xen, судя по всему, в ближайшие годы основанной на актуальной версии GRUB замены PV загрузчику предоставить не готово. Сам PV-GRUB, несмотря на то, что является, в общем-то, частью официального дистрибутива Xen, в настоящее время уже исключён как минимум из соответствующих пакетов в репозиториях Debian и Ubuntu, а пользователи, привыкшие к удобствам, между тем требуют (Debian bug #588839: Include pv-grub to securely boot guest kernels) и требуют (Ubuntu bug #798583: xen-utils-4.1 does not have pvgrub)… Хотя лично для меня не очень понятна мотивация мейнтейнеров Xen пакетов, ссылающихся на некие невнятные проблемы с зависимостями, в то время как пакеты с GRUB 0.97 всё ещё доступны и работоспособны, да и к существующему функционалу претензий нет.
Здесь я постараюсь описать, пожалуй, наиболее приемлемое в настоящий момент решение насущной проблемы — самостоятельную сборку PV-GRUB и необходимую настройку DomU для его использования, благо никаких сверхъестественных усилий для этого не требуется.
Для заинтересованных в понимании подноготной реализации для паравиртуальной среды Xen почти традиционного загрузчика будет уместно сразу подбросить некоторых материалов про появившийся ещё в Xen 3.3 функционал stub domains. Краткое введение есть в блоге Xen.org (Xen 3.3 Feature: Stub Domains, Xen 3.3 Feature: HVM Device Model Domain). В остальном стоит полистать презентацию (PDF) и послушать доклад «Stub domains: A step towards Dom0 disaggregation», представленный на Xen Summit North America.
Для сборки загрузчика понадобится исходный код Xen. Лучше всего взять ту версию, которая уже применяется на вашей хост-системе. Поскольку я использую Debian GNU/Linux «Wheezy», для меня это будет Xen 4.1.4. Собирать можно на любой системе с минимальным девелоперским набором (gcc+binutils+make). С официального сайта Xen (Xen 4.1 series, Xen 4.2 series) скачиваем и распаковываем tarball с исходниками:
$ wget http://bits.xensource.com/oss-xen/release/4.1.4/xen-4.1.4.tar.gz $ tar xzf xen-4.1.4.tar.gz
Переходим в директорию stubdom
. Запускаем сборку загрузчика:
$ make grub
Если необходимо собрать загрузчик под архитектуру, отличную от системной, то её можно задать через переменную XEN_TARGET_ARCH:
$ XEN_TARGET_ARCH=x86_32 make grub
В процессе не обходится и без некоторых подводных камней. Сборка данного компонента останавливается на банальных ошибках включения некоторых заголовочных файлов по несуществующим путям.
In file included from xc_core.c:69:0: xc_core.h:26:35: fatal error: xen/libelf/elfstructs.h: Нет такого файла или каталога compilation terminated.
Ищем виновника:
$ find .. -type f -name xc_core.h ../tools/libxc/xc_core.h
Заменяем в нём строку
#include "xen/libelf/elfstructs.h"
на
#include "xen/elfstructs.h"
При повторном выполнении make grub
следует ещё несколько подобных:
xc_dom.h:17:31: fatal error: xen/libelf/libelf.h: Нет такого файла или каталога xc_hvm_build.c:34:31: fatal error: xen/libelf/libelf.h: Нет такого файла или каталога ../../xen/common/libelf/libelf-private.h:70:31: fatal error: xen/libelf/libelf.h: Нет такого файла или каталога
Все решаются заменой
#include <xen/libelf/libelf.h>
на
#include <xen/libelf.h>
Теперь сборка должна завершиться успешно. Последние строки вывода, которые вы скорее всего увидите, будут:
gzip -f -9 -c /home/user/xen-4.1.4/stubdom/mini-os-x86_64-grub/mini-os >/home/user/xen-4.1.4/stubdom/mini-os-x86_64-grub/mini-os.gz make[1]: Leaving directory `/home/user/xen-4.1.4/extras/mini-os'
Полученный уже сжатый файл mini-os-x86_64-grub/mini-os.gz
пожно переименовать в более понятное pv-grub-x86_64.gz
(или pv-grub-x86_32.gz
если собирали под x86_32), а затем положить в директорию /usr/lib/xen-4.1/boot
вашего Dom0, где ему самое место (справедливо для Debian при установленном пакете xen-utils-4.1, там же находится hvmloader
).
Перед непосредственным переключением на использование загрузчика стоит проделать некоторые подготовительные процедуры для вашей гостевой системы.
Стоит отметить, что старый GRUB непосредственным образом не дружит с современными файловыми системами вроде Ext4, не говоря уже о менее распространённых. Поэтому предварительно стоит убедиться, что раздел, где у вас будет размещаться образ ядра, initrd и конфигурационные файлы GRUB (по умолчанию всё хранится в директории /boot
), будет доступен загрузчику для чтения. Я в процессе прохождения описываемого здесь квеста уже наступил на грабли: несмотря на теоретически существующую обратную совместимость Ext4 с Ext2, существует немало различных расширений (features), которые эту совместимость полностью исключают, особенно если при создании вашего Ext4 раздела вы специально об этом не задумывались. Прежде создавать отдельный раздел под /boot
у меня не возникало особой необходимости. Сейчас же решением может быть как раз вынос вышеотмеченной стартовой троицы на микро-раздел (думаю, двойного объёма от текущего содержимого /boot
будет вполне достаточно), отформатированный в Ext2. Не забудьте также отредактировать /etc/fstab
вашего DomU, добавив туда /boot
раздел и изменив наименования блочных устройств остальных разделов, если это необходимо. Это же касается и конфигурации дисков самой виртуальной машины.
Проверить насколько существующий раздел годится для монтирования в режиме обратной совместимости с Ext2 можно элементарно через mount -t ext2
.
На самой виртуальной машине необходимо установить старую версию GRUB (пакет grub-legacy в Debian или максимально кастрированный grub-legacy-ec2 в Ubuntu). На запросы об установке GRUB в загрузочный сектор какого-либо раздела стоит отвечать отрицательно — это не имеет смысла.
После установки убедимся, что директория для хранения конфигурации GRUB существует и доступна для записи. Запуск update-grub
создаст файл меню загрузчика, добавив в список загрузки установленные в системе ядра.
$ mkdir /boot/grub $ update-grub Searching for GRUB installation directory ... found: /boot/grub Probing devices to guess BIOS drives. This may take a long time. Searching for default file ... Generating /boot/grub/default file and setting the default boot entry to 0 Searching for GRUB installation directory ... found: /boot/grub Testing for an existing GRUB menu.lst file ... Generating /boot/grub/menu.lst Searching for splash image ... none found, skipping ... Found kernel: /vmlinuz-3.2.0-4-amd64 Updating /boot/grub/menu.lst ... done
Заглянем в созданный /boot/grub/menu.lst
:
title Debian GNU/Linux, kernel 3.2.0-4-amd64 root (/dev/xvda1) kernel /vmlinuz-3.2.0-4-amd64 root=UUID=f5731cf7-420a-4094-acf7-bec5976a0b62 ro initrd /initrd.img-3.2.0-4-amd64 title Debian GNU/Linux, kernel 3.2.0-4-amd64 (single-user mode) root (/dev/xvda1) kernel /vmlinuz-3.2.0-4-amd64 root=UUID=f5731cf7-420a-4094-acf7-bec5976a0b62 ro single initrd /initrd.img-3.2.0-4-amd64
Здесь бросается в глаза, что конфигуратор неверно заполнил опцию root
, подставив вместо понятного GRUB обозначения диска и раздела имя файла блочного устройства. При выборе данных пунктов меню GRUB попросту будет ругаться на неверное значение опции.
Чтобы исправить ситуацию, немного модифицируем сгенерированный /boot/grub/menu.lst
добавив опцию groot
с явным указанием порядкового номера загрузочного «диска», который впоследствие монтируется как /boot
(поскольку полноценных дисков с таблицей разделов в паравиртуальной среде не существует):
groot=(hd0)
Тут же можно уменьшить таймаут перед началом загрузки, чтобы не тратить время понапрасну:
## timeout sec # Set a timeout, in SEC seconds, before automatically booting the default entry # (normally the first entry defined). timeout 1
Чтобы значение groot
корректно воспринималось генератором конфигурации update-grub
, дополнительно создадим файл /boot/grub/device.map
, куда впишем единственную строку, касающуюся упомянутого загрузочного раздела:
(hd0) /dev/xvda1
Теперь update-grub
должен сгенерировать корректный файл меню, работоспособность которого к тому же не будет нарушена, если вы захотите обновить версию основного ядра или добавить дополнительные.
title Debian GNU/Linux, kernel 3.2.0-4-amd64 root (hd0) kernel /vmlinuz-3.2.0-4-amd64 root=UUID=f5731cf7-420a-4094-acf7-bec5976a0b62 ro initrd /initrd.img-3.2.0-4-amd64 title Debian GNU/Linux, kernel 3.2.0-4-amd64 (single-user mode) root (hd0) kernel /vmlinuz-3.2.0-4-amd64 root=UUID=f5731cf7-420a-4094-acf7-bec5976a0b62 ro single initrd /initrd.img-3.2.0-4-amd64
Если необходимо передавать ядру какие-либо дополнительные параметры, которые прежде задавались непосредственно в конфигурации виртуальной машины через опцию extra
, то это можно сделать путём модификации kopt
в том же /boot/grub/menu.lst
.
В качестве финального аккорда остаётся переконфигурировать процесс запуска DomU: имя файла загрузчика необходимо указать в опции kernel
. Если образ загрузчика лежит в директории по умолчанию (там же, где и hvmloader
), то полный путь указывать не обязательно. В опции extra
указывается раздел и путь к файлу меню GRUB.
kernel = 'pv-grub-x86_64.gz' extra = '(hd0)/grub/menu.lst'
От прежних опций ramdisk
и root
необходимо избавиться.
Чтобы убедиться в полной работоспособности новой конфигурации, первый запуск DomU стоит производить в режиме автоподключения к консоли (xm start -c
). Если всё проделано верно, то вы увидите меню GRUB и последующий процесс загрузки ядра с монтированием корневого раздела.
Автор: Lux_In_Tenebris