Системы инициализации Unix и Linux после SysV
До середины 2000-х никому в голову не приходило менять sysvinit, почти никому. Gentoo с самого начала создавала и развивала OpenRC. Все изменилось с появлением launchd в Mac OS X. Разработчики Ubuntu бросились создавать Upstart, в котором были позаимствованы некоторые идеи из launchd . Дело шло ни шатко ни валко, но тут случился systemd и смешал все карты. Но кто же был истинным первопроходцем?
Daniel J. Bernstein математик и специалист по криптографии, автор популярного MTA qmail и множества других менее известных программ, среди которых выделяется daemontools. Для множества современных систем инициализации daemontools являлся примером и вдохновителем. Прошу внутрь для того, чтобы познакомиться с самой элегантной, простой и влиятельной системой управления службами в Unix / Linux.
DJB и Daemontools
Внимание — не следует путать daemontools написанный DJB с программой-тезкой DAEMON Tools для монтирования iso образов и создания виртуальных CD/DVD дисков.
Daniel J. Bernstein создал свою программу в 1997 г. Последний стабильный релиз был в июле 2001 г.
Сейчас, когда в Linux сообществе наблюдается раскол из-за systemd, самое время вспомнить каким может быть настоящий инит в духе принципов и философии Unix. В этом смысле дзен-программист DJB является воплощением минимализма и простоты, а бритва Оккама у него встроена в клавиатуру. Его решения основательны и элегантны, на этом фундаменте он строит надежное, безопасное ПО, которое потребляет минимальное количество ресурсов ОС. Вот некоторые особенности его стиля работы.
- Самый большой файс исходного кода multilog.c имеет всего лишь 13898, нет не строк, а байтов. Команда wc указывает лишь на 617 строк кода.
- Большинство функций имеют меньше 30 строк.
- Принцип — никогда ничего не парсить.
- Принцип — использовать все, что дает ОС и не изобретать велосипеды.
Почему такие странные принципы, и еще с учетом того, что автор исповедует их фанатично? Строительный материал daemontools — директории, процессы, FIFO, исполняемые файлы. Это дает массу преимуществ в разработке и отладке приложения:
- Тестировать запуск службы проще простого — если запустится исполняемый файл ./run , то и служба тоже запустится.
- Можно использовать любой язык программирования, не только Bash. Сгодится даже скомпилированный бинарник.
- Понятно, что и как делает программа даже без подробного прочтения документации и изучения исходного кода.
- Парсить разнообразные текстовые структуры — на удивление трудная задача, если это делать по уму. Автор избегает этого, умело используя иерархическое свойство файловой системы Unix для воссоздания структуры переменных среды ключ=значение .
Сравнительная таблица DT
Стоит обратить внимание на неортодоксальную структуру директорий daemontools, ничтоже сумняшеся программа создает каталоги в корне файловой системы Unix. DJB прописал в коде программы директории /service , /command и рекомендует создать /package для исходников программы. Это считается весьма дурным тоном в Unix и Linux, создатели дистрибутивов всеми силами избегают этого, также как и пользователи с правами root.
features | inittab | ttys | init.d | rc.local | /service |
---|---|---|---|---|---|
Easy service installation and removal | No | No | Yes | No | Yes |
Easy first-time service startup | No | No | No | No | Yes |
Reliable restarts | Yes | Yes | No | No | Yes |
Easy, reliable signalling | No | No | No | No | Yes |
Clean process state | Yes | Yes | No | No | Yes |
Portability | No | No | No | No | Yes |
Давайте пробежимся по таблице самовосхваления daemontools. Начнем с первой строчки. Действительно создание и удаление нового сервиса проще простого, добавил, или удалил новую директорию в /service вместе с файлом ./run и на этом все. Сравните со скриптами sysvinit и остальных инитов, чтобы оценить простоту такого способа достичь того же самого.
Второй пункт менее убедителен в том смысле, что конечно же проще когда сервис и в первый раз стартует автоматически, но в остальных системах инициализации и управления службами достаточно одной команды для первого старта службы.
Возможность перезапустить завершившийся сервис в автоматическом режиме была на тот момент момент большим шагом вперед. На сегодняшний день это уже в порядке вещей.
Четвертая позиция — сигналы. Команда svc , как показано ниже, позволяет послать службе практически любой сигнал POSIX стандартов.
Последние две позиции кажутся несколько надуманными, не совсем понятно как daemontools восстанавливает состояние процесса в отличие от остальных инитов. Непонятно также, почему автор только свою программу считает переносимой на другие платформы.
Структура DT
По словам автора: daemontools — это набор инструментов для управления службами UNIX. Основными отличиями от традиционных инитов (структуры директорий rcX.d, rc.d, rc.local и пр.) является способность перезапустить сервис в случае его падения и наличие программы ведения и ротации логов — multilog. Также, multilog позволяет вести лог вывода программ, не умеющих перенаправлять вывод в syslog . Таким образом, можно запускать как сервис программы, для этого вовсе не предназначенные.
Внутреннее устройство daemontools, граница обведена красной прерывистой линией.
Теперь немного о принципах работы программы.
В самом начале системный инит запускает svscanboot , который затем запускает программу svscan в ново-созданной директории svscan . Далее svscanboot перенаправляет вывод запущенного svscan в отладочный процесс readproctitle .
Ядром daemontools являются всего две программы: svscan и supervise . Первая запускается с единственным аргументом по выбору, а вторая — с обязательным ключом.
Svscan служит для запуска и слежения за сервисами. Каждые 5 секунд Svscan проверяет каталог /service , если другой не задан, на наличие новых подкаталогов. Если такие будут обнаружены, запускается новая копия supervise для каждого каталога.
Supervise является, в соответствии с названием, контролирующем процессом. Он вызывается с параметром, в котором содержится имя каталога и в нем ищет скрипт ./run , который и запускает. Если по каким-то причинам ./run перестал исполняться, то тогда supervise его перезапустит после небольшой паузы — чтобы не создавать дополнительной нагрузки на ОС. Supervise не перезапустит ./run , если в каталоге будет обнаружен файл ./down . Supervise создает в каталоге сервиса подкаталог ./supervise , в котором хранятся данные о процессе. Эти данные могут быть прочитаны с помощью утилиты svstat . Для управления сервисом служит программа svc .
Синтаксис команды svc и опции представлены ниже.
- -u: Up, запустить сервис, в случае останова — перезапустить.
- -d: Down, остановить сервис.
- -t: Terminate, посылает сервису сигнал TERM.
- -k: Kill, посылает сервису сигнал KILL.
- -p: Pause, посылает сервису сигнал STOP.
- -c: Continue, посылает сервису сигнал CONT.
- -h: Hangup, посылает сервису сигнал HUP.
- -a: Alarm, посылает сервису сигнал ALRM.
- -h: Interrupt, посылает сервису сигнал INT.
- -x: Exit, supervise завершит работу как только ./run или его потомок завершится.
- -o: Once, запустить сервис, но не перезапускать после его завершения.
Есть еще одно обстоятельство, если в рабочем каталоге supervise содержится подкаталог ./log , в котором есть ./log/run , то тогда будет запущена еще одна копия supervise и создан канал между ./run и ./log/run .
Попробуем добавить сервис sshd.
#!/bin/sh # перенаправить stderr в stdout exec 2>&1 # с опцией -D sshd выполняется явно, с опцией -e отладка пишется в stderr exec /usr/sbin/sshd -D -e
В таком случае структура директорий может выглядеть так.
- service/ |- ngetty/ | |- run | |- log/ | |- run |- sshd/ | |- run | |- log/ | |- run |- squid/ | |- run | |- log/ | |- run
После того, как svscan пробежится по этому списку мы получим дерево процессов, в котором процессы service следят за сервисами и логированием.
-svscan-+-service-+-ngetty | `-log-service +-service-+-sshd | `-log-service +-service-+-crond | `-log-service
Зависимости между службами
Несмотря на то, что программа не поддерживает зависимости между различными службами, есть способ добиться учета зависимостей, используя для этого svok следующим образом.
#!/bin/sh svok postgres || (echo "waiting for postgres. " && exit 1) exec 2>&1 exec python3 your-web-app.py
- svok — проверяет, запущена ли определенная служба. Ничего не выводит в stdout и stderr , если служба запущена, то тогда программа возвращает код 0, в противном случае — код 100.
В данном примере программа на python запустится только в том случае, если стартовал postgres, если же последний пока не поднялся, скрипт завершится и затем через определенно время svscan его перезапустит. Когда же postgres наконец поднимется, python запустит веб приложение.
Квотирование сервиса
С помощью утилиты softlimit можно ограничить предоставленные данному сервису ресурсы.
#!/bin/sh exec 2>&1 # сменить пользователя на foo, ограничить стек 4096 байтами, открытые # файловые дескрипторы 15 и количество процессов 1: exec setuidgid foo softlimit -n 4096 -o 15 -p 1 \ bar -n
Логирование
Если у вас есть некая программа foo , которая не ведет логов, вы без труда сделаете это с помощью multilog , собрав в отдельном файле вывод stdout и stderr с временными метками.
[root@home: +5] cd /service/foo && mkdir log [root@home: +5] cd log && mkdir main [root@home: +5] nano run.new #пишем в файл #!/bin/sh exec 2>&1 exec multilog t ./main [root@home: +5] chmod u+x run.new [root@home: +5] mv run.new run [root@home: +5] cd /service [root@home: +5] svc -t foo
Из другого терминала запускаем:
tail -fn0 /service/foo/main/current
Последователи daemontools
Мало кто сегодня использует DT, но можно смело сказать, что Daniel J. Bernstein стал для многих примером, а дело его живет и здравствует. Вот неполный список его последователей.
- daemontools-encore — Разработчик Bruce Guenter, является дальнейшим развитием DT. Не полноценный инит, также как и оригинал.
- runit — Разработчик Gerrit Pape, умеет параллельно запускать службы. Хабрапост.
- s6 — Полноценный асинхронный инит с PID 1.
- nosh — Легковесный гипервизор процессов для BSD и Linux, умеет параллельно запускать и останавливать службы.