PXE-мультитул на базе Raspberry Pi

b7sqse-h6sonoyaga3cjotxdskk.png

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

Перед тем как приступить к достаточно тривиальной процедуре настройки BIOS и IPMI, мы должны убедиться в том, что каждый компонент сервера имеет требуемую версию прошивки. В большинстве случаев, за редким исключением, необходима актуальная версия, доступная на сайте производителя конкретных комплектующих. Сегодня мы расскажем, как мы придумали для ускорения процесса задействовать популярную нынче «малинку».

В чем сложность


Казалось бы задачка весьма проста. Ну давайте возьмем какой-нибудь live-дистрибутив Linux, раскатаем его на флешку и будем с него загружаться и выполнять требуемые процедуры. Но не тут-то было.

Проблема в том, что у каждого вендора свои собственные методы и собственные утилиты для обновления прошивок. Причем, не все они способны корректно работать под Linux. Какие-то компоненты, например, BIOS у серверов SuperMicro желательно шить из-под MS-DOS. Для каких-то компонентов, например, сетевых карт Mellanox в некоторых случаях нужно использовать Windows, а некоторые поддерживают прошивку непосредственно из Linux.

Таким образом нам нужно будет либо собирать мультизагрузочный образ и записать его на несколько десятков флешек, затем долго и упорно загружаться с них, либо придумать нечто более интересное и быстрое. И вот здесь приходит на ум использование сетевых карт серверов с поддержкой загрузки по PXE (Preboot eXecution Environment, произносится как пикси).

Берем «малинку», разворачиваем там DHCP- и TFTP-сервер, готовим требуемые образы для загрузки. Чтобы массово загрузить несколько десятков серверов просто временно задействуем неуправляемый гигабитный свитч на 48 портов и вуаля. По-умолчанию большинство серверов возможность загрузки с PXE предусматривают без дополнительной настройки, поэтому такой вариант идеален.

За основу я взял прекрасную статью от Romanenko_Eugene, но с поправками на особенности Raspberry Pi и решаемые задачи. Приступаем!

Малиновое варенье


Когда я первый раз проделывал такое — самой продвинутой версией Raspberry Pi была третья с сетевой картой на 100 Мбит/с. Этого откровенно говоря мало для массовой раздачи тяжелых образов, так что настоятельно рекомендую использовать доступную ныне Raspberry Pi 4 с портом на 1 Гбит/с. Еще одна рекомендация — по возможности использовать быструю и емкую карту памяти.

Теперь о первоначальной настройке. Графический интерфейс для такой штуки абсолютно не требуется, поэтому можно скачать самую простую Lite-версию операционной системы Raspberry Pi OS. Распаковываем образ и заливаем образ на MicroSD-карту любым удобным способом, например с помощью dd:

sudo dd if=<имя_образа> of=/dev/mmcblk0 bs=1M

В процессе настройки можно либо настраивать традиционным способом, подключив клавиатуру и монитор, либо через SSH. Чтобы при запуске SSH-сервис заработал, создаем пустой файл с именем ssh в разделе /boot.

Поскольку сетевой порт будет задействован для работы DHCP, то для доступа в интернет можно либо использовать подключение к Wi-Fi, либо подключить к «малинке» еще одну сетевую карту по USB. Вот такую, например:

f2zh-wjn-i8y_fsxznkeki7tvgs.png

Теперь запускаем Raspberry Pi, подключаем ее к интернету и обновляем репозитории и пакеты ПО:
sudo apt update
sudo apt upgrade

Поскольку я задействую еще одну сетевую карту, то встроенной надо задать статический IP-адрес. Открываем конфиг:
sudo nano /etc/dhcpcd.conf

Добавляем следующие строчки:
interface eth0
static ip_address=192.168.50.1/24

Здесь eth0 — встроенная сетевая карта Raspberry Pi. Именно с помощью нее будет осуществляться и раздача IP-адресов, и сетевая загрузка. Теперь устанавливаем tftp-сервер:
sudo apt install tftpd-hpa

После установки открываем конфигурационный файл:
sudo nano /etc/default/tftpd-hpa

Приводим строку к следующему виду:
TFTP_OPTIONS="--secure -l -v -r blksize"

В целом можно было бы и оставить все как есть, но тестировать каждый раз на отдельной машине иногда не слишком удобно. Опции -l -v -r blksize позволяют без проблем тестировать все это на виртуальной машине VirtualBox, исправляя некоторую проблему совместимости. Теперь устанавливаем DHCP-сервер для раздачи IP-адресов:
sudo apt install isc-dhcp-server

Открываем первый конфигурационный файл isc-dhcp-server:
sudo nano /etc/default/isc-dhcp-server

Явным образом указываем интерфейс, на котором предполагается работа DHCP-сервера:
INTERFACESv4="eth0"

Теперь открываем второй конфиг dhcpd.conf:
sudo nano /etc/dhcp/dhcpd.conf

Задаем необходимые параметры сервера, раздаваемую подсеть, а также передаем имя файла загрузчика:
default-lease-time 600;
max-lease-time 7200;
ddns-update-style none;
authoritative;

subnet 192.168.50.0 netmask 255.255.255.0 {
        range 192.168.50.2 192.168.50.250;
        option broadcast-address 192.168.50.255;
        option routers 192.168.50.1;
        filename "pxelinux.0";
}

Сохраняем файл и теперь нам предстоит собственно скачать загрузчик, нужные модули и сформировать меню PXE. Начнем со скачивания набора загрузчиков Syslinux, в моем случае наиболее удобна версия 5.01:
wget https://mirrors.edge.kernel.org/pub/linux/utils/boot/syslinux/syslinux-5.01.zip

Распаковываем:
unzip syslinux-5.01.zip

Теперь нам надо найти и извлечь модуль memdisk, умеющий подгружать в ОЗУ ISO-образы целиком, загрузчик pxelinux.0 и остальные модули comboot. Последовательно выполняем команды, которые отыщут и скопируют все найденное в директорию /srv/tftp:
find ./ -name "memdisk" -type f|xargs -I {} sudo cp '{}' /srv/tftp/
find ./ -name "pxelinux.0"|xargs -I {} sudo cp '{}' /srv/tftp/
find ./ -name "*.c32"|xargs -I {} sudo cp '{}' /srv/tftp/

После этой операции нам нужно создать конфигурационный файл непосредственно для меню, отображаемого на экране PXE. Переходим в нашу директорию:
cd /srv/tftp

Создаем директорию с именем pxelinux.cfg и переходим в нее:
sudo mkdir pxelinux.cfg
cd pxelinux.cfg

Создаем конфигурационный файл:
sudo nano default

В качестве примера давайте возьмем отличный live-дистрибутив GRML и загрузим его через PXE. Ниже готовый пример конфигурации:
UI vesamenu.c32                  
PROMPT 0
MENU TITLE Raspberry Pi PXE Server
MENU BACKGROUND bg.png
LABEL bootlocal
   menu label Boot from HDD
   kernel chain.c32
   append hd0 0
   timeout 0
TEXT HELP
Boot from first HDD in your system
ENDTEXT
LABEL grml
   menu label GRML Linux
KERNEL grml/boot/grml32full/vmlinuz
APPEND root=/dev/nfs rw nfsroot=192.168.50.1:/srv/tftp/grml/ live-media-path=/live/grml32-full boot=live lang=us nomce apm=power-off noprompt noeject initrd=grml/boot/grml32full/initrd.img vga=791
LABEL reboot
    menu label Reboot
    kernel reboot.c32
TEXT HELP
Reboot server
ENDTEXT

Здесь наверное стоит немного остановиться и разобраться что делает каждая строка:
  • UI vesamenu.c32 — используем модуль vesamenu.c32 для вывода меню;
  • PROMPT 0 — подсвечиваем нулевой пункт меню;
  • MENU TITLE Raspberry Pi PXE Server — задаем общее название меню;
  • MENU BACKGROUND bg.png — элемент оформления, используем bg.png в качестве фонового изображения.

Фоновое изображение можно изготовить заранее. По-умолчанию подойдет картинка 640×480 с глубиной цвета не более 24 бит, в формате PNG или JPG. Ее надо заранее скопировать в /srv/tftp. Теперь разберем каждую секцию. Первая секция введена для удобства. Если надо загрузится с первого жесткого диска, то прописываем:
  • LABEL bootlocal — внутреннее имя секции;
  • menu label Boot from HDD — то, как будет отображено меню у пользователя;
  • kernel chain.c32 — используем модуль chain.c32, умеющий выполнять загрузку с различных носителей;
  • append hd0 0  — явным образом указываем, что загрузка должна быть с первого раздела первого жесткого диска;
  • timeout 0 — тут можно либо задать таймаут в секундах, по истечении которого будет автоматически запущена загрузка, либо указав 0 убрать таймер.
  • TEXT HELP — указываем начало текста подсказки для пользователя;
  • Boot from first HDD in your system — текст подсказки;
  • ENDTEXT — указываем конец текста подсказки.

Приблизительно таким же образом формируем и секцию для перезагрузки. Единственным отличием будет вызов модуля reboot.c32, который собственно и отправляет машину в ребут. Ну, а перед тем как разобрать что делает третья секция, загружающая дистрибутив GRML, поговорим о том, что собственно будет загружаться и каким образом.

Everything is a file


Сам ISO-образ доступен на сайте этого live-дистрибутива. Скачиваем его и переименовываем для удобства, в примере возьмем 32-битную версию:
wget https://download.grml.org/grml32-full_2020.06.iso
mv grml32-full_2020.06.iso grml.iso

Теперь надо этот образ каким-то образом заставить загружаться. С одной стороны можно применить модуль memdisk и заставить его вначале загрузить все «сырое» содержимое образа непосредственно в ОЗУ и потом передать управление загрузкой. Но этот метод хорош только для очень маленьких образов, например, так удобно загружать MS-DOS. Большие же образы долго загружаются в память и не всегда работают адекватно.

Поэтому следует все-таки «распотрошить» образ и на загрузку передать только ядро и livefs. А вот дальнейшие файлы с диска можно предоставлять системе по запросу с помощью NFS-сервера. Такой подход работает значительно быстрее и адекватнее, но требует дополнительных телодвижений, таких как установка NFS-сервера.

Выполняется элементарно:

sudo apt install nfs-kernel-server

Чтобы не устраивать кашу из файлов создаем отдельную директорию для grml:
sudo mkdir /srv/tftp/grml

Нам потребуется примонтировать ISO-образ, поэтому позаботимся о временной точке монтирования:
sudo mkdir /tmp/iso

Монтируем образ. Система предупредит, что образ смонтирован в Read-Only режиме:
sudo mount -o loop grml.iso /tmp/iso

Рекурсивно копируем содержимое образа в нашу отдельную директорию:
sudo cp -R /tmp/iso/* /srv/tftp/grml

Чтобы не возникало проблем с правами доступа, меняем владельца и рекурсивно присваиваем этой директории права из серии «Дом свободный, живите кто хотите»:
sudo chown -R nobody:nogroup /srv/tftp/grml/
sudo chmod -R 777 /srv/tftp/grml/

Теперь дело за малым — указать NFS-серверу, что ему следует предоставлять директорию /srv/tftp/grml любому IP-адресу из нашей подсети:
sudo nano /etc/exports

Прописываем строчку:
/srv/tftp/grml 192.168.50.0/24(rw,sync,no_subtree_check)

Обновляем список и рестартуем NFS-сервер:
sudo exportfs -a
sudo systemctl restart nfs-kernel-server

Теперь у нас наконец-то появляется возможность корректно разделить процесс загрузки на два условных этапа. Первый этап — загрузка ядра и live-filesystem. Второй этап — подтягивание всех остальных файлов через NFS. Пришла пора посмотреть на оставшуюся секцию:
  • LABEL grml — имя секции;
  • menu label GRML Linux — отображение в меню;
  • KERNEL grml/boot/grml32full/vmlinuz — указываем путь до ядра, относительно корня /srv/tftp;
  • APPEND root=/dev/nfs rw nfsroot=192.168.50.1:/srv/tftp/grml/ live-media-path=/live/grml32-full boot=live lang=us nomce apm=power-off noprompt noeject initrd=grml/boot/grml32full/initrd.img vga=791 — тут мы говорим, что используем NFS, прописываем пути к условному корню, задаем некоторые дополнительные параметры, рекомендуемые в документации и указываем относительный путь до initrd.

Теперь остается только последовательно запустить TFTP и DHCP-сервер и можно пробовать выполнять загрузку в PXE. Если все сделано правильно, то вы увидите созданное меню:
qwmtf_hpwqrjegauxyt9grmbs-a.png

Выбрав пункт GRML Linux нажимаем Enter и видим, что у нас происходит успешный процесс загрузки образа:
bjhiphhg1txpsjkfcmtlrxlcohq.png

Таким образом мы получили возможность сетевой загрузки популярного среди системных администраторов дистрибутива GRML. Но что насчет того же самого MS-DOS и как можно самостоятельно подготовить образ для перепрошивки BIOS. Об этом поговорим дальше.

In DOS We Trust


Неужели в 21 веке все еще используется операционная система из 80-х годов прошлого тысячелетия. Каким бы странным это не казалось —, но для некоторых специфичных задач MS-DOS по прежнему актуальна и вполне себе находит применение. Одной из таких задач является обновления прошивки BIOS на серверах.

Возьмем для примера какую-нибудь материнскую плату, например, Supemicro X11SSL-F и скачаем обновление BIOS с официального сайта. Внутри видим приблизительно подобный набор файлов:

user@linux:~/Загрузки/X11SSLF0_B26> ls -l
итого 16592
-rw-r--r-- 1 user users   169120 фев  1  2015 AFUDOSU.SMC
-rw-r--r-- 1 user users     5219 сен 20  2003 CHOICE.SMC
-rw-r--r-- 1 user users    22092 апр 27  2014 FDT.smc
-rw-r--r-- 1 user users     3799 дек 15  2016 FLASH.BAT
-rw-r--r-- 1 user users     3739 мая 22  2019 Readme for UP X11 AMI  BIOS.txt
-rw-r--r-- 1 user users 16777216 ноя 25 23:48 X11SSLF0.B26

Видим, что у нас уже есть готовый BAT-файл, позволяющий прошить BIOS. Но для того, чтобы это сделать надо иметь уже загруженный в MS-DOS сервер. Сейчас покажем как именно это сделать.

Прежде всего нам надо подготовить небольшой raw-образ жесткого диска с операционной системой. Создаем новую виртуальную машину через Oracle VM VirtualBox с небольшим диском на 32 мегабайта. Формат выбираем QCOW, после всех манипуляций его можно будет легко сконвертировать в raw.

Наверняка вы все знаете где можно достать образы дискет с MS-DOS, так что монтируем их внутрь виртуальной машины и запускаем установку:

xdvnmfr0i-nhpzq_9waind4izpk.png

Дважды меняем образы дискет в виртуальном приводе и спустя буквально 20 секунд у нас есть qcow-образ со свеженькой ОС MS-DOS 6.22:
msdx5xagtjkp4oozglmrd4gjr_o.png

Самым простым способом теперь скопировать файлы на данный диск будет примонтировать его к любой другой виртуальной машине с Windows или Linux. После этой операции повторно монтируем диск к виртуальной машине с MS-DOS и проверяем, что файлы видны:
ijkcveqrhn-l6zz3nm-gbg-occw.png

При желании можно даже настроить автозагрузку на выполнение BAT-файла, чтобы перепрошивка BIOS производилась автоматически. Но помните, что это потенциально опасная операция и делаете вы ее на свой страх и риск. Теперь выключаем виртуальную машину и переконвертируем ее в raw-образ с помощью qemu-img.
qemu-img convert -f qcow -O raw DOS.qcow dos.img

Полученный IMG-образ копируем в отдельную директорию нашего TFTP-сервера:
sudo mkdir /srv/tftp/dos
sudo cp dos.img /srv/tftp/dos

Теперь открываем конфигурацию /srv/tftp/pxelinux.cfg/default на редактирование и добавляем в меню еще один пункт:
LABEL DOS
        kernel memdisk
        initrd dos/dos.img
        append raw

Сохраняем конфигурацию и теперь у нас в PXE-меню появился новый пункт меню, выбрав который мы загружаем созданный образ:
gfci4mrcrnx3esy7ia1_8lovdcg.png

Точно так же можно создавать образы, содержащие как служебные утилиты, так и забавы ради со старыми играми под DOS.

In Windows Veritas


Давайте теперь попробуем загрузить Live-образ c WinPE (Windows Preinstallation Environment). Загружать полноценный вариант часто просто не требуется, достаточно лишь ограниченного количества функций. Реальное применение эта штука находит при перепрошивке некоторых устройств.

Та же самая Mellanox, год назад поглощенная Nvidia, выпускает утилиту Mellanox Firmware Tools для перепрошивки своих сетевых карт в вариантах для WinPE различных версий. Разумеется сейчас доступны уже различные варианты MFT, но пару раз мы сталкивались именно с необходимостью шить через WinPE-версию.

Создание своего собственного образа WinPE и интеграция нужного ПО туда — задача не слишком сложная, но ее объяснение выходит за рамки данной статьи. Чтобы детально освоить этот процесс можно посетить отличный ресурс winpe.ru. Мы же для примера возьмем какую-либо готовую сборку для демонстрации процесса запуска.

Часто такие сборки представляют собой либо iso-образы, либо rar-архивы с iso-образами. Перед тем как расковыривать такой образ нам нужно раздобыть соответствующий загрузчик.

wimboot — достаточно простой загрузчик, умеющий выполнять загрузку образов в формате wim (Windows Imaging Format). Его можно использовать либо в связке с syslinux, либо с более продвинутым собратом, iPXE. Создаем отдельную директорию в /srv/tftp:

sudo mkdir wimboot
cd wimboot

Cкачиваем сам загрузчик:
wget https://github.com/ipxe/wimboot/releases/latest/download/wimboot

Теперь пришло время подмонтировать образ. Создадим временную директорию в /tmp:
sudo mkdir winpe

Переходим в каталог со скачанным ISO-образом сборки и выполняем:
sudo mount -o loop <имя_образа> /tmp/winpe

Создаем директорию в /srv/tftp, куда положим нужные нам файлы:
sudo mkdir /srv/tftp/winpe

Теперь ищем в сборке и копируем 4 файла:
sudo cp BOOTMGR /srv/tftp/winpe
sudo cp bcd /srv/tftp/winpe
sudo cp boot.sdi /srv/tftp/winpe
sudo cp boot.wim /srv/tftp/winpe

Дело за малым — в конфигурационный файл /srv/tftp/pxelinux.cfg/default дописываем следующую секцию:
LABEL WinPE
        menu label WinPE
        com32 linux.c32 wimboot/wimboot
        append initrdfile=winpe/BOOTMGR,winpe/bcd,winpe/boot.sdi,winpe/boot.wim

Фактически мы при помощи модуля linux.c32 выполняем загрузку загрузчика отличная тавтология, а ему передаем параметры уже в формате, привычном для wimboot. Таким образом вначале у нас загружается wimboot, затем последовательно распаковываются и запускаются необходимые файлы.
g8vfczus7dmcn6_ctf2qrnn6j7i.png

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

Вместо заключения


В этой статье нет какого-либо ноу-хау, но когда возникла необходимость — Raspberry Pi отлично справилась. Ей не потребовалось искать дополнительную розетку питания, прекрасно подошел USB-порт ближайшего свободного сервера. Нет дополнительных заморочек с поиском места в забитой под завязку железом стойке. Работает именно так как надо, а малые габариты позволяют всегда иметь ее под рукой.

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

bkgzbzsrulun7_ogtlmob3kem0w.png

© Habrahabr.ru