commit -m "better"
2.96K subscribers
868 photos
105 videos
3 files
2.07K links
just random thoughts
Download Telegram
commit -m "better"
Вкратце напомню, что у меня системными сервисами управляет #runit. Поэтому, когда у меня происходит обновление system #realm, происходит перезагрузка всего дерева сервисов. Ну просто потому что меняются inode путей в папке /etc/services, так как /etc - это…
#runit, будни #bootstrap, #herobora #runsvdir.

У используемого и нежно любимого мной runit есть одна неприятная особенность (врочем, как и у остальных init-ов, унаследованных от daemontools).

Он хочет безусловно портить структуру директорий, в которых лежат сервисы.

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

Контрольки сервисов (./run файлы) у меня лежат в /etc, которая есть ссылка на какую-то папку в content addressable store.

Из этого следует простой вывод - runit у меня "портил" (и продолжает портить) папки (и данные) в cas, чего допускать, конечно, нельзя.

Я обкатывал несколько разных решений этой проблемы, которые, так или иначе, сводились к "теневой" копии сервисов из /etc "куда-то", где можно писать. Например, регулярным копированием /etc/services куда-то в /var/run/, или, вот, с помощью rw overlay unionfs куда-то в tmpfs.

Короче, совершенно всратые решения, ошметки которых я даже не хочу показывать.

Смотрел на другие "простые" супервизоры, но нет.

В общем, после нескольких лет попыток придумать что-то не столь всратое, я плюнул, и переписал runsvdir - это часть runit, которая отвечает именно за управление деревом процессов.

Эдакий сплав (она же херобора) из обычного runit для pid1, и моей запчасти, с таким же интерфейсом, ее так же зовут скрипты инициализации runit, но с нужной мне семантикой.

Та-да-да-дам!

https://github.com/stal-ix/ix/blob/main/pkgs/bin/ix/runsvdir/run.py

Да, на python, потому что для прототипирования python подходит очень хорошо, и я уже смирился с тем, что он у меня есть в базовой системе (при этом я упаковал в один бинарь со всеми зависимостями, поэтому он не мозолит глаза своей россыпью файлов по всей fs).

Когда его семантика устаканится, перепишу на go, или даже на rust (не хочет кто-то взяться? там строк 50-100 идиоматичного rust-о кода должно быть).

Что я могу сказать?

* Мне кажется, это самый компактный супервизор процессов, который я когда-либо видел/делал, при этом, с приличным набором фич. Он умеет пасти структуру директорий, такую же, как у runit, но ничего не пишет на диск. Сервисы могут добавляться и убираться "на лету".

* Удивительно, но я вот написал этот скриптец, ребутнул машину, ожидая debug и recovery окирпиченного хоста, но ничего такого не случилось - все завелось сразу, с полпинка, потому что поверхность у runsvdir - очень простая и компактная.

* В принципе, теперь я могу там запилить недостающие фичи из runit, типа динамически создаваемых юнитов. Захочу ли - пока не знаю.
🔥26👍53🤔1
commit -m "better"
#runit, будни #bootstrap, #herobora #runsvdir. У используемого и нежно любимого мной runit есть одна неприятная особенность (врочем, как и у остальных init-ов, унаследованных от daemontools). Он хочет безусловно портить структуру директорий, в которых лежат…
Я, когда рассказывал про свою замену #runit, упустил один важный момент.

Если до того, как я заменил #runsvdir, у меня рестартовало вообще все дерево процессов после модификации system #realm (ну потому, что папка с симлинками на сервисы появлялась новая, inode у этих симлинок новый, и runit рестартовал вообще все), то после перестало рестартовать вообще все, даже в тех ситуациях, когда сервис поменялся, и рестарт был нужен.

Потому что мой скрипт смотрит не на inode, а на контент + путь до скрипта, и вот эта вот пара, на самом деле, меняется очень редко.

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

Вот и получается, что программа поменялась, а скрипт запуска - нет, и ничего не рестартует.

Самый очевидный способ это починить - сделать так, чтобы runit скрипт зависел (по сборке) от программы, которую он запускает, но это приводит к некоторым проблемам:

* build зависимость всегда host, а нужна зависимость от target.

* может оказаться так, что программа собирается с каким-то пользовательским флагом, тогда, по сути, одна и та же программа соберется два раза (под host, без флага, и под target, с флагом)

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

Ну и записал этот uid в тело runit скрипта - https://github.com/pg83/ix/blob/main/pkgs/etc/services/runit/script/ix.sh#L32

Использование выглядит вот так - https://github.com/pg83/ix/blob/main/pkgs/bin/chrony/runit/ix.sh#L4-L5 По сути, просто передаем в пакет с runit скриптом флаг (srv_deps=...), в котором лежит список всех зависимостей для этого скрипта.

С одной стороны, какое-то странное, неестественное, действие, с другой - теперь можно не рестартовать сервисы, когда что-то поменялось, но не очень хочется (например, пользовательские сессии)
🤔10👍63🔥1
Тем временем, у моей реализации #runsvdir https://t.iss.one/itpgchannel/1998 уже есть 4 open source альтернативы:

* оригинал из runit
* мой вариант на python
* вариант на Rust (https://github.com/maratik123/runsvdir)
* и мой же вариант на С++ (https://github.com/pg83/ix/blob/main/pkgs/bin/ix/runsvdir/cpp/m.cpp)

На приложенном скриншоте - все 4, на одной и той же конфигурации.

Хуже всех (по потреблению памяти) python, потом идет оригинальный из runit (меня это удивило, он, вроде как, на С), потом вариант на Rust, а пижже всех - мой, на С++.

Оптимизациями памяти я даже не занимался (посмотрите код, он максимально дубовый), можно лучше.
17👍6🔥4🤔3🤯2🤡1🆒1
commit -m "better"
В общем, после нескольких лет попыток придумать что-то не столь всратое, я плюнул, и переписал runsvdir - это часть runit, которая отвечает именно за управление деревом процессов.
#herobora

В итоге, мой #runsvdir у меня прижился, и, как понятное следствие, я окончательно отказался от ошметков #runit:

* https://github.com/pg83/ix/blob/main/pkgs/bin/ix/init/ewontfix/main.c - свой элементарный init.

* https://github.com/pg83/ix/blob/main/pkgs/bin/ix/init/rc/ix.sh#L9-L11 - клей между моим init, и схемой загрузки "как в runit", чтобы можно было заменять туда-сюда.

* https://github.com/pg83/ix/blob/main/pkgs/bin/ix/init/halt/main.c https://github.com/pg83/ix/blob/main/pkgs/bin/ix/init/reboot/main.c - реализации halt/reboot, они, как ни странно, тоже часть init. Если такой способ (без graceful shutdown) кажется странным - читаем мои заметки про #reboot, https://t.iss.one/itpgchannel/1572.

* https://github.com/pg83/ix/blob/main/pkgs/bin/ix/pid1/m.cpp#L157-L175 - пришлось перенести в runsvdir код, который убивает orphane процессы, в runit у меня это был cron job на shell (https://github.com/pg83/ix/blob/main/pkgs/bin/sched/stale/procs/scripts/staleprocs.sh), но в новой схеме так не получается, потому что сервисы сразу начинают наследоваться от pid 1, а не от pid > 1, поэтому инвариант "все, что подвисло к pid 1, но не runsvdir - мусор" перестал работать.

Так что теперь у меня официально полностью велосипедный in house init!

С точки зрения скриптов инициализации ничего не поменялось, ага.
👍11🔥74🆒3