Содержание статьи
Введение
Поддержка аппаратной виртуализации (в качестве хостовой системы) во FreeBSD появилась недавно — первое упоминание о bhyve встречается в новостях 2011 года. С одной стороны, в других ОС эта поддержка гораздо более зрела. С другой же — при разработке bhyve были учтены некоторые моменты, которые могли возникнуть, разрабатывайся он с нуля. Кроме того, чтобы не плодить сущности, в гостевых системах используется virtIO (как и в KVM и Xen).
А вот технология для создания контейнеров во FreeBSD существует с давних пор под названием Jail. В основе ее лежит группа системных вызовов (jail(), jail_get(), jail_set(), jail_remove() и jail_attach()). Вопреки распространенному мнению, в последних версиях FreeBSD они не вызывают chroot(), хотя и используют некоторые общие с данным системным вызовом процедуры. Из преимуществ Jail, помимо собственно изоляции, можно отметить также гибкое управление ресурсами с помощью RCTL (для включения данной функции, однако, требуется перекомпиляция ядра).
Посмотрим, насколько удобно использовать данные технологии по сравнению с их аналогами из других ОС.
Установка FreeBSD на bhyve
Для использования данной системы виртуализации необходима версия FreeBSD не ниже 10 (а с процессором AMD — не ниже 11-CURRENT). Чтобы ее включить, нужно добавить следующие строки в файл boot/loader.conf:
if_bridge_load="YES"if_tap_load="YES"vmm_load="YES"
После перезагрузки (или добавления данных модулей с помощью kldload) нужно создать бридж и интерфейс tap для поднятия сети в будущей виртуальной машине:
# ifconfig bridge0 create# ifconfig tap0 create# sysctl net.link.tap.up_on_open=1
Затем объединяем tap0 и реальный интерфейс в бридж и запускаем его — в моем случае для этого потребовались следующие команды:
# ifconfig bridge0 addm em0 addm tap0# ifconfig bridge0 up
Создаем файл образа, куда будем ставить систему:
# truncate -s 6g fbsd101.img
Затем либо качаем bootonly-образ, либо используем образ DVD, который ты применял при установке FreeBSD (файл устройства по неизвестным причинам для этих целей не подходит). Копируем скрипт, облегчающий запуск виртуальной машины, и редактируем в нем нужные параметры:
# cp /usr/share/examples/bhyve/vmrun.sh bhyverun.sh# chmod +x bhyverun.sh
Скрипт довольно большой и делает следующее:
- Проверяет, является ли вызывающий пользователь привилегированным.
- Проверяет, загружен ли модуль vmm.ko.
- Смотрит задаваемые опции.
- Проверяет, является ли файл жесткого диска загрузочным образом.
- Наконец, загружает с помощью bhyveload ядро FreeBSD и передает ему управление через bhyve.
О последнем этапе стоит рассказать подробнее. Поскольку bhyve пока не поддерживает ни BIOS, ни UEFI, а инициализировать виртуальное оборудование все же необходимо, для этого был сделан модифицированный загрузчик для FreeBSD. Аналогично есть и bhyve-модифицированный GRUB 2, служащий для загрузки иных систем. Но вот ты его отредактировал. Теперь можно и запускать:
# ./bhyverun.sh fbsd101
Устанавливаем стандартным образом. Для нормального завершения работы нужно перезагрузиться и в приглашении loader’а набрать quit.
Вместо использования файла образа можно взять функцию ZFS под названием zvol. Прежде всего создадим этот самый zvol:
# zfs create -V 6g zroot/fbsd
Устанавливаем FreeBSD обычным путем, указав вместо файла образа zvol (в моем случае — /dev/zvol/zroot/fbsd), метод разбиения выбираем «ZFS - Automatic Root-on-ZFS». После установки для запуска вводим примерно следующие две команды (так как скрипт не всегда корректно работает):
# bhyveload -m 2G -d /dev/zvol/zroot/fbsd fbsd# bhyve -c 1 -m 2G -A -H -P -s0:0,hostbridge -s 1:0,virtio-net,tap0 -s 2:0,ahci-hd,/dev/zvol/zroot/fbsd -s 31,lpc -l com1,stdio fbsd
И работаем как обычно.
Режим совместимости с Linux
FreeBSD поддерживает частичную совместимость с Linux, что позволяет запускать некоторые Linux-приложения. Фактически режим совместимости представляет собой реализацию Linux ABI и системных вызовов. Для установки слоя совместимости нужно выполнить следующую команду:
# pkg install linux_base-c6
Затем загрузить модуль, отвечающий за ABI и подмонтировать linprocfs:
# kldload linux# mount -t linprocfs linproc /compat/linux/proc/
Все. Можно использовать некоторые программы, написанные для Linux.
Bhyve: установка Linux
Для установки Linux потребуется установить grub2-bhyve:
# pkg install grub2-bhyve
Затем нужно скачать образ дистрибутива (я взял CentOS 7) и создать файл device.map и образ диска.
# fetch http://centos-mirror.rbc.ru/pub/centos/7.0.1406/isos/x86_64/CentOS-7.0-1406-x86_64-Minimal.iso# mv CentOS-7.0-1406-x86_64-Minimal.iso centos.iso# truncate -s 6g cebtos.img
Содержимое device.map:
(hd0) ./centos.img
(cd0) ./centos.iso
Запускаем grub-bhyve и набираем соответствующие команды для загрузки ядра:
# grub-bhyve -m device.map -r cd0 -M 2048M centos-bhyvegrub> linux (cd0)/isolinux/vmlinuz text
grub> initrd (cd0)/isolinux/initrd.img
grub> boot
Здесь нужно объяснить некоторые детали. Поскольку bhyve пока не поддерживает графический режим, нужно запустить установку в текстовом, для чего и передаем ядру параметр text. Кроме того, команда boot ядро, конечно, в память загружает — но управление не передает. Для передачи же управления используется следующая команда:
# bhyve -A -H -P -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0 -s 3:0,virtio-blk,./centos.img -s 4:0,ahci-cd,./centos.iso -l com1,stdio -c 1 -m 2048M centos-bhyve
Разберем опции:
- -A — генерировать таблицы ACPI (в которых хранится, например, количество процессоров);
- -H — реализует инструкцию HLT в виртуальном CPU. В случае если эту опцию не указать, реальный процессор будет загружен на 100%;
- -P — приостанавливает VCPU по получении им инструкции PAUSE;
- множество опций -s конфигурируют слоты PCI. После первой запятой указывается, какое устройство будет эмулироваться или пробрасываться. После второй указывается конфигурация для данного устройства — так, в примере выше для virtio-blk указан путь к образу. При необходимости для сетевого интерфейса таким же способом можно указать MAC-адрес;
- -l — предназначено для маппинга COM-портов;
- -c — количество виртуальных процессоров;
- -m — память.
Помимо указанных, есть еще и дополнительные опции:
- -C — включать память гостевой ОС в файлы дампа;
- -e — принудительно завершать работу bhyve, если гостевая ОС обращается к неэмулируемым портам ввода‑вывода — что полезно для целей отладки;
- -p vcpu:hostcpu — привязать указанный VCPU к CPU хостовой машины;
- -U uuid — уникальный идентификатор в структуре SMBIOS System Information. Если его не указать, генерируется на основе имени хоста хостовой системы и имени виртуальной машины;
- -w — игнорировать обращения к нереализованным регистрам MSR, что опять же полезно для отладки;
- -W — использовать в эмулируемой системе старую систему прерываний (MSI) вместо более новой (MSI-X);
- -x — использовать в гостевой системе режим x2APIC;
- -Y — отключить генерацию MPTable.
После загрузки запустится Anaconda в самом что ни на есть текстовом режиме (даже без использования ncurses — просто черно‑белый терминал, навевающий ностальгические мысли о временах DOS). Установка более чем стандартна. После ее завершения нас выбросит во FreeBSD — bhybe не умеет перезагружаться. Для дальнейшей работы нужно завершить работу bhyve, снова запустить grub-bhyve и вновь использовать ту же команду:
# bhyvectl --vm=centos-bhyve --destroy# grub-bhyve -m device.map -r hd0,msdos1 -M 2048M centos-bhyvegrub> configfile /grub2/grub.cfg
Выбираем нужный пункт и передаем управление командой bhyve с соответствующими опциями.
Но что, если нужно подключаться к консоли и отключаться от нее? В этом случае можно использовать нуль‑модемное устройство, для которого прежде всего необходимо загрузить модуль ядра nmdm:
# kldload nmdm
Затем в опции -l указываем вместо stdio /dev/nmdm0A и подключаемся к нему:
# cu -l /dev/nmdm0B -s 115200
Кроме того, при создании виртуальной машины создается также и файл в каталоге /dev/vmm, что позволяет быстро посмотреть список загруженных машин.
Настройка Jail
FreeBSD поддерживает также аналог LXC — Jail. Мы будем рассматривать его вкупе с ZFS. Для начала создадим файловую систему:
# mkdir /usr/jail# zfs create -o compress=lz4 -o mountpoint=/usr/jail zroot/jail# zfs create zroot/jail/.base101x64
Вторая команда создает сжатую ZFS, а третья — собственно ту ФС, куда мы будем его развертывать и откуда затем клонировать. Скачиваем файлы FreeBSD 10.1 и распаковываем их:
# cd /var/tmp# fetch ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/10.1-RELEASE/base.txz# fetch ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/10.1-RELEASE/lib32.txz# fetch ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/10.1-RELEASE/games.txz# fetch ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/10.1-RELEASE/src.txz# tar -JxvC /usr/jail/.base101x64/ -f base.txz# tar -JxvC /usr/jail/.base101x64/ -f lib32.txz# tar -JxvC /usr/jail/.base101x64/ -f games.txz# tar -JxvC /usr/jail/.base101x64/ -f src.txz
Сделаем некоторую базовую настройку (скопируем resolv.conf, поставим пароль, создадим каталог /usr/home и симлинк /home, установим обновления):
# cp /etc/resolv.conf /usr/jail/.base10x64/etc/# chroot /usr/jail/.base10x64Jail# passwd
Jail# mkdir /usr/ports
Jail# mkdir /usr/home
Jail# ln -s /usr/home /home
Jail# cd /etc/mail
Jail# make aliases
Jail# freebsd-update fetch install
Создадим снапшот текущего «чистого» Jail.
# zfs snapshot zroot/jail/.base101x64@clean
Конфигурируем шаблон дальше. Создадим в Jail следующий файл — /etc/make.conf:
# Использовать PKFNG для сборки портовWITH_PKGNG=yes# Делает возможным использование в Jail дерева портов хостовой системы (через проброс их с помощью mount)WRKDIRPREFIX=/var/ports
DISTDIR=/var/ports/distfiles
PACKAGES=/var/ports/packages
INDEXDIR=/usr/ports
# Количество одновременных потоков makeMAKE_JOBS_NUMBER=2
В файле /etc/rc.conf (в Jail) должно быть примерно следующее:
# Не запускаем sendmailsendmail_enable="NO"# Очищаем временные каталогиclear_tmp_enable="YES"# Syslog не будет принимать входящие подключенияsyslogd_flags="-ss"# Выключаем RPCBINDrpcbind_enable="NO"#### Services##sshd_enable="NO"
На данном этапе можно также добавить все настройки и программы, которые необходимо иметь во всех песочницах. После этого выходим из chroot и на хостовой системе снова делаем снапшот:
# zfs snapshot zroot/jail/.base101x64@p0
Здесь p0 — номер условного «патча».
На хостовой же системе создаем файл /etc/jail.conf следующего содержания:
# Глобальные параметры# Действие, выполняемое перед запуском песочниц. В данном случае пробрасывается каталог /usr/ports хостовой системы. Обрати внимание — триггеры exec.prestart и exec.poststop выполняются на хостовой системеexec.prestart = "/sbin/mount -t nullfs -o ro /usr/ports/ /usr/jail/$name/usr/ports";# Скрипты в Jail, выполняемые во время запуска и остановкиexec.start = "/bin/sh /etc/rc";exec.stop = "/bin/sh /etc/rc.shutdown";# После остановки отмонтируем проброшенную файловую системуexec.poststop = "/sbin/umount -f /usr/jail/$name/usr/ports";# Сбрасываем в Jail почти все переменные окруженияexec.clean;# Монтируем в Jail devfsmount.devfs;# Файл в формате fstab, описывающий, какие ФС монтировать в Jailmount.fstab = "/etc/fstab.$name";# Запрещаем монтирование в самом Jailallow.nomount;# Параметры подстановки# Путь к корню основан на имени Jailpath = "/usr/jail/$name";# Тестовый Jailtest { # Устанавливаем имя хоста для Jail host.hostname = "testjail"; # Включаем отдельный сетевой стек (требуется, чтобы ядро было скомпилировано с опцией VIMAGE) vnet = "new" interface = "lo0";}
Затем создаем на основе того снапшота, что был ранее сделан, клон:
# zfs clone zroot/jail/.base101x64@p0 zroot/jail/test
И в /etc/rc.conf добавим строчку (если нужно, чтобы созданные Jail запускались автоматически):
jail_enable="YES"
Для запуска же определенного Jail вручную набираем команду:
# /etc/rc.d/jail test start
где test — запускаемая песочница.
Чтобы запустить какую‑нибудь программу в песочнице, используем следующие команды:
# jls# jexec 5 csh
Первая команда показывает список запущенных песочниц, вторая же запускает в песочнице с указанным ID заданную программу. Останавливают их аналогично запуску, только вместо аргумента start пишем соответственно stop.
info
Одно время развивался проект FreeBSD VPS — альтернатива Jail; но сейчас про него ничего не слышно.
Тонкая настройка Jail. Интересные особенности
Конфигурация, приведенная выше, конечно же, может использоваться и на практике. Тем не менее всех возможностей песочницы она не охватывает. Посмотрим, какие еще возможности имеются у данной технологии. Поскольку песочницы неразрывно связаны с ограничениями, соответственно, и разбирать будем данный функционал.
Большинство параметров, настраиваемых в файле jail.conf, на самом деле являются параметрами sysctl (ветвь security.jail), поэтому описываемые ниже опции могут быть изменены также с помощью данной утилиты.
Параметр securelevel служит для ограничения securelevel в Jail. Отмечу, что значение данного параметра не может быть ниже, чем в хостовой системе (оно и понятно). Стоит напомнить, что ограничивает тот или иной уровень:
- 0 — разрешено все; фактически это уровень по умолчанию;
- 1 — запрещается изменение флагов файлов с помощью chflags, запись в файлы дисковых устройств (при смонтированных ФС) и в некоторые другие и загрузка модулей ядра (последнее, впрочем, в jail-окружении запрещено и так). Кроме того, запрещается вызов отладчика ядра;
- 2 — помимо запретов предыдущего уровня, писать в файлы дисковых устройств может только системный вызов mount и запрещено изменять время больше, чем на секунду;
- 3 — те же ограничения, что и на предыдущем, плюс запрет на изменение правил брандмауэра.
Параметр devfs_ruleset указывает, какой набор правил devfs будет применяться для контейнера. Везде пишут, что по умолчанию ни единого набора не применяется, — и при использовании команды /sbin/jail все верно. Однако в случае со стандартным скриптом запуска /etc/rc,d/jail это не так — применяется набор правил, заточенный специально для Jail и описанный в /etc/defaults/devfs.rules.
Во FreeBSD поддерживаются также вложенные контейнеры. Для их использования надо установить параметр children.max, указывающий максимальное количество вложенных контейнеров. По умолчанию он равен нулю, то есть вложенные контейнеры создавать запрещается. Текущее же количество потомков можно посмотреть в переменной children.cur.
С помощью параметра enforce_statfs можно управлять видимостью точек монтирования. При установке его в 0 процесс может получать информацию обо всех точках монтирования. При установке в 1 — только о точках монтирования в самой песочнице. Значение же по умолчанию, 2, и вовсе ограничивает информацию подмонтированным корнем.
Параметры, начинающиеся с «allow.», булевы и в подробном описании не нуждаются. Упоминания стоит разве что allow.raw_sockets — без данного параметра некоторые утилиты, такие как ping, работать не будут. Кроме того, при добавлении префикса no после «allow.» значение параметра инвертируется. К примеру, allow.nomount запрещает какое‑либо монтирование. Это имеет смысл, если в секции параметров для всех контейнеров действие разрешено, но для определенного его необходимо запретить. По умолчанию почти везде стоит запрет — исключение составляет лишь allow.set_hostname.
Если нужно поставить в Jail более старую версию FreeBSD, необходимо будет установить переменную UNAME_r соответствующе данной версии — в противном случае некоторые программы могут отказаться работать.
CBSD
Для облегчения создания Jail’ов можно использовать набор утилит CBSD — удобный фронтенд для работы с контейнерами, последние версии которого к тому же поддерживают и bhyve. Его особенности:
- наличие готового репозитория — что делает пересборку FreeBSD необязательной;
- поддержка ZFS;
- экспорт/импорт контейнеров;
- утилита конфигурирования в стиле bsdinstall.
И многое другое. Для установки нужно выполнить следующую команду:
# pkg install cbsd
После этого нужно его инициализировать:
# env workdir="/usr/jail" /uar/local/cbsd/sudoexec/initenv
Скрипт задаст с десяток вопросов, после чего можно будет запускать утилиту jconstruct-tui:
# /uar/local/cbsd/tools/jconstruct-tui
Появится текстовое диалоговое окно, в котором нужно указать параметры вновь создаваемой песочницы, такие как имя, IP-адрес, версия FreeBSD... После создания можно использовать следующую команду для запуска Jail:
# cbsd jcontrol-tui
У данного ПО есть еще множество возможностей, которые мы здесь описывать не будем. В целом функционал аналогичен раннему Docker — но, конечно, проекту до него еще расти и расти.
Заключение
Технологии виртуализации во FreeBSD производят двойственное впечатление. С одной стороны, да, они есть, и с их помощью можно даже что‑то запускать. С другой же...
Не будем сильно критиковать bhyve — в конце концов, это относительно недавняя разработка. Но помилуйте — даже абсолютно текстовые варианты ОС (которыми, к слову, обладают только *nix-подобные, да и то не все) устанавливать исключительно с использованием эмулируемого нуль‑модемного соединения крайне неудобно. Хотя бы по той причине, что на другую виртуальную консоль не переключишься — а зачастую там появляется полезная информация. Помимо того, опции самой команды bhyve предназначены скорее для внутреннего применения его разработчиков, чем для повседневного использования в качестве средства виртуализации.
В случае с Jail дела обстоят получше — но тоже несколько отстают от аналогичных технологий в Linux. Ярчайший пример этого — во FreeBSD, начиная с версии 7.0, поддерживается такая возможность, как privileges, аналогичная capabilities в Linux. Однако, несмотря на то что сама реализация системных вызовов, относящихся к Jail, в своем исходном коде ими оперирует, интерфейса, позволяющего администратору контейнеров определить, какие именно привилегии будут доступны суперпользователю того или иного контейнера, не существует.
Таким образом, технологии аппаратной виртуализации во FreeBSD еще недостаточно стабильны, а контейнеры (как бы они ни назывались) выглядят рабочими, хотя и немного старомодными — что, справедливости ради, никак не мешает их использовать.