Ядро Linux умеет монтировать корневую файловую систему, но не сразу после передачи управления загрузчиком. Сначала оно распаковывает в память небольшой архив и запускает из него /init. Это и есть initramfs — минимальная среда, которая загружает модули, собирает LVM, открывает LUKS, находит корень на RAID или по сети и передаёт управление настоящему systemd. Если образ собран неверно, система падает в busybox ещё до привычных сообщений.
Зачем нужен отдельный ранний этап
Причина проста: драйверы хранения, шифрования и файловых систем часто лежат в модулях. Ядро не может прочитать раздел, пока не загрузит драйвер, а загрузить его с этого раздела нельзя — замкнутый круг. initramfs разрывает его: архив уже в памяти, поэтому оттуда можно подгрузить nvme, ahci, dm_crypt, dm_raid или сетевой стек для NFS-корня.
Вторая задача — подготовить инфраструктуру: смонтировать /proc, /sys и /dev, дождаться устройств и передать эстафету через switch_root. Всё происходит в tmpfs, поэтому после переключения память архива освобождается.
Как устроен архив
Современный initramfs — cpio-архив, сжатый gzip, xz или zstd. Внутри обычное дерево: /init (PID 1 раннего этапа), /bin с busybox, /lib/modules, /etc и /scripts у Debian или хуки у dracut. Ядро распаковывает архив в rootfs на tmpfs и выполняет /init; тот монтирует псевдофайловые системы, загружает модули и вызывает switch_root. Старый initrd был образом диска, современный initramfs — файловой системой в памяти.
Микрокод процессора подключается отдельным cpio-сегментом перед основным архивом. Если в загрузке нет строки об обновлении микрокода, исправления CPU не применяются.
Осмотр и распаковка
Перед изменениями полезно увидеть, что уже лежит в образе. В Debian и Ubuntu есть lsinitramfs, в Fedora и RHEL — lsinitrd:
lsinitramfs /boot/initrd.img-$(uname -r) | head -40
lsinitrd /boot/initramfs-$(uname -r).img | head -40
Для разбора распакуйте архив. unmkinitramfs из initramfs-tools не смешивает early-сегмент с основным:
mkdir -p /tmp/initrd
unmkinitramfs /boot/initrd.img-$(uname -r) /tmp/initrd
find /tmp/initrd -maxdepth 2 -type d | head -30
Внутри видно, какие модули попали в образ, есть ли cryptsetup и не забыт ли драйвер дисковой подсистемы.
Сборка в Debian и Ubuntu
Здесь всем управляет initramfs-tools. Основные настройки — в /etc/initramfs-tools/initramfs.conf, локальные дополнения удобно держать в conf.d:
sudo tee /etc/initramfs-tools/conf.d/local.conf <<'EOF'
MODULES=most
COMPRESS=zstd
EOF
sudo update-initramfs -u -k all
-u обновляет существующий образ, -k all — для всех ядер. Если нужен модуль вне автодетекта, добавьте его имя в /etc/initramfs-tools/modules. Для сложной логики создайте исполняемый hook в /etc/initramfs-tools/hooks/: он подключает hook-functions и копирует файлы через copy_exec. После обновления ядра образ пересобирается сам; ручная команда нужна после смены шифрования или восстановления.
Сборка в Fedora и RHEL
В системах с dracut локальные файлы кладите в /etc/dracut.conf.d/, а не в /usr/lib/dracut. Пример включает LVM, шифрование и SSH-клиент, убирает Plymouth и добавляет NVMe:
sudo tee /etc/dracut.conf.d/10-local.conf <<'EOF'
add_dracutmodules+=" lvm crypt ssh-client "
omit_dracutmodules+=" plymouth "
drivers+=" nvme "
EOF
sudo dracut --force
Команда без аргументов пересобирает образ текущего ядра, --regenerate-all — всех. Проверяйте через lsinitrd, а отладочный shell включайте параметром rd.break.
Сборка в Arch Linux
В Arch образ собирает mkinitcpio. Список hook-ов в /etc/mkinitcpio.conf, и порядок критичен: udev до block, filesystems после. Строка для корня на LUKS поверх LVM:
HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block lvm2 encrypt filesystems fsck)
sudo mkinitcpio -P
-P пересобирает образы всех ядер. Для LUKS обязателен encrypt, для LVM — lvm2, для RAID — mdadm_udev. После смены hook-ов проверьте загрузку в виртуальной машине.
Минимальный initramfs своими руками
Лучший способ понять устройство — собрать крошечный образ из busybox. Он не загрузит систему, но даст оболочку и покажет механизм. Создайте дерево, скопируйте busybox и сделайте ссылки:
mkdir -p /tmp/myinit/{bin,dev,proc,sys,newroot}
cp /bin/busybox /tmp/myinit/bin/
for a in sh mount cat echo switch_root; do
ln -sf busybox /tmp/myinit/bin/$a
done
Теперь напишите /tmp/myinit/init — PID 1. Скрипт монтирует псевдофайловые системы и запускает оболочку:
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
exec sh
Не забудьте chmod +x, соберите cpio и проверьте в QEMU — диск не нужен:
cd /tmp/myinit
find . -print0 | cpio --null -o -H newc | gzip -9 > /tmp/myinit.cpio.gz
qemu-system-x86_64 -kernel /boot/vmlinuz-$(uname -r) -initrd /tmp/myinit.cpio.gz -append "console=ttyS0" -nographic
В реальном образе вместо exec sh нужно смонтировать корень в /newroot и вызвать switch_root /newroot /sbin/init. Готовые генераторы делают то же, но с LUKS, LVM и сетевыми корнями.
Отладка загрузки
Если система не доходит до корня, включите подробный вывод и аварийную оболочку. В Debian помогает break=premount, в dracut — rd.break и rd.debug; параметры добавляются в строку ядра через GRUB. Внутри проверьте ls /dev, lsmod, ручное монтирование и UUID в /etc/fstab.
Частые причины: забытый модуль ФС, неверный UUID после клонирования, отсутствие cryptsetup, конфликт hook-ов и переполненный /boot. Держите рабочий образ ядра: это дешевле загрузочной флешки.
Чек-лист
- Проверьте, что драйвер диска и ФС попал в образ.
- Сверьте UUID корня в
fstabи параметрах загрузки. - После правки hook-ов пересоберите образ для всех ядер.
- Сохраните рабочий initramfs и live-носитель.
Итог
initramfs — не «магический файл рядом с ядром», а понятный cpio-архив с программой /init. Он даёт ядру драйверы и инструменты до монтирования настоящего корня. Зная настройки initramfs-tools, dracut и mkinitcpio, вы добавите нужный модуль, включите отладочную оболочку и соберёте собственный минимальный образ. Начните с чтения текущего архива и теста в QEMU — это снимает большую часть загадок ранней загрузки.