Структура нашего линукса на кодере

топ 100 блогов levgem06.11.2022

Мы продаем железку для транскодирования, на которой находится упакованный нами линукс, т.е. прошивка (такая же как на роутере или ip-камере). Всё преднастроено и собрано в програмно-аппаратный комплекс.

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

Чтобы описать, что мы там навертели внутри, надо сначала описать, какие функции надо было реализовать средствами прошивки, почему не достаточно просто поставить убунту и успокоиться: 

  1. атомарный апгрейд и даунгрейд. Мы не просто меняем флюссоник, а можем добавить что-то в системные библиотеки, поменять версию SDK или ядра. Это всё работает только вместе и учить клиента (который не хочет этого делать) как апдейтить и откатывать ядро с пачкой библиотек — немыслимо.
  2. возможность настроить сеть и диски из прикладной программы (а наш флюссоник на железке выступает так же, как система настройки в каком-нибудь роутере) существует только если в линуксе файлы лежат на тех местах, где их ожидает программа. В линуксах несколько разных стандартов на настройку сети (и вообще тема глубокая), так что надо выбрать что-то одно и зафиксировать это.
  3. у нас ещё добавляется сложное и тонкое управление кодерами, это отдельные железки, которые добавляют интересных моментов и гонок в работе всей системы.
  4. мы очень упарываемся по автоматическим тестам, поэтому прошивка для железки обязательно тестируется в qemu. Это требует определенной степени автоматизации установки прошивки и её настройки через внешние механизмы

Прошивка

Сам дистрибутив линукса (прошивка) хранится у нас в readonly файле, упаковываем в squashfs. Это всё кроме ядра: все библиотеки, программы и прочее. На данный момент у нас пока один файл, возможно скоро будем учиться добавлять второй.

Рядом с таким образом прошивки лежит ещё ядро и uefi скрипт из пары строк, позволяющий забутить именно этот комплект.

Как вообще готовится образ линукса, способный работать из readonly образа, причем под обе архитектуры сразу?

Всё начинается с:

RUN qemu-debootstrap --verbose --arch=${ARCH} --variant=minbase --components=main,universe --include=${EXTRA_PACKAGES} bionic

Тут две ключевых штуки:  qemu и debootstrap. Они нужны для того, чтобы спокойно на сборочном amd64 сервере можно было собирать arm64 прошивку.

Ах, да, это всё собирается конечно же докером, тут без альтернатив вообще.

После того, как мы доставим всё что нам нужно, пора приготовить систему для того, чтобы она была управляемой из флюссоника.

Systemd

В качестве основы управления линуксом я выбрал systemd. Он отвечает за:

  1. загрузку системы и старт всех демонов
  2. настройку и переименование сетевых интерфейсов (это очень глубокая и сложная проблема)

Управление сетью очень сложная штука. Вроде как айпишники раскидать не проблема, но ведь есть такая фигня, как множество портов на кодере. Есть 100-мегабитное подключение по которому ходят к API и через него скачиваются апдейты, а есть 4 гигабитных порта. Штука вся в том, что когда запрос приходит к гигабитного порта, он и вернуться должен обратно.

В простой конфигурации ответ на запрос пойдет через дефолтный 100-мегабитный порт, что неправильно. Для правильной работы надо настраивать table routing, а оно болезненно сочетается с DHCP на интерфейсе и маршрутом по-умолчанию именно на этом интерфейсе. Systemd позволяет всё это сочетать, причем с декларативной настройкой, без рантайм скриптов.

Всякие NetworkManager и прочие подобные непонятные мне штуки я выкинул и забыл про них на старте.

Однако чтобы сеть работала и можно было переименовывать и управлять интерфейсами, надо всё подготовить на уровне initrd. И он у нас свой собственный.

initrd

В нормальной убунте ядро идет скомпиленное, а initrd перегенерируется под конфигурацию системы. Есть ряд процедур, которые приводят к перегенерации initrd, в частности выбор тех модулей, которые должны быть загружены в ядро для получения доступа к rootfs. У меня модулей на кодере кажется нет вообще (может недавно что-то добавили, но только для опциональных вещей).

initrd у нас написан свой при помощи alpinelinux и busybox. Alpine за то, что там прям совсем всё порезано до минималки (прежде всего libc) и то, что мне нужно весит сильно меньше, чем минималка на убунте.

Дальше начинается особая хитрость: initrd на инсталяцию и работу одинаковый, что позволяет его запихнуть в ядро.

Инсталяция — это наша фишечка. Отдаем клиенту образ флешки, он её втыкает в USB, загружается и без каких-либо вопросов и сомнений стирается под ноль весь сервер и перезаливается нулевая прошивка. Главное в стойке железкой не промахнуться.

Итак, действия initrd:

  1. монтирует всякие tmpfs, devpts и прочие sys
  2. монтирует диск, на котором хранятся образы прошивок
  3. монтирует прошивку, которая соответствует той версии, к которой относится этот initrd и ядро. Версии, понятно, берутся из git describe и calendar versioning. От фуфла под названием semver отказались давно
  4. дальше идет магия udev. Очень много часов ушло на это, делюсь ценной информацией. До того, как стартует systemd-init обязательно запустить udev, чтобы он познакомился с железом и пометил его как конфигурируемое через udev, а не руками. Без этого systemd не возьмет над ним управление. Детали ниже
  5. для каталогов /etc/ и /var создается раздел для записи. Туда записывается то, что должно переживать рестарты (конфигурация, какие-то локальные базы данных)
  6. readwrite раздел (его пытаемся на старте смонтировать и если есть проблемы, то просто стираем, обнуляя девайс. Это практичнее, чем оставлять систему в разваленном состоянии с довольно бесполезной информацией на этом разделе) монтируется поверх squashfs через современный механизм overlayfs. Это хорошая штука, но в ней есть глюки, которые мы дорого и больно выясняли на продакшне. Фиксы ниже
  7. всё что насобирали, намонтировали перетаскивается (mount --move) в свежесобранный через overlayfs рут системы и дергаем switch_root для прыжка туда. Это аналог для exec только в масштабах линукса.

udev

udev ищет устройства (умеет делать это в рантайме) и пытается решить нерешаемую в общем случае задачу их идентификации. Что вот эта сетевуха, которая ожила на новом PCIe адресе — это та же самая, через которую полчаса назад был интернет.

Что за тайную магию пришлось вызывать в initrd:

echo "Launching preboot udev" > /dev/kmsg  
/lib/systemd/systemd-udevd --daemon --resolve-names=never  
udevadm trigger --type=subsystems --action=add
udevadm trigger --type=devices --action=add
udevadm settle || true  
echo "Udev configured" > /dev/kmsg

До этого в /lib (напоминаю, что это временный /lib от initrd) копируются все udev файлы из основной прошивки. В обычной убунте они копируются и пакуются в initrd, а мы это делаем в рантайме, потому что нам не требуется что-то особенное для поиска рутового устройства.

overlayfs

overlayfs позволяет «редактировать» файлы, лежащие на нижнем слое, вписывая информацию про них в rw раздел, лежащий на верхнем слое. Иногда эта хитрость разламывается и тогда никакими силами не починить этот файл, но его можно стереть. Часто ломается файл /etc/machine.id, тогда systemd не может забутиться.

Наша хитрость в том, чтобы перепроверить на fopen все файлы в /etc и /var при старте и если какой-то из них не открывается, то удалить его. Это лучше, чем остаться с залипшей нерабочей системой.

uefi

вся эта конструкция загружается на интеле через uefi. Он оказался очень простым, если выкинуть на помойку grub и воспользоваться systemd-uefi: файлик лежит в EFI/BOOT/BOOTX64.EFI  на UEFI разделе.

Туда мы складываем загрузчик для каждой версии фирмвари, этим можно управлять через консоль и из уже загруженного линукса, заказывая загрузку на следующий ребут. Получается, что выложили новую прошивку, заказали её одноразовый старт (в uefi есть такая опция). Если она запустилась, то фиксируется как постоянная, иначе откатываемся назад.

Сам раздел под UEFI у нас размером в очень много гигабайт, там как раз лежат все прошивки (несколько последних версий).


Верхнеуровнево получились такие заметки. Что ещё интересно из этого?


Оставить комментарий

Архив записей в блогах:
В Нижнем Новгороде мигрант прижался к девушке и начал мастурбировать, чем вызвал гнев пассажиров. Она пожаловалась на приставания, после чего специалисту и его другу дали по лицу и выкинули из транспорта. источник ...
      Невероятно стильный своеобразный криминальный нуар, тягучее, неспешное повествование - т ягучее, как знойный испепеляющий полдень в сердце пустыни Техаса. Необычайно атмосферное, погружающее в свою собственную, странную, но всё же привлекательную ...
Фентанил был разработан, чтобы убивать людей, это оружие массового поражения, уносящее десятки тысяч. в основном молодых жизней. Китай использует фентанил, чтобы убить множество белых людей по всему миру, по моему это факт. Фентанил при аналогах является опиатом как минимум в 100 раз ...
...
Благодаря любезному приглашению "БТК групп", единственного поставщика вещевого имущества для ВС РФ , я могу вам продемонстрировать окончательный вариант всесезонного комплекта базового обмундирования (ВКБО) - новой полевой формы, которая с этого года поступает в российские ...