Модифицируем процесс загрузки с помощью утилиты make-initrd
Модифицируем процесс загрузки с помощью утилиты make-initrd
Упрощенный процесс загрузки ОС Linux
Загрузка ОС на устройстве с UEFI обычно проходит по следующему сценарию:
Размещение в оперативной памяти EFI загрузчика (например, grub) и передача управления ему;
EFI загрузчик записывает образ ядра в оперативную память и вызывает функцию start_kernel;
Ядро инициализирует себя, монтирует корень файловой системы и запускает процесс init, лежащий на корне (/init, /sbin/init или другой). Этим процессом может быть systemd, System V и другие;
Процесс init запускает сервисы и уже подготавливает систему для работы с пользователем.
Такая схема является вполне работоспособной. Но что, если мы хотим усложнить сценарий загрузки? Например, загрузиться с зашифрованного раздела или сетевого диска или, элементарно, проверить файловую систему перед загрузкой. На помощь приходит initramfs!
Что такое initramfs?
Initramfs (Initial ram file system) – образ файловой системы, загружаемый в оперативную память вместе с ядром. Основные задачи этого образа: монтирование реального корня файловой системы и запуск процесса init лежащего на нем.
Многие современные дистрибутивы Linux используют initramfs образы для загрузки системы. Вы можете убедиться в этом сами, найдя файл /boot/
Возможные сценарии использования initramfs
Внутри initramfs образа могут реализовываться разнообразные сценарии загрузки ОС. Все они ограничены лишь возможностями Linux, характеристиками устройства и вашей фантазией.
Достаточно популярными и широко используемыми сценариями являются:
Вывод приветственной картинки на экран;
Проверка целостности корневой файловой системы;
Загрузка с нестандартных файловых систем;
К экзотическим можно отнести:
Загрузку с образа корня, лежащего в интернете;
Загрузку с зашифрованного раздела;
Загрузку другой ОС с помощью системного вызова kexec.
Утилиты для создания initramfs образов
Разные дистрибутивы используют разные утилиты для создания initrd образов. Debian обычно используют для этого initramfs-tools, Red Hat – Dracut. Но в этой статье я хочу представить другую, менее известную утилиту – make-initrd.
Почему make-initrd?
make-initrd – утилита для создания initramfs образов, разрабатываемая нашим соотечественником Алексеем Гладковым. Сейчас она используется как основной сборщик initramfs в дистрибутиве ALT Linux.
Этот проект обладает рядом преимуществ:
Имеет модульную структуру. Разные логические части образа создаются разными модулями, именуемыми Features (фичами). Например, есть фича luks, отвечающая за внедрение сценария загрузки с зашифрованных разделов LUKS.
Создаваемые initramfs образы имеют простое внутреннее устройство.
Удобен для использования конечным пользователем. Многие части конфигурации определяются автоматически. Это достигается за счет отдельного модуля guess.
Утилита показала свою работоспособность на множестве различных дистрибутивов. Нам лично удавалось собрать и использовать эту утилиту на Ubuntu, Fedora, ALT Linux, Red OS, Astra Linux.
Генерируемые initramfs образы имеют относительно небольшой размер, а их сборка происходит достаточно быстро Для сравнения: initramfs-tools на моей машине генерирует образы размером 51Мб за 24 секунды. Образы же make-initrd весят всего 5Мб и создаются 10 секунд!
Проект продолжает активно развиваться. Со временем появляются новые фичи, улучшается архитектура утилиты и архитектура initramfs образов. Разработчик идет на контакт и всегда рад вашим патчам и предложениям:)
К сожалению, у make-initrd есть и недостатки:
Проект тестируется лишь на Fedora, Ubuntu и Alt Linux. И, хотя нам удавалось использовать его на других дистрибутивах, нет гарантии, что все будет работать гладко и везде.
Устройство initramfs образов, генерируемых make-initrd
В этом разделе описаны основные сведения по архитектуре образов, генерируемых make-initrd.
Сервисы
Создаваемые образы имеют сервисно-ориентированную архитектуру. При передаче управления программе /init порождается дерево сервисов, которые в дальнейшем взаимодействуют между собой. Последовательность их запуска и завершения определяется в процессе сборки образа и настраивается с помощью конфигурационных файлов LSB.
В системе есть несколько основных сервисов, на которых базируется основная логика работы:
udevd – обработчик событий ядра об изменении состояния устройств. При изменении состояния любого устройства он параллельно запускает фильтры (скрипты), добавляющие пользовательские события в очередь. Этот демон используется во многих дистрибутивах Linux.
ueventd – обработчик пользовательских событий. Этот демон запускает обработчики пользовательских событий. События внутри одной очереди обрабатываются последовательно, а сами очереди – параллельно.
polld – демон, ожидающий выполнения условий загрузки системы. Все, что он делает – один раз в заданное время проверяет условия запуска системы. Если они удовлетворены, запускается некоторый экшен. Скрипты проверки условий и экшена зависят от метода загрузки. Тем не менее, зачастую, их задача – проверить, что корень найден и переключить runlevel на второй уровень.
Runlevel
Runlevel определяет уровень запуска системы. Эти уровни отличаются от тех, что стандартно используются в Linux. В make-initrd номер уровня зависит от состояния загрузки системы. А от номера уровня, в свою очередь, зависит то, какие сервисы будут активны в текущий момент. В make-initrd предусмотрено всего 3 уровня:
Уровень 3 – означает, что реальный корень системы еще не примонтирован. На этом уровне происходит запуск и работа большинства сервисов.
Уровень 2 – означает, что реальный корень системы уже примонтирован. На этом происходит завершение большинства сервисов.
Уровень 9 – специальный уровень, на котором происходит подмена корня системы на реальный и запуск /sbin/init на нем.
Разберем пример загрузки с жесткого диска.
Последовательно запускаются различные сервисы, среди которых будут udevd, ueventd и polld;
После обнаружения устройства жесткого диска ядро сгенерирует соответствующее событие для демона udevd;
udevd просмотрит список своих правил. При нахождении правила на добавление блочного устройства запустится соответствующий обработчик (в make-initrd они называются фильтрами);
В какой-то момент ueventd запустит обработчик этого пользовательского события. Обработчик примонтирует жесткий диск;
Демон polld в очередной раз запустит скрипт проверки наличия корня. После его обнаружения запустится экшен, переключающий runlevel на второй уровень;
После переключения runlevel произойдет отключение большинства сервисов. Далее запустится сервис runinit, который установит runlevel на девятый уровень;
Процесс init произведет подмену корня и запустит /sbin/init ;
Устройство файловой системы initramfs образов
Initramfs образ, генерируемый make-initrd, имеет строгую структуру файловой системы. Поэтому при добавлении собственной фичи важно понимать, что и где должно лежать:
/init – программа, которой передается управление после инициализации ядра;
/etc/ – директория конфигураций;
/etc/rc.d/init.d/ – директория LSB конфигураций сервисов;
/etc/udev/rules.d/ – директория правил udevd;
/etc/initrd/method – файл содержащий текущий метод загрузки;
/etc/initrd/cmdline.d/ – директория описаний аргументов ядра;
/lib/uevent/filters/ – директория фильтров uevent событий;
/lib/uevent/handler/ – директория обработчиков uevent событий;
/lib/initrd/boot/method/ – директория скриптов методов загрузки;
/lib/initrd/boot/method/*/check – скрипт проверки условии активации метода загрузки;
/lib/initrd/boot/method/*/action – скрипт активации метода загрузки;
/lib/initrd/boot/scripts/ – директория подскриптов проверки условий метода загрузки;
/.initrd/ – директория, которую могут использовать сервисы и программы для внутренней работы;
/tmp/ – директория временных файлов.
Устройство генератора initramfs образов
process-config – Считывание конфигурационного файла. Во время ее выполнения происходит последовательный запуск целей guess и genimage;
guess – Угадывание конфигурационного файла;
create – Наполнение корня initramfs образа;
pack – Сортировка сервисов и упаковка initramfs образа в cpio архив. Зависит от create;
install – Установка образа в загрузочную директорию. Зависит от pack;
genimage – Вывод информационного сообщения о сборке образа. Зависит от install.
Процесс выполнения этих целей можно разбить на два больших этапа:
1. Угадывание дополнения к конфигурации (цель guess);
2. Сборка образа (цели create – genimage)
Этап угадывания конфигурации
Неправильное задание конфигурации грозит тем, что initramfs образ может оказаться излишне «тяжелым», а загрузка ОС будет происходить медленнее или не происходить вовсе. Например, если вы активировали фичу network, но не используете сеть в процессе загрузки, это грозит вам лишними секундами на настройку сетевых интерфейсов.
Именно для решения этих проблем в make-initrd был добавлен модуль guess. Он помогает оптимально сгенерировать или дополнить существующую конфигурацию, основываясь на окружении компьютера.
Guess использует разные модули для определения разных сфер образа. Например:
device – определяет модули ядра устройств;
keyboard – определяет модули ядра для работы с клавиатурой;
net – определяет модули ядра сетевых устройств;
root – определяет какие разделы нужно примонтировать, а также модули ядра для работы с ними;
smart-card – определяет конфигурацию фичи smart-card;
common – простой универсальный модуль, который может быть использован внутри фич.
Этот список не является исчерпывающим: описание остальных модулей можно посмотреть в README.md файлах модулей guess.
Этап сборки initramfs образа
Во время выполнения цели create создается временная директория, в которой заполняется корень initramfs образа. Корень заполняется в соответствии с переданной конфигурацией. Туда кладется все необходимое: приложения, библиотеки, файлы конфигурации и т.д. Но это еще не все: на последующих шагах корень еще будет дополняться.
Этап сборки образа может сколь угодно расширяться с помощью фич. Например, если активирована фича compress, то cpio архив будет дополнительно сжат с помощью одного из методов: gzip, lzma, bzip2 и т.п.
Фича – это независимый модуль сборщика initramfs образов. Фичи могут воздействовать практически на любой этап сборки образа. Например:
фича compress позволяет сжимать итоговый образ;
фича luks добавляет в initramfs утилиты и модули для работы с luks;
фича clean – очищает после себя рабочую директорию.
Структура проекта make-initrd
Выгрузим проект make-initrd из репозитория и посмотрим на его содержимое
Проект make-initrd очень хорошо структурирован. Давайте опишем, за что отвечают различные файлы и директории в проекте:
data/ – постоянная часть корня initramfs образа. Файлы, находящиеся в ней, будут присутствовать внутри почти всех образов;
datasrc/ – исходники утилит, устанавливаемые внутрь образа;
mk/config.mk – Makefile c постоянной частью конфигурации make-initrd (переменные путей и т.п.);
mk/functions.mk – Makefile с вспомогательными функциями make;
mk/make-initrd.mk – основной Makefile описывающий правила сборки initramfs образа;
tools/ – внутренние утилиты make-initrd;
utils/ – исходники внешних утилит make-initrd;
guess/ – директория guess модулей;
guess/*/config.mk – Makefile с описанием конфигурации guess модуля (переменные модуля, правила его активации). Он подключается вне зависимости от того, активен ли guess модуль или нет;
guess/*/rules.mk – Makefile с описанием правил отработки guess модуля. Он подключается, только если guess модуль активен;
features/ – директория фич;
features/*/data/ – часть корня initramfs образа, предоставляемая фичей;
features/*/datasrc/ – исходники утилит фичи, устанавливаемые внутрь initramfs;
features/*/guess/ – подскрипты модулей guess;
features/*/bin/ – вспомогательный утилиты сборки initramfs образа;
features/*/config.mk – Makefile с конфигурацией фичи (описание зависимых фич, переменных и т.п.). Он подключается вне зависимости от того, активна ли фича или нет;
features/*/rules.mk – Makefile с правилами сборки фичи. Он подключается только в случае, если фича активна.
Установка make-initrd
Сборка initramfs и запуск на реальной машине
ПРЕДУПРЕЖДЕНИЕ Перед генерацией initramfs образа обязательно сделайте backup.
Если система перестанет загружаться, откройте раздел с /boot на другой системе (например, через загрузочную флешку) и верните backup на место. Если вы не сделали backup, попробуйте загрузиться с помощью другого ядра. Это может сработать, так как по-умолчанию make-initrd собирает initramfs только для текущего ядра.
Перезапустим систему и попробуем загрузиться c ядра, для которого мы сгенерировали initramfs образ.
Добавление собственной фичи
Чтобы лучше разобраться с тем, как устроены фичи, давайте напишем свою – hello-usb.
Фича Hello USB
Примечание Исходный код фичи hello-usb можно взять здесь.
Напишем фичу hello-usb, добавляющую в initramfs функционал вывода сообщения “Hello USB!” при нахождении USB-устройства. Также добавим возможность указывать имя производителя устройства, для которого выводится сообщение.
Внутренняя логика работы нашей фичи, следующая:
Контроль добавления нового USB-устройства осуществляется с помощью udev правила /etc/udev/rules.d/99-hello-usb.rules : при добавлении любого USB-устройства вызывается фильтр /lib/uevent/filter/hello-usb
Внутри фильтра создается uevent событие hello-usb
Дерево файлов фичи следующее:
config.mk содержит описание фичи:
Через функцию feature-requires можно указывать зависимые фичи. Мы хотели получить модули ядра и udev-правила для работы с USB-устройствами, поэтому добавили в зависимости фичу usb.
rules.mk содержит правила сборки фичи:
feature/hello-usb/etc/initrd/cmdline.d/hello-usb – регистрирует опцию ядра usb-vendor:
feature/hello-usb/etc/udev/rules.d/99-hello-usb.rules – udev правило, запускающее фильтр. Число в начале имени нужно для задания порядка его обработки. В нашем случае было важно, чтобы правило hello-usb отработало позже, чем 60-fido-id (т.к. оно выставляет тег security-device ). Поэтому мы выставили любое число больше 60 – это 99.
Подробнее о udev правилах можно прочитать здесь.
Обратите внимание на простоту этого скрипта. Это связано с тем, что udev может запустить несколько таких скриптов одновременно, а это может привести к состоянию гонки. Именно поэтому логика большинства фильтров крайне проста – передать событие uevent, который обработает его последовательно.
feature/hello-usb/lib/uevent/handlers/060-hello-usb – обработчик события hello-usb. Uevent передает управление обработчикам в соотвествии с их номером. В нашем случае порядок обработки нашего события не важен, и мы выставили случайный номер – 60.
Соберем make-initrd с добавленной фичей:
Настройка и генерация initrd образа
Обычно файл с конфигурацией выглядит следующим образом:
Здесь написано, что вся конфигурация будет определена автоматически. Увидеть «угаданную» конфигурацию можно с помощью команды:
Вы можете попросить make-initrd запускать только определенные guess модули. Например, можно попросить «угадывать» только модули ядра и фичи необходимые для монтирования корня и работы с клавиатурой:
Попробуем теперь собрать intird образ вместе с фичей hello-usb. Подправим конфигурацию
И запустим сборку образа:
Перейдем к проверке нашего образа на виртуальной машине.
Запуск в qemu
Проверку образа будем производить с помощью qemu. Установим необходимые пакеты:
В процессе работы мы планируем пробрасывать USB-устройство на виртуальную машину. Чтобы это сделать, необходимо узнать его адрес на USB-шине:
Мы хотим пробросить устройство, имеющее класс Mass Storage (флешку). В нашем случае оно находится на первой шине и втором порту.
Перейдем к запуску:
После запуска системы сначала отобразится вывод ядра, а затем вывод самого initrd. Если вы все сделали правильно, в какой-то момент увидите вывод обработчика событий hello-usb:
На этом, увы, ваша система зависнет:) Это связано с тем, что initramfs будет продолжать искать устройство с корнем, но оно не проброшено в qemu. К счастью, есть возможность экстренно перейти в rescue консоль: для этого надо нажать клавиши ** + **.
В ней можно изучить окружение initramfs образа:
* Убедитесь, что файловая структура, описанная ранее, соответствует действительности;
* Изучите список активных процессов;
* Посмотрите логи в /var/log/ ;
* Посмотрите содержимое других файлов.
Вы всегда можете запустить rescue консоль, если имеются какие-либо проблемы при запуске. В ней возможно:
Изучить логи сервисов;
Примонтировать корень вручную и запуститься с него;
Запустить отладчик (если добавлена фича debug-tools).
Передача аргументов ядра
Запустим qemu с аргументом ядра usb-vendor
Заметьте, если указать другое имя производителя, то в лог может ничего не вывестись.
Популярные опции ядра, которые поддерживает initramfs можно посмотреть здесь
Проверка на реальной машине
После того, как вы убедились, что initramfs образ работает в qemu, можно проверить его на реальной машине. Но здесь могут возникнуть некоторые нюансы.
Мы покажем, как это сделать, на примере GRUB.
Настройка аргументов ядра в GRUB
Откроем файл /etc/default/grub. Его содержимое выглядит примерно следующим образом:
Подробнее об опциях grub можно прочитать здесь
Для генерации конфигурации второго уровня выполним команду:
Разбор полученной конфигурации является уже более сложной задачей и выходит за рамки этой статьи. Тем не менее посмотреть на файл /boot/grub/grub.cfg все равно будет полезно:) Подробную информацию о его конфигурации grub можно найти здесь.
Добавим в аргументы ядра, параметр usb-vendor. Для этого зададим в файле /etc/default/grub переменную GRUB_CMDLINE_LINUX_DEFAULT :
И снова обновим конфигурацию:
Перезапустите систему и посмотрите на результат.
Вывод
Естественно, фича hello-usb не раскрывает всю мощь использования make-initrd. Если вы хотите рассмотреть более живые примеры фич, посмотрите исходники фич:
sshfsroot. – фича для загрузки с сетевых дисков по протоколу ssh;
luks – фича для загрузки с зашифрованных разделов;
raid – фича для загрузки с raid разделов.
В первые две, кстати, мы недавно добавили возможность для аутентификации по смарт-карте.
Обратите внимание насколько архитектура этих фичей совпадает с той, что мы реализовывали в hello-usb. Такая структурированность упрощает процесс разработки, и вы можете опираться на нее.
Make-initrd также удобен и для конечного использования. Такая крупная ОС, как ALT Linux, использует его в своих дистрибутивах в качестве основного генератора initramfs образов, в частности на таких продуктах, как “Альт Рабочая станция” и “Альт Сервер”. Это ли не показывает его широкую направленность?
На десктопных платформах вы можете добиться желаемого с помощью пары строк в конфигурационном файле, а порой полностью автоматически. Хотите добавить загрузку c сетевого раздела, используя sshfs? Просто напишите FEATURE += sshfsroot в файле конфигурации, а в опциях ядра укажите адрес сервера. Хотите добавить загрузку с RAID или LVM разделов? Ничего делать не надо! Если ваш корень лежит на таком разделе, make-initrd автоматически подберет правильную конфигурацию.
На серверных решениях вы можете добиться быстрой загрузки за счет высокой конфигурируемости образов. Вы всегда можете отключить любые ненужные вам фичи, тормозящие загрузку вашей системы. За счет обилия имеющегося функционала можно реализовать загрузку практически любой сложности. А если какого-то функционала будет не хавать, можно быстро реализовать его самостоятельно.
MKINITCPIO V31 и заглушки UEFI
Само внесенное мной изменение можно найти на GitHub.
Далее я коротко продемонстрирую, чем эта возможность хороша, как она упрощает запуск системы, и как с ее помощью можно повысить безопасность, используя, например, Secure Boot.
Процесс загрузки
Дело в том, что при запуске Linux через UEFI по факту мы запускаем двоичный файл Linux с набором команд, создавая точку входа. А поскольку UEFI сам является загрузчиком, то Linux можно запускать из него напрямую в виде загрузочной записи.
Защита цепочки загрузки
*Да, некоторые люди шифруют свои загрузочные разделы.
Решением будет вложить все эти составляющие в один исполняемый файл. Реализовать это доступным и вполне понятным способом позволяют образы EFI ядра.
Заглушки UEFI
Принцип такой: мы вставляем нужные данные в разделы двоичного файла, который затем подхватывается файлом заглушки.
Приведенный пример с Arch Linux создает исполняемый файл, содержащий информацию дистрибутива ( os-release ), считанные из файла параметры ядра, bmp-файл с логотипом дистрибутива, ядро и initramfs с микрокодом.
Подписав этот файл, мы в дальнейшем сможем аутентифицировать большинство других файлов, используемых в процессе загрузки. После этого его можно будет выполнять из оболочки UEFI без дополнительных аргументов, а также использовать загрузчиком*.
Прием этот очень прост, но из соображений безопасности большинство инструментов реализуют его по-разному. В данном случае ощутимо помогает возможность единой генерации этих файлов.
MKINITCPIO
При желании проработать последующий пример можете взять его предвыпускную версию из репозитория проекта. Любые полезные изменения кода и документация приветствуются.
Вот и все! Мы сгенерировали с помощью mkinitcpio заглушку UEFI!
Совет
Разбираемся с установкой и загрузкой Linux на примере ArchLinux
Сначала мы установим Archlinux и превратим его в загрузочный сервер. Прямо оттуда подготовим новую компактную систему, в которую добавим минимальное графическое окружение и самый необходимый функционал (на примере Firefox). Научим нашу систему загружаться по сети даже на компьютерах с UEFI. Затем полностью переведём её в режим «только для чтения» (сделаем «живой»), что позволит нам использовать систему одновременно хоть на пол сотне разномастных компьютеров с одним единственным загрузочным сервером. Это всё будет работать даже внутри дешёвой 100-Мб сети, которую мы дополнительно «разгоним» в пару раз.
Никакие закладки в жестких дисках будут вам не страшны, потому что дисков у нас не будет. Никакие очумелые ручки пользователей ничего не сломают, т. к. после перезагрузки система вернется в первозданное лично вами состояние. Конечно же, вы научитесь и сможете самостоятельно изменять загружаемую систему таким образом, чтобы в ней содержался только нужный вам функционал и ничего лишнего. Между делом мы выясним, как и в каком порядке загружается Linux, а также из чего он состоит. Знания, как известно, — бесценны, поэтому я делюсь ими даром.
Постараюсь без долгих рассуждений пояснять происходящее, иногда забегая немного вперёд, но впоследствии обязательно раскладывая всё по полочкам. Чтобы у вас вообще не возникало проблем с пониманием, предполагаю, что вы уже работали с каким-нибудь готовым дистрибутивом Linux, пробовали писать простые скрипты с помощью nano или другого текстового редактора. Если вы новичок в ArchLinux, то узнаете много нового, а если «старичок», то узнаете поменьше, но, надеюсь, что в любом случае вы ещё сильнее полюбите Linux.
Информации оказалось очень много. И по устоявшейся голливудской традиции впереди вас ждёт сериал в нескольких частях:
продолжение;
окончание.
Сейчас мы установим Archlinux в VirtualBox, который можно будет клонировать и запускать практически на любом компьютере с legacy BIOS без каких-либо дополнительных настроек. Между делом мы познакомимся с основными приёмами работы с systemd, а также узнаем как его использовать для запуска произвольных служб и программ во время загрузки. Ещё мы увидим, какие этапы проходит Linux при загрузке, и напишем собственный обработчик (hook), который поместим в initramfs. Не знаете что такое initramfs? Тогда заходите под кат.
Есть много причин, по которым выбор пал именно на Archlinux. Первая причина: он мой давний изворотливый приятель и верный помощник. Gentoo, как пишут на просторах Интернета, ещё более изворотлив, но собирать систему из исходников не хочется. Вторая причина: в готовых сборках всегда содержится много лишнего, а перекачивание больших объемов данных может критично сказаться на производительности сети, да и ничего не видно за широкой спиной «автоматического инсталлятора» — это третья причина. Четвертая: systemd постепенно проникает во все дистрибутивы и даже в Debian, так что мы сможем хорошенько покопаться в грядущем готовых дистрибутивов на примере Archlinux. При всём при этом, систему, которую мы позднее подготовим, можно будет загружать по сети не только сервера, работающего в виртуальной машине, но и с обычного компьютера, например, с Raspberry Pi, и даже с Western Digital My Cloud (проверено), который работает под Debian.
Подготовительные работы
Скачиваем свежий образ по ссылке с официального сайта. В Москве с серверов Яндекса, например, загрузка происходит очень быстро, и если у вас процесс затянулся — просто попробуйте качать в другом месте. Рекомендую запомнить в каком, т. к. эта информация нам ещё пригодится.
В VirtualBox создаем новую виртуальную машину (например, с 1 Гб оперативной памяти и 8 Гб накопителем). В настройках сети необходимо выбрать тип подключения «сетевой мост» и подходящий сетевой адаптер с доступом к сети Интернет. Подключаем скаченный образ к CD-ROM’у. Если не терпится начать работать с железом, то берите флешку и записывайте образ с помощью Win32 Disk Imager (если работаете под Windows), а потом загружайте будущий сервер прямо с неё.
Включаем машину, дожидаемся появления командной строки и устанавливаем пароль, без которого SSH работать не будет:
Запускаем сервер SSH командой:
Остается узнать IP адрес машины, изучив вывод команды:
Адрес будет указан сразу после «inet».
Теперь пользователи Windows смогут подключиться к машине с помощью putty, а потом будут копировать отсюда команды и вставлять их и нажатием правой кнопки мыши.
Базовая установка
Дальше максимально коротко опишу стандартную установку Archlinux. Если появятся вопросы, то ответы на них вы, вероятно, найдете в Подробном описании установки для новичков. Wiki просто замечательная, а англоязычная wiki даже актуальная, так что старайтесь пользоваться именно ей.
Подготавливаем носитель с помощью cfdisk (это консольная утилита с простым и понятным интерфейсом). Нам достаточно одного раздела, только не забудьте пометить его как загрузочный:
Форматируем в ext4 и устанавливаем метку, например HABR:
Будущий корневой раздел монтируем в /mnt:
Archlinux обычно устанавливается через интернет, поэтому сразу после установки у вас будет самая новая и актуальная версия. Список репозиториев находится в файле /etc/pacman.d/mirrorlist. Постарайтесь вспомнить, откуда скачивали дистрибутив и перенесите эти серверы в самое начало списка — так вы серьезно сэкономите время на следующем шаге. Обычно это серверы, географически расположенные там же, где вы сейчас находитесь.
Устанавливаем базовый набор пакетов и набор для разработчиков:
Теперь воспользуемся командой arch-chroot, которая позволяет временно подменить корневой каталог на любой другой, в котором есть структура корневой файловой системы Linux. При этом программы, которые мы оттуда запустим, не будут знать о том, что снаружи ещё что-то существует. Мы практически окажемся в нашей новой системе с правами администратора:
Обратите внимание, как поменялось приглашение командной строки.
Выбираем языки, которые планируем использовать. Предлагаю оставить en_US.UTF-8 UTF-8 и ru_RU.UTF-8 UTF-8. В текстовом редакторе нужно просто снять комментарии напротив них:
Теперь генерируем выбранные локализации:
Если всё прошло хорошо, то вы увидите примерно такой текст:
Устанавливаем язык, который будет использоваться по-умолчанию:
А также раскладку и шрифт в консоли:
Указываем часовой пояс (я использую московское время):
Придумываем название для нашего будущего сервера:
Теперь установим пароль администратора. Делаем мы это в первую очередь из-за того, что SSH не позволит нам подключиться к системе без пароля. Тему неразумности использования системы, незащищенной паролем, здесь мы развивать не будем.
Дважды вводим пароль и убеждаемся, что password updated successfully.
Добавим нового пользователя с именем username (можете выбрать любое), наделим его правами администратора и зададим ему пароль из тех же соображений, а ещё и из-за того, что под root в текущей версии Arch мы не сможем собирать пакеты из AUR (Arch User Repository — это репозиторий от сообщества пользователей Arch Linux с программами, которые не вошли в основной репозиторий):
Редактируем файл настроек /etc/sudoers с помощью nano:
Добавив в него сразу после строки «root ALL=(ALL) ALL» ещё одну строчку:
И задаём пароль для пользователя username:
Теперь нужно установить загрузчик на внутренний накопитель, чтобы система смогла самостоятельно с него загрузиться. В качестве загрузчика предлагаю использовать GRUB, потому что позже он нам снова пригодится. Устанавливаем пакеты с помощью стандартного для Archlinux менеджера пакетов pacman:
Записываем загрузчик в MBR (Master Boot Record) нашего внутреннего накопителя.
Если всё прошло нормально, то вы увидите Installation finished. No error reported.
И замечаем, как поменялось приглашение командной строки.
Мы будем использовать метки дисков, подробное объяснение этого утверждения последует позже.
Снимите комментарий со строки GRUB_DISABLE_LINUX_UUID=true, чтобы не использовались UUID накопителей:
Генерируем файл конфигурации загрузчика, снова используя arch-chroot. Будет произведён вход, выполнение одной единственной команды, и последует автоматический выход:
Нам нужно заменить все упоминания /dev/sda1 на LABEL=HABR в файле конфигурации:
Если поменять в этом же файле строку set lang=en_US на set lang=ru_RU, то загрузчик будет общаться с нами на великом и могучем.
На этом базовая установка ArchLinux закончена. Система будет загружаться самостоятельно и встретит вас приветливым русскоязычным интерфейсом командной строки. Если после этого мы введем команду dhcpcd, то скорее всего даже Интернет заработает. Но мы пока не будем торопиться с перезагрузкой.
Запуск при загрузке с помощью systemd на примере NTP и SSH
Поскольку наша система будет общаться с другими компьютерами, нам потребуется синхронизировать время. Если время на сервере и клиенте будет отличаться, то существует большая вероятность того, что они вообще не смогут соединиться друг с другом. В свою очередь sudo может начать просить пароль после каждого действия, думая, что таймаут авторизации давно истёк. И кто знает, с чем нам ещё предстоит столкнуться? Перестрахуемся.
Чтобы синхронизировать время с серверами через Интернет по протоколу NTP, нам нужно установить недостающие пакеты. Можно воспользоваться arch-root, но но мы обойдёмся ключами, которые сообщат новое место для установки менеджеру пакетов:
Настроим получение точного времени с российских серверов:
Нам достаточно синхронизировать время один раз при загрузке. Раньше мы бы записали запуск службы точного времени в файл rc.local, но сейчас появился менеджер системы и служб systemd, который старается запускать службы (в оригинале они называются unit) параллельно для уменьшения времени загрузки системы. Естественно, что работоспособность одной службы может зависеть от функционирования другой. Например, нам бесполезно пытаться синхронизировать время через Интернет до того, как у нас на компьютере заработает сеть. Чтобы описать все эти взаимосвязи, уже недостаточно простого указания имени исполняемого файла, поэтому запуск посредством systemd стал весьма нетривиальным занятием. Для этой цели были созданы специальные файлы с расширением «.service». В них указаны зависимости, имена исполняемых файлов и другие параметры, которые нужно учитывать для успешного запуска. В частности, для управления этапами загрузки в systemd используются цели (target), которые по возлагаемым на них задачам схожи с уровнями запуска (runlevel). Подробности читайте в вики.
В блоке [Unit] в строке Description указывается краткое описание службы, и при каких условиях она должна быть запущена (в данном случае, после запуска сети, но до перед запуском сервера NTP, который мы вообще не планируем запускать). Запрос точного времени происходит единственный раз во время загрузки, и за это отвечает строка Type=oneshot из блока [Service]. В этом же блоке в строке ExecStart указаны действия, которые необходимо выполнить для запуска сервиса. В блоке [Install] в нашем случае указано, что запуск нашей службы необходим для достижения цели multi-user.target. Рекомендуется использовать такое же содержание блока [Install] для запуска самодельных служб.
В качестве первого практического примера мы немного расширим функциональность ntpdate.service, попросив его дополнительно исправлять время на аппаратных часах. Если после этого, на этом же самом компьютере вы загрузите Windows, то увидите время по Гринвичу, так что не пугайтесь.
Изменение стандартного поведения любой службы systemd производится следующим образом: сначала в папке /etc/systemd/system/ создается новый каталог с полным именем службы и расширением «.d», куда добавляется файл с произвольным именем и расширением «.conf», и уже там производятся нужные модификации. Приступим:
Добавляем службу ntpdate в автозагрузку (синтаксис стандартен для всех служб):
Как видите, в каталоге multi-user.target.wants создалась обыкновенная символическая ссылка на файл ntpdate.service, а упоминание о цели multi-user.target мы видели в блоке [Install] этого самого файла. Получается для того, чтобы система достигла цели multi-user.target, должны быть запущены все службы из каталога multi-user.target.wants.
Теперь устанавливаем пакет SSH аналогичным способом (в ArchLinux он называется openssh):
Но на этот раз для автозапуска мы будем использовать сокет, чтобы сервер SSH стартовал только после поступления запроса на подключение, а не висел мёртвым грузом в оперативной памяти:
Мы не поменяли стандартный 22-й порт и не включили принудительное использование Protocol 2 — пусть это останется на моей совести.
Забегая вперед или знакомимся с обработчиками (hooks)
Чтобы мы могли не глядя подключиться к нашему будущему серверу, нам нужно знать его IP адрес. Будет намного проще, если этот адрес — статический. Обычные способы, о которых говорится в вики, нам не подходят. Проблема в том, что сетевые адаптеры в современном мире именуются согласно своему физическому расположению на материнской плате. Например, имя устройства enp0s3 означает, что это сетевой адаптер ethernet, который расположен на нулевой шине PCI в третьем слоте (подробности здесь). Сделано так для того, чтобы при замене одного адаптера другим, имя устройства в системе не поменялось. Такое поведение нам не желательно, т. к. на разных моделях материнских плат положение сетевой карты может быть разным, и когда мы попытаемся перенести наш загрузочный сервер из VirtualBox на реальное железо, нам скорее всего придётся загружаться с клавиатурой и монитором, чтобы правильно настроить сеть. Нам нужно, чтобы имя сетевого адаптера стало более предсказуемым, например, eth0 (это место зарезервировано смайликом).
Устанавливаем пакет mkinitcpio-nfs-utils, и у нас появится обработчик (hook) под названием «net»:
По-умолчанию, все файлы обработчика попадают в /usr/lib/initcpio/. Обычно это парные файлы с одинаковым названием, один из которых окажется в подкаталоге install, а другой — в hooks. Сами файлы являются обычными скриптами. Файл из папки hooks обычно попадает внутрь файла initramfs (позже мы о нём всё узнаем) и выполняется при загрузке системы. Второй файл из пары попадает в папку install. Внутри него есть функция build(), в которой находятся сведения о том, какие действия нужно выполнить во время генерации файла initramfs, а также функция help() с описанием того, для чего предназначен данный обработчик. Если запутались, то просто читайте дальше, и всё сказанное в этом абзаце встанет на свои места.
Папка initcpio также присутствует в каталоге /etc, и в ней тоже есть подкаталоги install и hooks. При этом она имеет безусловный приоритет над /usr/lib/initcpio, т. е. если в обеих папках окажутся файлы с одинаковыми названиями, то при генерации initcpio будут использоваться файлы из /etc/initcpio, а не из /usr/lib/initcpio.
Нам нужно немного поменять функциональность обработчика net, поэтому просто скопируем файлы из /usr/lib/initcpio в /etc/initcpio:
Приводим файл hooks/net к следующему виду:
Останется просто установить значение переменной, чтобы задать статический IP адрес и название сетевого устройства, например так «192.168.1.100::192.168.1.1:255.255.255.0::eth0:none» (здесь и далее используйте подходящие для себя настройки сети). В следующем разделе вы узнаете, где именно задаётся значение.
Когда во время загрузки менеджер устройств systemd-udevd попробует переименовать наше сетевое устройство в привычное ему predictable network interface name, например, в enp0s3, то у него ничего не получится. Почему — читайте дальше.
Как происходит загрузка системы
Для простоты рассмотрим обычные BIOS. После включения и инициализации, BIOS начинает по порядку идти по списку загрузочных устройств, пока не найдет загрузчик, которому передаст дальнейшее управление загрузкой.
Как раз такой загрузчик мы записали в MBR нашего накопителя. Мы использовали GRUB, в настройках которого (файл grub.cfg) указали, что корневой раздел находится на диске с меткой HABR. Вот эта строка целиком:
Здесь упомянут файл vmlinuz-linux, который является ядром системы, а указатель на корневую систему является его параметром. Мы просим искать корневую систему на устройстве с меткой HABR. Здесь также мог бы быть уникальный для каждого накопителя UUID, но в этом случае при переносе системы на другой диск нам несомненно пришлось бы его изменить. Если бы мы указали положение корневой системы привычным для линуксоидов образом: /dev/sda1, то не смогли бы загрузиться с USB накопителя, т. к. это имя USB накопитель бы получил только будучи единственным накопителем в компьютере. Маловероятно, что в компьютере окажется ещё один накопитель с меткой HABR, но не стоит об этом забывать.
Здесь же устанавливается значение глобальной переменной «ip» для нашего обработчика «net» (не забудьте поменять адреса на используемые в вашей сети):
В соседней строке есть упоминание файла initramfs, с которым я обещал разобраться:
Далее при загрузке происходит следующее: загрузчик GRUB получает файлы vmlinuz и initramfs, сообщает им, где искать корневую файловую систему и передаёт им управление дальнейшей загрузкой.
Название initramfs образовано от initial ram file system. Это на самом деле обычная корневая файловая система Linux, упакованная в архив. Она разворачивается в оперативной памяти во время загрузки и предназначена для того, чтобы найти и подготовить корневую файловую систему нашего linux, который мы пытаемся загрузить в итоге. В initramfs есть всё необходимое для этих целей, ведь это настоящий «маленький линукс», который может выполнять многие обычные команды. Его возможности расширяются с помощью обработчиков (hooks), которые помогают сформировать новую корневую файловую систему нашего linux.
После того, как программы из initramfs выполнят свою работу, управление дальнейшей загрузкой передается процессу init подготовленной корневой файловой системы. В качестве процесса init Archlinux использует systemd.
Менеджер устройств systemd-udevd является частью systemd. Он, как и его старший брат, старается обнаруживать и настраивать все устройства в системе параллельно. Он начинает свою работу одним из первых, но уже после того, как наш обработчик net инициализирует сетевую карту ещё на на этапе работы initramfs. Таким образом, systemd-udevd не может переименовать используемое устройство, и имя eth0 сохраняется за сетевой картой в течение всего времени работы.
Готовим initramfs
Обязательно убираем обработчик autodetect. Он проверяет устройства установленные в данном конкретном компьютере, и оставляет только необходимые для них модули в initramfs. Нам этого не нужно, поскольку мы изначально рассматриваем возможность дальнейшего переноса системы на другой компьютер, который аппаратно скорее всего будет отличаться от используемой виртуальной машины.
Достаточный для наших целей список обработчиков включая созданный нами net выглядит следующим образом:
вставляем эту строку в файл mkinitcpio.conf, а старую комментируем:
На базе стандартного пресета linux создаем свой пресет habr:
И приводим его к такому виду:
Нам не нужна ветка ‘fallback’, которая удаляет из обработчиков autodetect, ведь мы его уже сами убрали, и нам не нужно дважды генерировать одинаковый файл initramfs с разными названиями.
Генерируем новый initramfs с помощью пресета habr:
Пишем службу обновления DNS для использования с systemd
Наша сетевая карта получает все настройки для того, чтобы работала сеть и Интернет. Но названия сайтов переводиться в IP адреса не будут, т. к. наша система не знает, какие серверы DNS следует для этого использовать. Напишем собственную службу для этих целей, которую при загрузке будет запускать systemd. А чтобы узнать что-то новое и не заскучать от однообразия, передадим информацию о названии сетевого устройства в качестве параметра, а список DNS серверов сохраним во внешнем файле.
Обновлением информации о DNS серверах занимается resolvconf. Нам идеально подходит синтакскис:
В импортируемом здесь файле IP адрес каждого сервера указывается в новой строке после ключевого слова nameserver. Можно указать сколько угодно серверов, но использоваться будут только первые 3 из них. В качестве примера воспользуемся серверами Яндекс. В этом случае файл, передаваемый в resolvconf, должен выглядеть вот так:
Нам нужно получать информацию о DNS серверах до того, как система будет уверена, что сеть полностью работает, т. е. до достижения цели network.target. Будем считать, что информацию о серверах нам достаточно обновлять один раз во время загрузки. И стандартно скажем, что нашу службу требует цель multi-user.target. Создаём файл запуска службы в каталоге со следующим содержанием:
Обратите внимание, что в названии файла update_dns@.service используется символ @, после которого можно указать переменную, и она попадёт внутрь файла, заменив собой «%i». Таким образом строка EnvironmentFile=/etc/default/dns@%i превратится в EnvironmentFile=/etc/default/dns@eth0 — именно это название внешнего файла, мы будем использовать для хранения значения переменных DNS0 и DNS1. Синтаксис как в обычных скриптах: «название переменной=значение переменной». Создадим файл:
И добавим следующие строки:
Теперь добавляем службу в автозагрузку не забывая указать имя сетевой карты после @:
Только что мы написали универсальный файл, обеспечивающий запуск службы. Универсальность заключается в том что, если в нашей системе окажется несколько сетевых адаптеров, то для каждого из них мы сможем указать свои собственные DNS серверы. Нужно будет просто подготовить набор файлов со списком серверов для каждого из устройств и запускать службу для каждого адаптера в отдельности указывая его имя после @.
Перед первым запуском
На этом первоначальная настройка закончена. Нам нужно загрузить установленный ArchLinux с внутреннего накопителя, чтобы произведённые нами изменения вступили в силу.
Отключаем готовую корневую систему:
И выключаем виртуальную машину:
Теперь можно отключить загрузочный образ из CD-ROM или достать флешку, после этого включаем машину и убеждаемся, что всё работает.




