Бездисковая загрузка по сети и жизнь после нее

от автора

История

Однажды к нам пришли (ну, не сами…) серверы с 14 хардами по 2Тб. Избавившись от аппаратного рейда (зачем — вопрос отдельный), мы задумались о том, что неплохо бы сделать для них загрузку по сети, дабы избавиться от возни с разделами. Диски предполагалось экспортировать по iSCSI, и не хотелось выделять какие-то диски на Особенные Системные Диски, а какие-то на всё остальное. Таким образом возникла задача сделать загрузку по сети с размещением корневого каталога в оперативной памяти.

Теория

По сути, для того, чтобы система загрузилась ей необходимо 3 компонента — ядро, начальное окружение initramfs и корневой каталог, в котором система будет работать.

Практика

Все действия проводятся на машине с ubuntu precise.

PXE

Для начала настроим PXE. Мануалов на эту тему уйма, поэтому я расскажу только самую суть.
Ставим ваш любимый dhcp сервер, например isc-dhcp-server, который будет раздавать машинкам ip адреса и указывать путь к файлу pxelinux.0, который будет отдавать tftp сервер (tftp-hpa или же atftp).

aptitude install isc-dhcp-server tftpd-hpa 

Пример конфига dhcp сервера. В примере pxe-сервер находится по адресу 10.0.0.1.

option domain-name-servers 8.8.8.8; server-name "pxe";  subnet 10.0.0.0 netmask 255.255.255.0 {         range dynamic-bootp 10.0.0.2 10.0.0.10;         option subnet-mask 255.255.255.0;         option routers 10.0.0.1;         option root-path "10.0.0.1:/var/lib/tftpboot/";          filename "pxelinux.0"; } 

Запускаем tftp сервер (в ubuntu он имеет init-скрипт, но вполне вероятно, что вам придется запускать его и через inetd/xinetd).
Проверяем работоспособность. Кладем файл в каталог /var/lib/tftpboot и пробуем стянуть его tftp клиентом.

tftp 10.0.0.1 tftp> get pxelinux.0 

В принципе неважно, где вы возьмете файл pxelinux.0, так как он является просто начальным загрузчиком, в который мы передаем то, что надо грузить дальше.
Вы можете сделать красивую менюшку в загрузчике, но сейчас нам это не нужно, поэтому мой pxelinux.cfg/default выглядит так

default vesamenu.c32 aprompt 1 timeout 2 label ubuntu 12.04 menu label Ubuntu precise kernel vmlinuz append initrd=initrd.img boot=ram rooturl=http://10.0.0.1/rootfs.squashfs ip=dhcp 
rootfs

Образ rootfs собираем через debootstrap, чрутимся в него и ставим необходимые программы. Настраиваем сеть, hostname, фаервол и прочее, чем больше сделаем настроек, тем больше будет образ. Главное не забудьте сменить пароль на рута.

mkdir -p /mnt/rootfs debootstrap precise /mnt/rootfs/ http://mirror.yandex.ru/ubuntu/ chroot /mnt/rootfs /bin/bash aptitude install vim  ... 

С нашим минимальным набором система получилась весом 200Мб.

Initramfs

В этом примере мы будем забирать образ корневой фс с веб-сервера, расположенного на нашем сервере сетевой загрузки, то есть на 10.0.0.1. Решение было таким просто потому, что в нашем initramfs была утилита wget. Чтобы не тянуть большой объем данных по сети, мы решили сжать образ. Это можно было бы сделать и обычным tar, но можно попробовать squashfs, тем более, что обычно в initramfs tar не встроен, с другой стороны, ничего не мешает его туда добавить.

Squashfs
Squashfs — это сжимающая файловая система, которая включена в ядро с версии 2.6.29. С ее помощью можно заархивировать каталог, примонтировать на loop устройство и читать с него, для записи же необходимо провести процедуру добавления файлов в архив. Так как при обращении к squashfs, вы читаете из архива, то это дает дополнительную нагрузку на cpu.

 mksquashfs /mnt/rootfs/ rootfs.squashfs -noappend -always-use-fragments  du -hs rootfs.squashfs   92M	rootfs.squashfs 

Для более эфферктивного сжатия вы можете использовать опцию -comp, чтобы установить тип сжатия, по умолчанию используется gzip.

Далее надо научить init из initramfs забирать образ корня и помещать его в оперативную память.

init в initramfs — это скрипт на sh, который производит разбор опций из cmdline, монтирует фс, делает switch_root и запускает гланый init-процесс системы.
Воспользуемся этим и допишем свои опции для cmdline. Напишем скрипт ram, который будет вызываться при значении опции boot=ram.

vim /usr/share/initramfs-tools/scripts/ram

#!/bin/bash retry_nr=0  do_rammount() {         log_begin_msg "Configuring networking"         configure_networking         log_end_msg          log_begin_msg "Downloading rootfs image"         mkdir -p /tmp/squashfs         wget ${rooturl} -O /tmp/squashfs/rootfs.squashfs         log_end_msg          log_begin_msg "Mounting rootfs image to /mnt/squashfs"         mkdir -p /mnt/squashfs         mount -t squashfs -o loop /tmp/squashfs/rootfs.squashfs /mnt/squashfs         log_end_msg          log_begin_msg "Mounting tmpfs and copy rootfs image"         mkdir -p ${rootmnt}         mount -t tmpfs -o size=1G none ${rootmnt}         cp -r -v /mnt/squashfs/* ${rootmnt} || exit 2         log_end_msg          log_begin_msg "Umount squashfs"         umount /mnt/squashfs || exit 2         log_end_msg } mountroot() {         for x in $(cat /proc/cmdline); do                 case $x in                 rooturl=*)                         export rooturl=${x#rooturl=}                         ;;                 esac         done          log_begin_msg "Loading module squashfs"         modprobe squashfs         log_end_msg        # For DHCP         modprobe af_packet          wait_for_udev 10          # Default delay is around 180s         delay=${ROOTDELAY:-180}          # loop until rammount succeeds         do_rammount          while [ ${retry_nr} -lt ${delay} ] && [ ! -e ${rootmnt}${init} ]; do                 log_begin_msg "Retrying rammount"                 /bin/sleep 1                 do_rammount                 retry_nr=$(( ${retry_nr} + 1 ))                 log_end_msg         done } 

Через параметр rooturl можно указывать откуда качать образ корневой фс. Для работы со squashfs необходимо подгрузить ее модуль в ядро. Указываем в /etc/initramfs-tools/initramfs.conf BOOT=ram и пересобираем initramfs

mkinitramfs -o /var/lib/tftpboot/initrd.img 

Включаем машинку, на которой будем тестировать, и смотрим на происходящее. После успешной загрузки мы получили бездисковую систему, которая занимает в памяти около 300Мб, при этом мы может писать в нее, но после ребута, система вернется в свое первоначальное состояние.

В это примере, мы использовали squashfs просто для сжатия образа, но почему бы нам не попробовать примонтировать корневой раздел в squashfs и не посмотреть, что получится? Меняем наш скрипт, в функции do_rammount() оставляем только монтирование squashfs.

do_rammount() {         log_begin_msg "Configuring networking"         configure_networking         log_end_msg          log_begin_msg "Downloading rootfs image"         mkdir -p /tmp/squashfs         wget ${rooturl} -O /tmp/squashfs/rootfs.squashfs         log_end_msg          log_begin_msg "Mounting rootfs image to /mnt/squashfs"         mkdir -p /mnt/squashfs         mount -t squashfs -o loop /tmp/squashfs/rootfs.squashfs ${rootmnt}         log_end_msg  } 

Пересобираем initramfs, запускаем, смотрим. Система загружается в режиме ro, но зато занимает в памяти всего около 180Мб.
В каких-то случаях монтирование в режиме ro это хорошо, но нас это не устраивает, но и просто так тратить оперативную память нам тоже не хочется. Выход же был найден при помощи Aufs.

Aufs
Aufs позволяет делать каскадно-объединённое монтирование файловых систем — одну в режиме только на чтение, а вторую в rw. Работает она в режиме copy-on-write, то есть все изменения записываются на rw систему и после этого чтение производится с нее же.
Опять переписываем наш скрипт.
В фукнцию mountroot() добавляем

        log_begin_msg "Loading module aufs"         modprobe aufs         log_end_msg 

А фукнцию do_rammount() приводим к следующему виду:

do_rammount() {         log_begin_msg "Configuring networking"         configure_networking         log_end_msg          log_begin_msg "Downloading rootfs image"         mkdir -p /tmp/squashfs         wget ${rooturl} -O /tmp/squashfs/rootfs.squashfs         log_end_msg          log_begin_msg "Mounting rootfs image to /mnt/ro"         mkdir -p /mnt/ro         mount -t squashfs -o loop /tmp/squashfs/rootfs.squashfs /mnt/ro         log_end_msg          log_begin_msg "Mounting tmpfs to /mnt/rw"         mkdir -p /mnt/rw         mount -t tmpfs -o size=1G none /mnt/rw         log_end_msg          log_begin_msg "Mounting aufs to /mnt/aufs"         mkdir -p /mnt/aufs         mount -t aufs -o dirs=/mnt/rw=rw:/mnt/ro=ro aufs /mnt/aufs         log_end_msg          [ -d /mnt/aufs/mnt/ro ] || mkdir -p /mnt/aufs/mnt/ro         [ -d /mnt/aufs/mnt/rw ] || mkdir -p /mnt/aufs/mnt/rw          mount --move /mnt/ro /mnt/aufs/mnt/ro #сдвигаем точку squashfs монтирования в aufs         mount --move /mnt/rw /mnt/aufs/mnt/rw #сдвигаем точку монтирования tmpfs в aufs           mount --move /mnt/aufs ${rootmnt} #сдвигаем точку монтирования aufs в ${rootmnt} } 

Пересобираем initramfs, запускаем, смотрим. Система занимает в памяти 181Мб, при этом мы можем менять ее, писать, читать. Все изменения хранятся отдельно в /mnt/rw, а сама система хранится в /mnt/ro.

В результате мы получили систему, которая грузится по сети, занимает небольшой объем в памяти, при этом после каждой перезагрузки пропадают все изменения (поэтому надо заранее собирать все нужные продукты жизнедеятельности системы в надежное место).

Все вышеперечисленные способы имеют право на жизнь. Надеюсь, что эта информация вам пригодится, а мне же будет интересно почитать/послушать ваши комментарии.
Спасибо за внимание.

Ссылки

Ubuntu boot to ram
Squashfs home page
PXE

ссылка на оригинал статьи http://habrahabr.ru/post/164147/


Комментарии

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

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