KVM/Helper: различия между версиями
Klark (обсуждение | вклад) мНет описания правки |
Klark (обсуждение | вклад) мНет описания правки |
||
(не показана 1 промежуточная версия этого же участника) | |||
Строка 29: | Строка 29: | ||
=Будущее UML Helper'а= | =Будущее UML Helper'а= | ||
Для уменьшения образа основой должен быть [https://www.busybox.net/ BusyBox], слинкованный с '''glibc''', поскольку в конечном итоге это процесс на полноценной сборочной машине, а не Embedded-применение, к тому же, в составе BusyBox практически нет полноценных утилит для работы с файловыми системами и разметки дисков, их всё равно придётся дотягивать с зависимостями на glibc. Для создания образа '''initrd''' не должно быть зависимости от текущей реализации '''make-initrd''', это попросту чревато. Как вариант, можно рассчитывать на стабильные возможности make-initrd, которых в нём пока не реализовывалось. Модульность и конфигурируемость в прототипе '''UML Helper''''а | Для уменьшения образа основой должен быть [https://www.busybox.net/ BusyBox], слинкованный с '''glibc''', поскольку в конечном итоге это процесс на полноценной сборочной машине, а не Embedded-применение, к тому же, в составе BusyBox практически нет полноценных утилит для работы с файловыми системами и разметки дисков, их всё равно придётся дотягивать с зависимостями на glibc. Для создания образа '''initrd''' не должно быть зависимости от текущей реализации '''make-initrd''', это попросту чревато. Как вариант, можно рассчитывать на стабильные возможности make-initrd, которых в нём пока не реализовывалось. Модульность и конфигурируемость в прототипе '''UML Helper''''а также отсутствует, пока это скорее "'''Proof of Concept'''". | ||
=Отказ от stage2= | =Отказ от stage2= | ||
Строка 43: | Строка 43: | ||
2. В ядро всегда вкомпилируется пустой образ '''initrd''', это занимает считанные байты. Но в параметрах загрузки ядра можно указать альтернативный путь, и тогда ядро будет использовать указанный initrd. Если ядро не найдёт в initrd '''/init''', оно использует собственный fallback-механизм для обнаружения корневого устройства. При этом нигде не говорится, что содержимое initrd отбрасывается -- ведь кроме скриптов инициализации, там могут быть '''модули ядра и firmware'''. Выходит, если указать в параметрах загрузки всё необходимое для поиска относительно стандартного '''rootfs''', можно обойтись без скриптов и программ в initrd. Так ли это? | 2. В ядро всегда вкомпилируется пустой образ '''initrd''', это занимает считанные байты. Но в параметрах загрузки ядра можно указать альтернативный путь, и тогда ядро будет использовать указанный initrd. Если ядро не найдёт в initrd '''/init''', оно использует собственный fallback-механизм для обнаружения корневого устройства. При этом нигде не говорится, что содержимое initrd отбрасывается -- ведь кроме скриптов инициализации, там могут быть '''модули ядра и firmware'''. Выходит, если указать в параметрах загрузки всё необходимое для поиска относительно стандартного '''rootfs''', можно обойтись без скриптов и программ в initrd. Так ли это? | ||
По факту оказалось: devtmpfs действительно заполняется автоматически после загрузки нужных модулей. Возможно, если бы все необходимые модули уже | По факту оказалось: devtmpfs действительно заполняется автоматически после загрузки нужных модулей. Возможно, если бы все необходимые модули были бы уже вкомпилированы в ядро, без udev'а, скриптов инициализации и каких-либо утилит в user-space'е действительно можно было бы обойтись. Но в случае модульного ядра кто-то должен дать команду '''insmod''' ('''modprobe''') и он должен знать, какие модули нужно грузить на данной системе. А это значит: либо '''udev''', либо статическая загрузка модулей скриптом по заранее созданному списку. Разумеется, речь о надёжности и не о десктопном применении. ''Предположение от '''vt@''': если модули в ядро вкомпилированы, оно делает '''insmod''' по ним по всем, почему бы не сделать так же скриптом в '''initrd'''?'' | ||
=Как не следует использовать make-initrd= | =Как не следует использовать make-initrd= |
Текущая версия от 00:49, 3 февраля 2019
KVM Helper
Описание прототипа функционального аналога guestfs, собираемого пользователем в Hasher без жёстких ограничений пакетным менеджментом. Адрес проекта: http://git.altlinux.org/people/klark/packages/kvm-helper.git
Первоначальные цели проекта
- Проверить идею Сергея Большакова с отказом от stage2 "поместить всю систему в initrd".
- Провести эксперименты с devtmpfs без udev, при этом не отказываясь от модульного ядра.
- Минимальными усилиями создать рабочий прототип initrd (rootfs) для будущего UML-Helper'а.
Планируется его использовать в будущей "разбивалке дисков", как один из Helper'ов. В 2018 году уже использовался для создания нестандартных загрузочных образов и в автоматизированном процессе подготовки образов для ноутбуков подмосковных школ. В 2019 году станет частью системы массового развёртывания в качестве одного из под-проектов. Таким образом, даже в действующем виде "Proof of Concept" уже востребован в трёх моих проектах. Надеюсь, сгодится и вам!
Зачем это надо, когда уже есть guestfs?
guestfs ограничивается возможностями, закладываемыми апстримом и мэйнтейнером пакета. Но цели использования guestfs могут быть разными, сам образ guestfs должен быть как можно меньше. Если один человек убедит мэйнтейнера включить те или иные опции, другому уже не получится убедить его в обратном. Лично меня побудило заняться альтернативой куцее наполнение утилитами для работы с файловыми системами. Даже то, что было в образе, имело ограниченный функционал и не подходило для моих задач, а отрывать от серьёзной работы уважаемого человека было неудобно.
Поэтому я посчитал, что такой инструмент не должен иметь жёсткой конструкции. Напротив, любой пользователь должен иметь возможность создать себе образ за считанные секунды по заранее определённой конфигурации, иметь возможность эту конфигурацию легко изменять под свои нужды. Второй причиной является то, что guestfs определяет собственную систему команд, тогда как для всех моих задач было предпочтительно сохранить оригинальную семантику, определяемую стандартными утилитами для разметки дисков и работы с файловыми системами.
Зачем это надо вообще?
Hasher не обеспечивает возможности для прямой работы с накопителями (дисками), loop-устройствами, не позволяет монтировать что-то такое в публичной сборочнице. Схожие ограничения есть и у контейнеров. Всё перечисленное реализуется средствами полноценной виртуальной машины, и, что очень важно, для этого пользователю не нужно получать привилегии root. Скрипты для сборки или подготовки специальных образов могут работать в достаточно изолированной виртуальной среде и пользователю не надо бояться, что они испортят что-то в его физической host-системе. Однако полноценная виртуализация -- это довольно большие накладные расходы. Поэтому такая вспомогательная виртуалка должна быть минималистична с точки зрения наполнения.
Несколько слов о названии
На данный момент используется qemu-kvm, в идеале же стоит перейти на kvm-tools, поэтому "KVM Helper". Если возможности железа ограничены, можно отказаться от KVM, тогда логичнее это назвать "QEMU Helper". Первоначальное название "UML Helper" несёт в себе лишь общий смысл -- User-Mode Linux используется как принцип, но не как конкретная одноимённая технология виртуализации в ядре.
Будущее UML Helper'а
Для уменьшения образа основой должен быть BusyBox, слинкованный с glibc, поскольку в конечном итоге это процесс на полноценной сборочной машине, а не Embedded-применение, к тому же, в составе BusyBox практически нет полноценных утилит для работы с файловыми системами и разметки дисков, их всё равно придётся дотягивать с зависимостями на glibc. Для создания образа initrd не должно быть зависимости от текущей реализации make-initrd, это попросту чревато. Как вариант, можно рассчитывать на стабильные возможности make-initrd, которых в нём пока не реализовывалось. Модульность и конфигурируемость в прототипе UML Helper'а также отсутствует, пока это скорее "Proof of Concept".
Отказ от stage2
Действительно не во всех ситуациях нужна двухстадийная загрузка. Так, список всего "железа" для виртуалки известен заранее. Нынешний инструментарий ALT практически не приспособлен создавать системы, работающие в stage1. Тем не менее, данный проект показывает общий принцип и что это всё-таки возможно!
Про эксперименты с devtmpfs без udev
Документация к ядру объявляет два интересных постулата, которые хотелось пощупать на практике:
1. devtmpfs работает независимо от udev, инициализируется на ранней стадии и наполняется по мере обнаружения устройств. Единственное, чего не окажется в /dev без udev, это каких-то особых прав, все устройства будут иметь права доступа 0600 и принадлежать root'у. Зачем тогда, спрашивается, костыль под названием udev?
2. В ядро всегда вкомпилируется пустой образ initrd, это занимает считанные байты. Но в параметрах загрузки ядра можно указать альтернативный путь, и тогда ядро будет использовать указанный initrd. Если ядро не найдёт в initrd /init, оно использует собственный fallback-механизм для обнаружения корневого устройства. При этом нигде не говорится, что содержимое initrd отбрасывается -- ведь кроме скриптов инициализации, там могут быть модули ядра и firmware. Выходит, если указать в параметрах загрузки всё необходимое для поиска относительно стандартного rootfs, можно обойтись без скриптов и программ в initrd. Так ли это?
По факту оказалось: devtmpfs действительно заполняется автоматически после загрузки нужных модулей. Возможно, если бы все необходимые модули были бы уже вкомпилированы в ядро, без udev'а, скриптов инициализации и каких-либо утилит в user-space'е действительно можно было бы обойтись. Но в случае модульного ядра кто-то должен дать команду insmod (modprobe) и он должен знать, какие модули нужно грузить на данной системе. А это значит: либо udev, либо статическая загрузка модулей скриптом по заранее созданному списку. Разумеется, речь о надёжности и не о десктопном применении. Предположение от vt@: если модули в ядро вкомпилированы, оно делает insmod по ним по всем, почему бы не сделать так же скриптом в initrd?
Как не следует использовать make-initrd
make-initrd умеет делать несколько полезных вещей: определять модули ядра, необходимые для монтирования корня на конкретной системе, строить зависимости одних модулей ядра от других, строить всю цепочку библиотечных зависимостей для помещаемых в образ программ. Есть в нём и другие полезные вещи, но мне совершенно точно не требовалось помещать в initrd скрипты legion@ самого make-initrd вместе со всей udev-обвязкой, да и реализовывать повторно все эти полезные фишки тоже было лениво. Пришлось на лету менять пару скриптов от текущей версии make-initrd и целый каталог удалять, заменяя своим. Это весьма хлипкая конструкция, понятное дело, не для долгоживущего решения. Не стоит повторять такое в production!
Инструкция по применению
На рабочей машине (x86_64) должны быть установлены и настроены git-core, hasher, qemu-system-x86_64. Убедитесь, что в BIOS'е включена технология KVM и загружены соответствующие модули ядра (lsmod | grep kvm). Иначе придётся подправить скрипт runme1st под свою конфигурацию хост-системы. В скриптах также используются такие утилиты, как realpath и readlink. По умолчанию работа ведётся на tmpfs, а созданная виртуалка запускается с параметром -m 2048, т.е. ей потребуется 2G ОЗУ, что можно поменять в том же скрипте.
Клонируем к себе репозиторий
$ git clone git://git.altlinux.org/people/klark/packages/kvm-helper.git Cloning into 'kvm-helper'... remote: Counting objects: 39, done. remote: Compressing objects: 100% (31/31), done. remote: Total 39 (delta 0), reused 39 (delta 0) Receiving objects: 100% (39/39), 11.56 KiB | 0 bytes/s, done.
Переходим в созданный каталог
$ cd kvm-helper
Меняем настройки под свои нужды
$ mv local.conf.example local.conf $ $EDITOR local.conf
Менять можно всё, что определено по умолчанию (head -n18 mkhelper). Хотя, скорее всего, ввиду зависимости от текущей версии make-initrd, это будет работать только с Сизифом, так что branch лучше не менять.
Запускаем всё одной командой
$ ./runme1st
Обращаем внимание на размер получившегося образа
$ du -csh .work/results/{vmlinuz,initrd.img} 4,8M .work/results/vmlinuz 11M .work/results/initrd.img 16M итого
Обращаем внимание (см. скриншот), что удаление модулей освобождает 2Мб из занятых 43Мб. Программ понапихано чересчур много и есть ещё, куда оптимизировать. Листинг содержимого лежит рядышком: .work/results/initcpio.
В дальнейшем, чтобы запустить всё с нуля, используем ./runme1st --clean. Повторный запуск этого скрипта без параметров не приведёт к пересборке ранее созданного образа. Принудительно пересобрать образ без запуска самой виртуалки можно вторым скриптом: ./mkhelper. Исходников не так много, только чтобы показать общий принцип, так что внесение изменений будет не сложным делом. Обращу лишь внимание на модули ядра. Если в файл features/base-system/files.list добавить строчку /etc/modules, будут загружены модули всех виртуальных устройств Qemu/KVM, попадающих в прошивку:
GUEST> lsmod Module Size Used by sd_mod 49152 0 joydev 24576 0 iTCO_wdt 16384 0 virtio_rng 16384 0 iTCO_vendor_support 16384 1 iTCO_wdt rng_core 16384 1 virtio_rng 9pnet_virtio 20480 1 virtio_balloon 20480 0 9p 61440 1 9pnet 86016 2 9p,9pnet_virtio ahci 40960 0 libahci 40960 1 ahci i2c_i801 32768 0 libata 278528 2 libahci,ahci i2c_smbus 16384 0 evdev 28672 0 input_leds 16384 0 qemu_fw_cfg 16384 0 psmouse 147456 0 i2c_core 77824 3 i2c_smbus,i2c_i801,psmouse serio_raw 16384 0 button 16384 0 scsi_mod 253952 2 sd_mod,libata pcspkr 16384 0 lpc_ich 28672 0 intel_agp 24576 0 intel_gtt 24576 1 intel_agp virtio_pci 28672 0 virtio_ring 28672 4 virtio_rng,virtio_balloon,9pnet_virtio,virtio_pci virtio 16384 4 virtio_rng,virtio_balloon,9pnet_virtio,virtio_pci
Взаимодействие между хостовой и гостевой системами
Хотя в примере и на скриншоте запускается оболочка в интерактивном режиме, задача KVM Helper'а безусловно в другом: выполнять внутри виртуалки не интерактивные команды, определяемые в хост-системе. Замените содержимое скрипта guest и посмотрите, что получится. Ещё более интересным видится вариант разового запуска KVM Helper'а со скриптом, организующем собственный цикл исполнения запросов, которые могут поступать через ту же plan9-шару в виде отдельных команд или скриптов. Пример есть в руководстве по Rescue/Launcher'у, только там полноценная Rescue-система с двухстадийной загрузкой, которая весит 700Мб в сжатом виде, здесь же 16Мб в сжатом, а реально -- до 43Мб в ОЗУ, и временем от запуска до выключения в 1.6 секунды (скрипт set_hwclock замедляет загрузку для наглядности, на самом деле его лучше убрать). И ещё раз: приблизить начало выполнения полезной нагрузки и снизить загрузку процессора до нуля можно организацией собственного цикла обработки запросов.
Enjoy! ;-)