PXE-мультитул на базе Raspberry Pi
Инженеры дата-центров часто сталкиваются с задачей первоначальной настройки серверов. Причем, чаще всего настраивать приходится не одну-две единицы оборудования, а несколько десятков или даже сотен. Каждый раз, когда к нам приезжает новое оборудование мы должны его не только досконально проверить перед сдачей в эксплуатацию, но еще и соответствующим образом настроить для работы с нашими внутренними системами.
Перед тем как приступить к достаточно тривиальной процедуре настройки 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. Вот такую, например:
Теперь запускаем 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. Если все сделано правильно, то вы увидите созданное меню:
Выбрав пункт GRML Linux нажимаем Enter и видим, что у нас происходит успешный процесс загрузки образа:
Таким образом мы получили возможность сетевой загрузки популярного среди системных администраторов дистрибутива 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, так что монтируем их внутрь виртуальной машины и запускаем установку:
Дважды меняем образы дискет в виртуальном приводе и спустя буквально 20 секунд у нас есть qcow-образ со свеженькой ОС MS-DOS 6.22:
Самым простым способом теперь скопировать файлы на данный диск будет примонтировать его к любой другой виртуальной машине с Windows или Linux. После этой операции повторно монтируем диск к виртуальной машине с MS-DOS и проверяем, что файлы видны:
При желании можно даже настроить автозагрузку на выполнение 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-меню появился новый пункт меню, выбрав который мы загружаем созданный образ:
Точно так же можно создавать образы, содержащие как служебные утилиты, так и забавы ради со старыми играми под 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, затем последовательно распаковываются и запускаются необходимые файлы.
Важно учитывать, что подгрузка файлов происходит по TFTP, что не слишком быстро и удобно. В целом можно немного видоизменить конфигурацию и вытянуть их другим способом. Но для простых утилитарных задач и этого вполне достаточно.
Вместо заключения
В этой статье нет какого-либо ноу-хау, но когда возникла необходимость — Raspberry Pi отлично справилась. Ей не потребовалось искать дополнительную розетку питания, прекрасно подошел USB-порт ближайшего свободного сервера. Нет дополнительных заморочек с поиском места в забитой под завязку железом стойке. Работает именно так как надо, а малые габариты позволяют всегда иметь ее под рукой.
В сети на самом деле есть множество мануалов по настройке сетевой загрузки. Проблема лишь в том, что для разных образов используются разные средства и различные подходы. Выбор правильного подхода иногда требует массы времени и я очень надеюсь, что этот материал поможет многим сэкономить время при решении различных задач, связанных с необходимостью быстрого развертывания PXE на одноплатном компьютере Raspberry Pi.