systemd с точки зрения мэйнтейнера upstream-проекта
Это архивная статья
Теперь уже самым закоренелым скептикам должно быть понятно, что
systemd, это нынешний стандарт. Для закрепления успеха systemd
продвигают в LSB с начала 2012
года.
Однако, несмотря на огромнейшую гору документации по systemd (и даже на русском), спрос на нее все еще заметен.
Инженеh SUSE, Neil Brown
написал пару статей (часть 1 и
часть 2), где попытался описать
свой опыт с systemd, как upstream-мэйнтейнера mdadm и nfs-utils.
Для начала Neil отмечает, что init-скрипты слишком сильно отличаются
от дистрибутива к дистрибутиву, чтоб имело смысл их поставлять в
составе файлов в upstream. Да и для сложных случаев, таких, как
nfs-utils, синхронизация с помощью SysV была недостаточной, и Neil
затрудняется вспомнить, есть ли дистрибутив, в котором демоны NFS для
сложных конфигураций были бы запущены в нужном порядке. А вот в случае
с systemd, это стало возможным - и общая настройка для всех дистров, и
точный порядок запуска.
Neil увидел уменьшение объема скриптов systemd по сравнению с SysV
(примерно с 800 строк до менее 200 строк), зато ему пришлось включить
большее количество описаний сервисов - вместо двух SysV-скриптов, для
nfs-utils понадобилось 14 systemd-файлов. К счастью среди этих 14
файлов присутствуют 4 *.target (в терминологии systemd, это некая
"точка" на дереве загрузки системы, достигнутая стадия), что упрощает
жизнь пользователей.
Дальше Neil заинтересовался, как бы не дать повода пользователю
редактировать unit-файлы, одновременно позволив ему изменять ряд
параметров (количество потоков демона и т.п.). Тут Neil увидел
область, нуждающуюся доработки. Например, если пользователь хочет
переназначить порт, то было бы логично просто установить переменную
$MOUNTD_PORT где-нибудь в /etc/sysconfig/ или /etc/defaults/. Однако,
тогда демону может потребоваться ключ командной строки, который не
нужен, если порт не переназначается. Т.е. требуется написать что-то
типа
/usr/sbin/rpc.mountd ${MOUNTD_PORT:+-p $MOUNTD_PORT}
(если установлена переменная, то добавить ключ командной строки, иначе
не добавлять). В systemd возможности задавать такие условия нет, и
пользователю придется пересоздавать unit-файл в /etc/systemd. А это
"перезапишет" изменения в unit-файлы из /usr/lib/systemd. Можно,
конечно, указывать в файлах конфигурации не $MOUNTD_PORT, а
MOUNTD_PORT_ARG="-p 12345", но это как-то не очень красиво
получается. Вообще, более правильно было бы использовать
include-директивы или "drop-ins" (unit-файлы в директории
/etc/systemd/system/*.d).
Neil продолжил вторую часть с
обсуждения вопросы активации сервисов. Он отмечает, что в отличие от
Upstart, установка unit-файла не означает его автоматическую
активацию. Далее он обращает наше внимание на существенную разницу в
активации legacy-сервисов из SysV, с помощью insserv, и в systemd. В
SysV активация сервиса сопровождается проверкой на завивимости
(директива Required-Start), и если сервис не активирован, но insserv
выпадает с подробным сообщением, из которого понятно. что нужно
сделать. Утилита для обработки SysV-скриптов из systemd действует
по-другому - оно учитывает порядок, но не смотрит на наличие. Таким
образом можно смело говорить, что работать SysV-скрипты в systemd
будут лишь в простейших случаях, и потребуется полный переход на него.
Neil также отмечает, что несмотря на противопоставление event-based
системе активации из Upstart и декларативному подходу в systemd,
некоторые сервисы активируются таким образом, что сторонний
непредвзятый наблюдатель не сможет отличить это от событийной модели
активации. Например, mdadm активируется с помощью udev, при появлении
дискового массива - это выглядит именно как событие.
Дальше Neil обращает внимание на недоработку - в механизме socket
activation отсутствует возможность активации не с предопределенного, а
с произвольного номера порта. Это нужно для rpc.statd. Что интересно,
в Solaris такой механизм есть.
Возвращаясь к параллелям и отличиям с Upstart, Neil продолжает
анализировать активацию сервисов и отмечает, что достижение target в
systemd хотя и практически неотличимо от событий Upstart ("starting"),
сервисы systemd запускаются не по событиям, а сами создают списки
событий ("я запускаюсь, поэтому запусти также то-то и то-то").
Зависимость тут развернутая - сервис Upstart точно знает, что за
событие его запустило, в то время, как сервис systemd знает какие
"события" (сервисы) оно запускает. Здесь очень интересно появление
двух директив в systemd WantedBy и RequiredBy, которые являются
антиподами для Wants и Requires соответственно, но в отличие от них
могут использоваться лишь в секции [Install]. Эти директивы можно
считать аналогом "start on" в Upstart, но интересно, что в секции
[Unit] их использовать не получится. Несмотря на то, что каждое
указание Wants и Requires в секции [Unit] действительно создает
внутреннее представление WantedBy и RequiredBy в зависимых сервисах,
явное их использование не допускается.
Neil продолжает с интересной задачей - условный запуск сервиса
(запускать лишь в каком-то случае, иначе запускать другой сервис).
Оказалось, что без вмешательства суперпользователя это в общем случае нерешимо. Однако, в ряде случаев, используя директиву Requisite=, удалось привязать сервис к доступности указанного target. Сервис не запускается, если не запущен target, и останавливается сразу при остановке target. Получив уже четыре разных target в составе пакета nfs-utils, Neil отмечает удобство пресетов (presets) для systemd, которые позволяют мэйнтейнерам дистрибутивов самостоятельно решать то, какие target и сервисы будут включены по умолчанию.
Подытоживая вопросы активации, Neil говорит, что systemd предоставляет
довольно богатый набор директив для управления сервисами, которые
покрывают даже очень сложные случаи. К сожалению, как отмечает Neil,
включение и отключение сервисов не принимает во внимание файлы
конфигураций из /etc/sysconfig или /etc/defaults. Также интересно, что
поведение udev по запуску правил по умолчанию полностью отличается от
systemd, хотя присутствует в том же пакете ПО.
Далее Neil, обладая некоторым опытом разработки языков
программирования, переключается с практических аспектов, на
теоретические. Он недоумевает - директивы systemd разрешены лишь в
конкретных секциях, тогда зачем нужны все эти [Unit], [Service],
[Install]? Ну, кроме того, чтоб unit-файл выглядет бы как ini-файл.
Neil продолжает с логическими директивами systemd, такими как
ConditionPathExists, которые отчасти позволяют логические выражения,
такие, как A and B and (C or D or E), но не такие, как (A or B) and (C
or D). Он интересуется, почему было бы не использовать традиционные
правила, вместо частичного их подмножества. Любое отклонение от
общепринятого стандарта лишь усложняет изучение, хотя и понятно, что
сложные логические выражения в unit-файлах не будут часто встречаться.
Однако, собственная алгебра логики, это не единственное проблематичное место. Neil отмечает огромное количество директив - Requires, RequiresOverridable, Requisite, RequisiteOverridable, Wants, BindsTo, PartOf, Conflicts, Before, After, OnFailure, PropagateReloadsTo, ReloadPropagateFrom и т.п. Он чувствует, что такое их количество, это явный признак некоей модели, скрытой от пользователя, и которую можно было бы более эффективно представлять с помощью DSL.
На этом Neil останавливается, и подводит итог. Он отмечает. что
указанные менее 200 строк он написал с первой попытки, и он впервые
чувствует, что его описание nfs-utils в systemd делает именно то, что
нужно. Как разработчик, он отмечает, что наконец-то он обладает
инструментарием и языков описания, которое позволяет ему выражать
именно то, что он хочет, и сравнивая с тем, что дает разработчикам
systemd, можно не обращать внимание на в основном косметические
минусы.