Давно не писал про systemd... ибо он просто не работает

Что: 6d663c82fdf9b0534da062f02f639730dcc235f1

Когда: 2020-08-21 13:50:00+03:00

Темы: systemd

Давно не писал про systemd... ибо он просто не работает

Вчера с коллегами поднимали NSD сервер на современном CentOS.
systemctl start nsd отрабатывает, успешный код возрата, ничего не
говорит. А демона нет. В логах, выясняется, что nsd то в принципе не
запускался потому что :53 порт занят. Если запустить nsd руками, то он
честно возвращает плохой код. Что это значит? Значит что systemd даже с
своей первоочередной задачей не справился, не работает -- отвечает что
всё хорошо, хотя демон честно вышел с ошибкой. Ну и как с этим дерьмом
работать? Вопрос риторический конечно же и для меня это как Windows --
с этим я просто не связываюсь.

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

комментарий 0:

From: kmeaw
Date: 2020-09-11 21:13:28Z

Сталкивался с той же проблемой несколько лет назад, когда писал
cgroup manager, и хотел уметь запускать его из sysvinit, runit и
upstart.

Концептуально проблема с том, что сервис может или сам заниматься
демонизацией, или отдать эту задачу супервизору.

В первом случае это усложняет работу супервизора:

У Upstart есть хак, который подключается через ptrace к процессу и ждёт,
пока он сделает fork или double fork. В systemd есть Type=forking, но
деталей реализации я не знаю.

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

Также программа самостоятельно решает, куда она запишет pid-файл.

Первый случай хорош только для init-скриптов. В этом случае не надо
использовать start-stop-daemon --background --make-pidfile, а достаточно
просто запустить программу, а дальше она всё сделает сама.

Ещё далеко не все разработчики умеют правильно демонизировать процессы.
Кто-то забудет файловые дескрипторы позакрывать или ещё что-нибудь.
Кажется в nginx была проблема, которая проявлялась, если после
демонизации поменять размер окна эмулятора терминала - рабочему процессу
nginx прилетал SIGWINCH, и он завершался.

Во втором случае это усложняет процедуру оповещения пользователя об
успешном старте:

Пользователь ожидает, что запуск service XXX start выдаст ошибку, если
что-то где-то неправильно сконфигурировано, и нормальная работа сервиса
невозможна.

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

Upstart предполагает, что процесс пошлёт себе SIGSTOP. Мне это решение
очень нравится — его легко использовать даже в ограниченных окружениях
(chroot, namespaces), и процессы обычно так не делают в любых других
ситуациях.

В systemd есть шина, на которую сервис должен послать сообщение о своей
готовности. Для этого нужна библиотека, которая умеет к ней подключаться
и доступ к сокету шины. Я хотел затащить вариант Upstart в systemd, но
нашёл тикет, в котором кто-то уже предлагал такую возможность, и Леннарт
Поттеринг закрыл его с WONTFIX с мотивировкой "не вижу, чем вариант с
raise(SIGSTOP); проще варианта с sd_notify("READY=1");".

Кажется ещё в systemd и upstart есть возможность подождать указанное в
конфиге время. Если сервис упадёт раньше, чем это время пройдёт, значит
он сломался.

В случае с init-скриптами обычно вставляют цикл, который проверяет
живость процесса и готовность сервиса работать — либо по маркерному
файлу, либо попыткой послать в него ping-запрос.

С runit я нормального решения не придумал. В итоге пошёл на
TOCTOU-компромисс, и просто вставил вызов configtest перед запуском.
Правда это никак не решает проблему с тем, что пользователь делает up на
сервис. Приходится делать обёртку, которая позволяет донести до
пользователя ошибку.

Наконец, есть альтернативный подход - переложить всю подготовительную
работу на супервизор. Пусть он кладёт секреты в environment, открывает
сокеты и запускает configtest. Увы, он не обладает должной
универсальностью — нельзя предсказать все хотелки сервиса при разработке
супервизора. Хотя runit/daemontools и systemd пытаются, предоставляя
tcpsvd (socket activation), envdir (EnvironmentFile=), chpst (куча
всего в sytsemd.exec) и прочее.

Из всего этого можно сделать вывод, что проблема вполне реальна. И
приходится либо решать её каждый раз (или в коде демонизации сервиса,
или в init-скрипте, проверяющим успешность старта), что часто приводит к
ошибкам, либо положиться на умный супервизор. Ни sysvinit, ни runit не
предоставляют удобных механизмов для уведомления пользователя. А Upstart
и systemd требуют кооперации со стороны сервиса, что не всегда приемлимо
для разработчика, особенно в случае необходимости работать на широком
спектре систем.

комментарий 1:

From: Sergey Matveev
Date: 2020-09-13 13:13:26Z


>Концептуально проблема с том, что сервис может или сам заниматься
>демонизацией, или отдать эту задачу супервизору.

Лично я с ходу даже не помню как корректно нужно демонизироваться. Почту
что закрывать за собой дескрипторы, как минимум. Есть ещё double fork.
Когда-то знал для чего он нужен, но, так как на таком уровне не работал
толком, то и забывается оно. В Go вроде как проблематично сделать double
fork/whatever подходы для демонизации (чтобы легко, без хаков) и я был
немного разочарован. А с daemontools наоборот выясняется что лучше чтобы
демонизацией службы самостоятельно не занимались :-)

Насколько понимаю, грубо говоря, в идеале софт или не должен
демонизироваться сам, либо он должен давать отдельную опцию для этого.
Но многие так и делают. Одно удобнее для init-скриптов, другой для
супервизоров уже. Хотя далеко не каждый демон то состоит из одного
процесса.

>Upstart предполагает, что процесс пошлёт себе SIGSTOP.
>[...]
>В systemd есть шина, на которую сервис должен послать сообщение о своей
>готовности.

Ух, жесть. systemd не перестаёт меня удивлять :-). Но мне просто нужно
перестать воспринимать systemd-based системы как что-то UNIX-way-ное :-)

>С runit я нормального решения не придумал. В итоге пошёл на
>TOCTOU-компромисс, и просто вставил вызов configtest перед запуском.

Я вот недавно перевёл nsd и unbound под daemontools и у меня всё очень
тупо: вызывается отдельно проверка, дальше exec на демона:

    # cat /var/service/nsd/run
    #!/bin/sh
    cfg=/usr/local/etc/nsd/nsd.conf
    /usr/local/sbin/nsd-checkconf $cfg
    exec /usr/local/sbin/nsd -d -c $cfg

    # cat /var/service/unbound/run
    #!/bin/sh
    cfg=/usr/local/etc/unbound/unbound.conf
    /usr/local/sbin/unbound-checkconf $cfg
    exec /usr/local/sbin/unbound -c $cfg

>Наконец, есть альтернативный подход - переложить всю подготовительную
>работу на супервизор.[...]

Ага, в daemontools и особенно в runit заметил кучу подобных утилит.
Безусловно, как вы и пишете, предугадать все хотелки невозможно. Но я за
то чтобы была маленькая небольшая компактная система, удовлетворяющая
99% потребностей (определённого класса пользователей, конечно же). А там
где что-то не укладывается... ну или писать обёртки или вспомогательные
программы. Если кто-то насильно демонизируется и это мешает, то сделать
патч для программы, попробовать уговорить её разраба сделать возможно
оставаться в foreground.

>Из всего этого можно сделать вывод, что проблема вполне реальна. И
>приходится либо решать её каждый раз (или в коде демонизации сервиса,
>или в init-скрипте, проверяющим успешность старта), что часто приводит к
>ошибкам, либо положиться на умный супервизор. Ни sysvinit, ни runit не
>предоставляют удобных механизмов для уведомления пользователя. А Upstart
>и systemd требуют кооперации со стороны сервиса, что не всегда приемлимо
>для разработчика, особенно в случае необходимости работать на широком
>спектре систем.

Всё так, всё верно! Но... для меня факт остаётся фактом: одного из самых
популярных демонов я не смог запустить под systemd :-), с родным пакетом
из CentOS. Ну точнее он падал с тем что не может сделать bind и выходил
с ошибкой, а я не получал даже плохой return code. Просто уже сколько
лет CentOS/systemd существуют, но до сих пор так и не нашлись люди
которые бы "подружили" nsd с systemd в этом дистрибутиве.

Спасибо за массу интересной информации!

Сгенерирован: SGBlog 0.34.0