Установка nos на выделенный сервер
Разверните системный образ nos на свободный раздел Linux-сервера, загрузите его один раз через GRUB самого сервера и войдите по SSH.
В итоге на физическом сервере x86-64 будет работать nos со своего собственного раздела: ядро и все модули из системного образа релиза, загруженные через GRUB самого сервера, и SSH-сервер nos, который пускает по выданному вами ключу. Linux на сервере остаётся на месте и остаётся загрузкой по умолчанию: nos загружается один раз, когда вы об этом просите, а следующая загрузка — снова Linux.
Понадобится:
- сервер x86-64 с Linux и GRUB 2, root-доступ к нему и оборудование, для которого у nos есть драйверы (см. раздел «Сначала проверьте сервер»);
- свободный обычный раздел на нём, не меньше 64 МиБ;
- ключ
ssh-ed25519: sshd в nos других типов не принимает; - возможность перезагрузить сервер снаружи (кнопка сброса или API хостинг-провайдера) — на случай, если nos не поднимется.
Так nos работает на собственных серверах проекта в Hetzner: со своего раздела, загружаясь по одному разу рядом с Linux сервера. Полный справочник — страница Install: nos on a server.
Почему это безопасно
Установка устроена так, что неудачная загрузка nos стоит вам одной перезагрузки, а не сервера:
- У nos свой раздел. На нём файловая система ext2, которую nos монтирует как корневую. В Linux на сервере
nos-installдобавляет один файл,/etc/grub.d/42_nos, и пересобираетgrub.cfg. Больше ничего не трогается. - Пункт загрузки по умолчанию не меняется. Загрузку nos «взводит» флаг
nos_next=nosв блоке окружения GRUB на разделе nos. GRUB читает флаг, один раз загружает nos и сбрасывает флаг до того, как что-либо загрузить. Первый пункт самого сервера остаётся запасным. rebootвнутри nos возвращает в Linux, потому что флага уже нет.- От nos, который так и не поднялся, до Linux — одна перезагрузка. После паники, зависания или ядра, которое GRUB не смог загрузить, откатывать нечего.
- После паники nos перезагружается сам. Пункты GRUB из образа передают ядру
panic=10: через десять секунд после отчёта о панике машина перезагружается, и после однократной загрузки это означает Linux.
Сначала проверьте сервер
nos работает только на оборудовании, для которого у него есть драйверы, а единственная консоль сервера — его сеть. nos-install проверяет каждый пункт ниже и сообщает, что нашёл: неподходящий раздел он отвергает, об остальном предупреждает. Исправить что-либо из этого он не может.
| Что нужно | Почему |
|---|---|
| Диск NVMe или virtio под раздел nos (virtio-scsi тоже подходит) | nos читает только такие диски. С диска SATA или из-за RAID-контроллера GRUB всё равно загрузит ядро через прошивку, но nos не найдёт на нём своего корня: ни модулей, ни /etc/rc, ни sshd. |
Сетевая карта с драйвером в nos: Realtek RTL8168 или RTL8125, Intel 82576, I210 или I211 (драйвер igb) либо virtio-net |
С любой другой картой nos загрузится, но достучаться до него будет нельзя. |
| DHCP в этой сети | Статической настройки IP в nos нет. |
| Обычный раздел не меньше 64 МиБ (с 256 МиБ свободнее; дискам гостевых систем нужно больше) | Он не может входить в md RAID, быть томом LVM или быть зашифрован: ничего из этого nos не читает. На сервере, где диски целиком зеркалированы, такого раздела нет, поэтому оставьте под него место при установке сервера (installimage в Hetzner принимает список разделов) или создайте его из свободного места. |
| Linux, загружаемый через GRUB 2, BIOS или UEFI, с выключенным Secure Boot | nos — ядро Multiboot2, а GRUB в режиме блокировки Secure Boot такое ядро загружать отказывается. |
e2fsprogs, util-linux, утилиты GRUB (grub-editenv, а также update-grub или grub-mkconfig) и поддержка squashfs в ядре или unsquashfs |
Всё это запускает nos-install. В стандартных Ubuntu и Debian всё есть. |
Скачайте системный образ
Устанавливается системный образ nos-x86_64.squashfs. Он собирается для каждого тега релиза. На сервере:
wget https://github.com/irqlevel/nos/releases/latest/download/nos-x86_64.squashfs
wget https://github.com/irqlevel/nos/releases/latest/download/SHA256SUMS
sha256sum --check --ignore-missing SHA256SUMS
Что внутри:
| Путь | Что это |
|---|---|
/boot/kernel64.elf |
ядро |
/boot/grub/nos.cfg |
его пункты GRUB, nos и nos (next), с той командной строкой, под которую собрано ядро |
/lib/modules/*.ko |
все модули, собранные вместе с этим ядром, поэтому каждый из них в него загружается |
/etc/rc |
скрипт загрузки по умолчанию: insmod /lib/modules/sshd.ko, затем sshd start |
/nos-install |
установщик, соответствующий этому образу |
/VERSION |
version=, revision=, arch=, module_abi= |
В образе нет ничего, что относилось бы к конкретной машине: ни ключа хоста, ни разрешённых ключей, ни адреса. Владельцы, времена и права файлов зафиксированы, поэтому сборка каждый раз даёт одни и те же байты, и sha256 образа однозначно определяет его содержимое.
Чтобы собрать образ самому, выполните make image в Linux с установленным тулчейном или scripts/build-image.sh везде, где работает Docker (--clean собирает с нуля, как при релизе). В обоих случаях nos-x86_64.squashfs появится в корне дерева. См. Сборка nos.
Разверните образ на раздел
Под root на сервере смонтируйте образ и запустите лежащий в нём nos-install. Замените /dev/nvme0n1p4 на свой свободный раздел:
mount -o loop,ro nos-x86_64.squashfs /mnt
/mnt/nos-install install /dev/nvme0n1p4 --ssh-key /root/.ssh/authorized_keys
umount /mnt
--ssh-key принимает файл .pub, файл authorized_keys или строку ключа и может повторяться. Если передать /root/.ssh/authorized_keys самого сервера, в nos будут пускать те же ключи, что уже пускают в Linux. Из файла nos-install берёт строки ssh-ed25519 и сообщает, какие пропустил: ключи других типов и строки с опциями перед ключом. Отдельно переданная строка ключа другого типа, как и файл без единого ключа ssh-ed25519, отвергаются. Сама опция необязательна, но без ключа sshd не пустит никого, а если ещё и UDP-шелл выключен, в загруженный nos не попасть вообще.
install проходит эти шаги и останавливается на первой же проблеме:
- Проверяет образ и раздел. В образе должны быть
/VERSION, ядро и модули, и он должен быть для архитектуры этой машины. Раздел должен быть блочным устройством, не может быть целым диском с разделами, не может быть занят md, LVM или device mapper и не может быть одной из файловых систем работающей системы.installпредупреждает, если диск не NVMe и не virtio, если нет сетевой карты с драйвером в nos и если включён Secure Boot. - Готовит файловую систему. Пустой раздел получает файловую систему ext2 с меткой
nosв том формате, который пишет nos. Раздел с ext2, в которую nos умеет писать, сохраняет всё своё содержимое. Всё остальное отвергается, если не передан--format, который создаёт новую файловую систему, и прежнее содержимое теряется. - Копирует систему. Монтирует раздел и кладёт туда ядро, модули,
nos.cfgи/VERSION. Если на разделе они уже были, старые три переезжают в.prevвместе, для последующегоrollback./etcпринадлежит машине:/etc/rcиз образа кладётся, только если своего нет, а ваши ключи дописываются в/etc/ssh/authorized_keys. Если процессор Intel и у сервера есть для него файл микрокода в/lib/firmware/intel-ucode, этот файл копируется в/boot/ucode, и GRUB загружает его рядом с ядром. Если файла нет (процессор AMD или не установлен пакетintel-microcode), nos работает с микрокодом, загруженным прошивкой. - Пишет
/grubenvна раздел. Там параметры ядра для этой машины и флаг однократной загрузки.e2fsckпроверяет файловую систему до записи и после неё. - Добавляет пункт в GRUB. Пишет
/etc/grub.d/42_nos, который находит раздел по UUID, запускаетupdate-grub(илиgrub-mkconfig) и проверяет, что вgrub.cfgон появился.
Опции install и update:
| Опция | Что делает |
|---|---|
--ssh-key KEY |
Добавляет ключ, по которому пускает sshd. |
--udpshell[=PORT] |
Включает UDP-шелл на порту 9000 или PORT. Выключен, если не задан: он выполняет любую команду для любого, кто может отправить машине датаграмму (UDP shell). |
--netconsole IP:PORT |
Шлёт лог ядра по UDP на IP:PORT (Netconsole). Это единственный способ увидеть загрузку, которая умирает уже после подъёма сети. |
--cmdline "ARGS" |
Добавляет параметры ядра (Kernel parameters). |
--format |
Создаёт на разделе новую файловую систему, даже поверх существующей. |
--boot-once, --reboot |
Взводит одну загрузку nos на следующую загрузку; --reboot вдобавок сразу перезагружает. |
--image FILE |
Указывает образ, если запускается не тот nos-install, что лежит внутри него. |
Загрузите nos один раз
Взведите одну загрузку и перезагрузитесь. Вместо этого можно передать --reboot в install или update:
mount -o loop,ro nos-x86_64.squashfs /mnt
/mnt/nos-install boot --reboot
boot записывает nos_next=nos в /grubenv на разделе. При следующей загрузке GRUB читает его через 42_nos, делает пункт nos загрузкой по умолчанию на этот один раз, оставляя запасным первый пункт сервера, и сбрасывает флаг, прежде чем что-либо загрузить.
Командная строка ядра — root=UUID=<partition-uuid> dhcp=auto dns=on panic=10 (UUID раздела nos) плюс то, что задано для этой машины через --udpshell, --netconsole и --cmdline. nos монтирует раздел как корень на чтение и запись, получает адрес по DHCP и выполняет /etc/rc. /etc/rc из образа загружает и запускает sshd. При первом запуске sshd создаёт ключ хоста из пула энтропии ядра и хранит его в /etc/ssh/ssh_host_ed25519_key для всех следующих загрузок. Пускает он по ключам из /etc/ssh/authorized_keys.
Примерно через минуту после перезагрузки, со своей машины:
ssh -o UserKnownHostsFile=~/.ssh/known_hosts.nos root@<server> version
Команда выведет nos <version> (<revision>). Отдельный файл known_hosts нужен потому, что у nos собственный ключ хоста, а не тот, что ваш known_hosts уже помнит для Linux по этому адресу. Чтобы у обеих систем был один ключ хоста, до первой загрузки скопируйте /etc/ssh/ssh_host_ed25519_key из Linux по тому же пути на раздел nos: sshd в nos читает формат OpenSSH.
Когда закончите, reboot в nos вернёт сервер в его Linux.
Или разверните всё со своей машины
scripts/nos-deploy.py делает всё описанное выше с машины, где лежит образ, по SSH в Linux сервера под root:
scripts/nos-deploy.py root@<server> install /dev/nvme0n1p4 --ssh-key ~/.ssh/id_ed25519.pub
scripts/nos-deploy.py root@<server>
Первая команда устанавливает. Вторая, без команды, означает update — её и запускают каждый следующий раз. Скрипт:
- копирует образ на сервер, проверяет там его sha256 и монтирует;
- запускает лежащий в образе
nos-installс--boot-onceи перезагружает сервер; - ждёт, пока на порту 22 ответит собственный sshd nos с баннером
SSH-2.0-nos_sshdвместо прежнего OpenSSH; - входит и проверяет, что работает ядро из образа.
Его опции:
| Опция | Что делает |
|---|---|
--image FILE |
Берёт этот образ вместо образа по умолчанию — nos-x86_64.squashfs в корне дерева (от make image или scripts/build-image.sh). |
--no-boot |
Кладёт образ на сервер и останавливается перед перезагрузкой. |
--wait SECONDS |
Задаёт, сколько ждать подъёма nos (по умолчанию 600). |
--ssh-key, --udpshell[=PORT], --netconsole IP:PORT, --cmdline ARGS, --format |
Передаются в nos-install. |
Проверьте, что установлено
status запускается из смонтированного образа и опций не принимает:
mount -o loop,ro nos-x86_64.squashfs /mnt
/mnt/nos-install status
Он называет пункт GRUB и раздел, что установлено сейчас и что было до этого, параметры ядра, взведена ли загрузка, сколько ключей SSH и ревизию микрокода. На сервере с AMD сразу после первой установки это выглядит так:
GRUB entry: /etc/grub.d/42_nos, UUID <uuid>
partition: /dev/nvme0n1p4
installed: nos <version> (<revision>)
previous: none
parameters: none
next boot: this machine's default
ssh keys: 1
microcode: none; nos runs firmware's
Обновление, откат, удаление
Чтобы поставить более новый образ поверх текущего, скачайте его, как в разделе «Скачайте системный образ», и выполните:
mount -o loop,ro nos-x86_64.squashfs /mnt
/mnt/nos-install update --reboot
update находит раздел через 42_nos, так что указывать его снова не нужно. Новые ядро, модули и nos.cfg кладутся на место. Старые ядро, модули и /VERSION вместе уезжают в .prev, а микрокод заново копируется из /lib/firmware сервера. /etc (rc, ключи, ключ хоста) и всё остальное на разделе остаются. Параметры ядра тоже остаются, если не передан --udpshell, --netconsole или --cmdline. Любая из этих опций заменяет все параметры целиком, а --cmdline "" их очищает.
Ядро и его модули всегда заменяются только вместе: ядро отвергает модуль, собранный под другой интерфейс ядра. /lib/modules принадлежит образу, поэтому собственные модули держите в другом месте.
Остальные команды тоже запускаются из смонтированного образа:
/mnt/nos-install rollbackменяет местами текущие ядро, модули и/VERSIONс версиями из.prev./mnt/nos-install uninstallудаляет42_nos, пересобирает конфигурацию GRUB, чтобы nos пропал из меню, и не трогает раздел.
Как опробовать ядро изнутри nos
Работающий nos может опробовать ядро из релиза, прежде чем оно заменит установленное. Выполните это в шелле nos:
wget https://github.com/irqlevel/nos/releases/download/<tag>/kernel-x86_64.elf /boot/kernel64.elf.next
sha256 /boot/kernel64.elf.next
grubenv /grubenv nos_next=nos-next
reboot
Сверьте вывод sha256 с SHA256SUMS релиза. После этого один раз загрузится пункт nos (next). Модулей он не приносит: модули в /lib/modules принадлежат работающему ядру, и новое ядро их отвергнет, если у них другой интерфейс ядра. Образ целиком обновляйте из Linux.
Как вернуться в Linux
- Из работающего nos: выполните
reboot. Флага однократной загрузки уже нет, поэтому GRUB загрузит пункт сервера по умолчанию. - Если nos завис или не отвечает: перезагрузите сервер снаружи. GRUB сбросил флаг ещё до загрузки nos, так что поднимется Linux.
- После паники: подождите.
panic=10перезагрузит машину через десять секунд после отчёта. - Чтобы убрать nos из меню: выполните
nos-install uninstallиз Linux, как описано выше.
Если вы хотите оставаться в nos между перезагрузками, сделайте его загрузкой сервера по умолчанию: задайте GRUB_DEFAULT=nos в /etc/default/grub и выполните update-grub.
Если nos — загрузка по умолчанию, то nos, который не поднимается, будет загружаться снова после каждого сброса, и выбраться можно будет только через rescue-систему хостинг-провайдера.
Если что-то пошло не так
nos-deploy: <server> came back with <banner>, not nos -- GRUB did not boot it. Сервер вернулся в Linux, а не в nos. Проверьте, что Secure Boot выключен. Ещё поищите собственный пункт сервера с id nos или nos-next, например созданный вручную в /etc/grub.d/40_custom: он стоит в grub.cfg первым, и GRUB загружает именно его. nos-install предупреждает, если находит такой пункт.
no nos on <address>:22 after 600 s. nos либо погиб, либо поднялся, но недоступен. Перезагрузите сервер, чтобы вернуть Linux, и выясните причину:
- Переустановите с
--netconsole IP:PORT, указав машину под вашим контролем, и запустите тамpython3 scripts/netconsole.py -p PORT. Лог ядра, включая отчёт о панике, придёт по UDP (Netconsole). - Для загрузки, которая умирает до подъёма сети, используйте
disklog=on. Ему нужна область на диске, подготовленнаяscripts/disklog.py format(Kernel parameters).
ssh отвергает ключ хоста или предупреждает, что он изменился. Ключ хоста nos не совпадает с ключом Linux на сервере. Используйте для nos отдельный файл known_hosts, как в разделе «Загрузите nos один раз», или скопируйте ключ хоста Linux на раздел.
sshd в nos отвергает ваш ключ. Он принимает только ключи ssh-ed25519. Добавьте такой через --ssh-key при следующем update или командой sshd allow изнутри nos (sshd).
WARNING: ... is not on an NVMe or a virtio disk. GRUB загрузит с него ядро, но nos не найдёт там своего корня, а значит, не будет ни sshd, ни способа войти. Разместите раздел на диске NVMe или virtio.
... is in use by ... (RAID, LVM, device mapper). nos нужен обычный раздел: ничего из перечисленного он не читает.
... has a <type> filesystem on it, which nos does not take. Передавайте --format, только если готовы потерять содержимое этого раздела.
/etc/rc on it runs 'insmod ...': that module is not the image's. После обновления модуль, загружаемый не из /lib/modules, оказывается от другой сборки, и новое ядро его отвергает. Пересоберите его под новое ядро.
e2fsck finds ... damaged. Выполните из Linux e2fsck -f <partition>, чтобы починить файловую систему, и запустите nos-install снова.
Что дальше
- Подключение к работающему nos: SSH и UDP-шелл, и что можно делать после входа.
- Linux-гость под гипервизором nos: на сервере модуль гипервизора уже лежит в
/lib/modules/hv.ko. - Загружаемый модуль ядра на Rust; собственные модули держите вне
/lib/modules. - Install: nos on a server — полный справочник; Real hardware — машины, на которых работает nos.