Сборка и развертывание образа бездискового клиента с корнем на nbd

nbd - это тип устройства, а также прикладной сетевой протокол в linux, который позволяет работать с блочными устройствами (либо файлами как блочными устройствами) по сети.

Ближайший аналог - iSCSI, но есть некоторые отличия. Например, nbd внутри значительно проще, протокол предусматривает набор команд для блочного устройства, например чтение, запись, указание смещения от начала устройства. iscsi же реализует внутри себя весь его (scsi) функционал - например определение типа устройства, копирование между устройствами, работа с несколькими устройствами по одному каналу, и т.д.

Ещё один аналог - AoE, но опять же есть отличия. AoE работает на одном уровне с ip, что уменьшает накладные расходы, но позволяет использовать его только в пределах одной подсети, т.к. он немаршрутизируемый. AoE не гарантирует целостность передаваемых данных и зависит от надежности оборудования.

Бездисковая загрузка может быть организована двумя основными способами:

  • с использованием сетевой ФС
  • с использованием блочных устройств

Поскольку nbd подразумевает использование второго варианта, возникает вопрос как лучше организовать установку ОС на это самое блочное устройство, а также её (ОС) настройку и обновление впоследствии. Во-первых необходимо определится, что это будет за устройство: {диск, раздел на диске, файл}. Во-вторых - определится с методом установки ОС для клиента: {chroot, установка в виртуалке, установка на реальном железе}.

Я лично - использую обычный файл в качестве устройства и виртуалку1. Почему именно так:

Во-первых, ставить современный "user-friendly" дистрибутив через chroot - то ещё удовольствие, не рассчитаны они на это. (для примера - попробуйте поставить 12й минт с корнем внутри lvm). Опять же, если есть инсталлер, который значительно экономит время - почему бы его не использовать.

Во-вторых, почему именно файлы, а не разделы на диске или тома lvm - да, работает медленнее, но значительно проще управление и перенос на другой компьютер. Типичная моя виртуалка - директория с двумя файлами - сам «диск» и shell-скрипт запуска. При использовании в качестве формата диска qcow или что-то подобное - место расходуется только по необходимости. Например, на 30-гиговом разделе спокойно умещаются 6 виртуалок с типовыми дисками по 8Гб. Впрочем, я отвлекся.

Подготовка клиента

Создаем файл диска минимально необходимого размера.

qemu-img create diskless.raw 4G

На самом деле, можно использовать любую виртуалку и любой поддерживаемый её формат, но raw-формат позволяет лазить внутрь образа с помощью loop-устройства. В другие распространенные форматы - тоже можно, но уже при помощи qemu-nbd, что требует установленного qemu.

Запускаем в минимальной конфигурации, добавить опций по вкусу:

qemu -vga std -m 512M       -net nic,macaddr=52:54:00:XX:XX:XX       -hda "diskless.raw"       -cdrom "install-cd.iso"

Обратите внимание, что сетевой адаптер создается с определенным mac-адресом, иначе он может меняться при каждом запуске. Ставим систему как обычно, в минимально возможной конфигурации. Несколько рекомендаций:

  • initrd собирается с полным набором модулей для сетевых карт, всё остальное безжалостно выкидывается. Если это не настраивается - собирается со всеми модулями. Проследите, чтобы туда попал модуль nbd.
  • размер свопа - подбирается в зависимости от наличия памяти на клиентах, чем её больше, тем своп меньше.
  • в качестве загрузчика - syslinux или lilo2, функционал grub'а здесь нафиг не нужен.
  • чем меньше разделов - тем лучше, снижается вероятность нехватки места где-либо, lvm - ещё одна прослойка, добавляющая тормозов.
  • планировщик для «диска» с nbd - noop, серверная часть сама разберется как и когда ей писать на диск.
  • отключайте NetworkManager и ему подобные для основного интерфейса, во избежание выстрела себе в ногу. Если возможно - не ставьте совсем.
  • ничего лишнего в демонах и автозагрузке.

После установки - делаем бэкап диска, настраиваем/обновляем систему и делаем второй бэкап. После этого можно готовить систему к работе по сети. На что нужно обратить внимание при настройке:

  • в fstab - корень вида "none / auto defaults,relatime 0 0", все остальные разделы - подключаются по uuid или по метке. Это позволит использовать систему как в виртуалке, так и в развернутом виде. root= и rootfstype= указываются в параметрах загрузчика.
  • проследите, чтобы сеть не опускалась при перезагрузке/выключении, иначе вы получите kernel-panic вместо ожидаемого.
  • проследите, чтобы сетевые интерфейсы не менялись местами (если их несколько) и сразу после загрузки удалялись файлы hal'а/udev'а, отвечающие за их наименование. Обычно это что-то вида *-persistent-net-*.rules в /{etc,lib}/{hal,udev}/. Иначе получите eth(X+1) вместо ожидаемого eth(X). А ещё лучше - перевесьте интерфейс виртуалки, где это всё собирается - на какой-нибудь eth5. Примерное содержание фала /etc/udev/rules.d/70-persistent-net.rules:

    SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?", ATTR{address}=="aa:bb:cc:dd:ee:ff", ATTR{dev_id}=="0x0", ATTR{type}=="1", КERNEL=="eth", NAME="eth5"

TODO: дописать этот раздел

Развертывание

Нужна машина с dhcp, tftp и nbd сервером. Канал до свитча с клиентами - желательно гигабитный, но и 100-мегабитного, по моим наблюдениям, машин на 10 должно хватить. 3.

Требования к месту на диске - при использовании copy-on-write - <размер образа> * <количество клиентов> - максимально, минимально - смотреть по ситуации. Без использования - только под размер образа.

Требования к dhcp - уметь отдавать кроме, собственно, ip-адреса - параметры "next-server" и "filename".

Требования к tftp - уметь отдавать файлы :-). Дополнительно - поддерживать согласование blocksize. это сильно ускоряет отдачу файлов.

Требования к nbd - подойдет любой. Дополнительно - поддержка copy-on-write, это позволяет не сильно напрягаться насчет мест в образе, куда требуется запись (снижает требования к памяти у клиентов (tmpfs) + убирает необходимость монтировать что-то ещё чисто "под запись").

Заливаем получишийся образ на сервер, кладем в отдельную директорию, выставляем владельца/права на файлы примерно так:

drwxrwxr-x  2 admin nbd   4,0K янв.  01 00:00 .
drwxr-xr-x  3  root root  4,0K янв.  01 00:00 ..
-rwxrwxr-x  1 admin nbd   4,0G янв.  01 00:00 diskless.bin

TODO: выложить конфиг nbd с комментариями.

Вытаскиваем из образа ядро и initrd, кладем в директорию, доступную tftpd. Переписываем опции загрузчика из образа в конфиг pxelinux, у меня получилось что-то вроде этого:

label debian
    kernel /_boot/debian/vmlinuz
    append initrd=/_boot/debian/initrd.img quiet vga=789 **nbdroot=192.168.0.1,2004 root=/dev/nbd0p2**

Пути нужно поправить, выделенное ** - добавлено4.

Обновление

TODO

Замеченные грабли

  • При использовании файловых систем семейства ext? и древнего железа может возникнуть следующая ситуация: ФС (в т.ч. корень) может отказаться монтироваться, если в биосе на клиенте слетели настройки времени из-за сдохшей батарейки и/или отключения света. "Last mount time in the future, run fsck manually." и всё, курите бамбук. UPD: решение этой задачи вынесено в отдельную статью.
  • Сочетание cow + atime в параметрах монтирования вызывает взрывной рост diff-файла. Используйте noatime/relatime где только можно.
  • Если используется обновление образа с помощью одной из машин, нужно удалять правила udev'а для назначения имени сетевым картам выключения, иначе «поплывет» нумерация сетевух.
  • Связано с предыдущим пунктом - получение сетевых настроек по dhcp и их применение без конфигурации самого интерфейса. Это нужно для того, чтобы при смене адреса dns-сервера (например) не лезть в образ. При использовании в качестве клиента dhcpcd - это делается с помощью ключа «-s» с пустым параметром (т.е. dhcpcd -s «» )5. В других клиентах - смотреть в мане, как включается тип сообщений DHCP_INFORM.
  • Нужно как-то чистить diff'ы образа после отключения клиента. Есть идея пропатчить сервер, чтобы он создавал и сразу удалял diff-файл, т.о. проблема чистки решится автоматически - с закрытием соединения/вылетом nbd-сервера/перезагрузкой машины файл будет удален. Пока не знаю, будет ли это работать, это зависит от того используется ли там mmap/seek+write и работают ли они для удаленных, но не закрытых файлов. Если это не работает - пропатчить, чтобы порожденный процесс удалял diff перед самым выходом.
  • Debian'о-специфичные грабли - инитскрипты жестко привязаны к имени корневого раздела вида /dev/nbd?p?, т.е. корень внутри lvm'а идет лесом. Возможно оно и к лучшему.

TODO: четвертый пункт дополнить описанием, зачем вообще здесь нужен dhcp_inform, пятый переписать по результатам тестирования патча.

Ссылки


  1. qemu. проигрывает VBox'у в простоте и удобстве, выигрывает в скорости и гибкости ↩

  2. осторожно, у него проблемы с дисками, подключенными как virtio ↩

  3. Разумеется оно и через диалап-модем загрузится, но загрузки дождетесь как раз к завтрашнему вечеру ↩

  4. debian'о-специфичные опции, у вас они могут отличаться, смотрите в своих инитскриптах ↩

  5. в дебиане это пишется в /etc/default/dhcpcd, OPTIONS=('-p' '-s'). ↩