До середины 2000-х никому в голову не приходило менять sysvinit, почти никому. Gentoo с самого начала создавала и развивала OpenRC. Все изменилось с появлением launchd в Mac OS X. Разработчики Ubuntu бросились создавать Upstart, в котором были позаимствованы некоторые идеи из launchd
. Дело шло ни шатко ни валко, но тут случился systemd и смешал все карты. Но кто же был истинным первопроходцем?
Daniel J. Bernstein математик и специалист по криптографии, автор популярного MTA qmail и множества других менее известных программ, среди которых выделяется daemontools. Для множества современных систем инициализации daemontools являлся примером и вдохновителем. Прошу внутрь для того, чтобы познакомиться с самой элегантной, простой и влиятельной системой управления службами в Unix / Linux.
DJB и Daemontools
Уравнения Максвелла для управления процессами на Unix ОС.
Внимание — не следует путать 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
и опции представлены ниже.
svc options services
- -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
- softlimit — запускает программу с ресурсными ограничениями.
Логирование
Если у вас есть некая программа 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, умеет параллельно запускать и останавливать службы.
Использованные материалы
Комментарии (20)
miga
05.05.2017 20:19+2Можно сколько угодно твердить мантру про философию UNIX, но правда в том, что _простые_ запускалки стартовых скриптов на самом деле не решают проблему инициализации и управления сервисами.
Нужно знать взаимные зависимости сервисов (чтобы при загрузке запускать их параллельно), неплохо бы воспользоваться всеми ништяками cgroups и т.д. Например, простейшая, казалось бы, задача — по PID процесса узнать, какой сервис за него отвечает — не решается нигде, кроме как в systemd. Так что как бы не говнили systemd за переусложненность, архитектурные проблемы и чего там еще, его появление — это _очень_ большое дело (и, безусловно, хорошее) для развития всей платформы.NaHCO3
06.05.2017 03:46> Например, простейшая, казалось бы, задача — по PID процесса узнать, какой сервис за него отвечает — не решается нигде, кроме как в systemd.
Либо двумя строчками башскрипта. Взять полученный pid и записать его в файл.miga
06.05.2017 10:02+1К сожалению, все не так просто и для SysV однозначного решения нет. Не все сервисы утруждают себя созданием PID-файлов, кроме того, очевидно, что у сервиса может быть больше чем один процесс. Да и вообще, PID-файлы решают обратную задачу — по имени сервиса найти процесс, а не наоборот. В общем, итоговое решение у меня заняло строчек двести, с использованием различных угадаек. В systemd же это решается тривиально, так как каждый сервис запускается в своей cgroup.
temujin
06.05.2017 12:50Философия или принципы Unix — это не мантра, а стратегия разработки ПО, которая по сей день актуальна. В этом отличие от мантры, которую повторяют, но ничего не делают или делают наоборот.
Как мне кажется многие обиды и непонимание возникли оттого, что стратегию стали разменивать ради тактических соображений. Именно это и означает отказ от принципов Unix, ради того, чтобы инит умел находить сервис по PID и стартовал ОС на 17% быстрее.
Параллельный запуск процессов уже многие иниты умеют (nosh, openRC, runit), также как и отслеживать зависимости. OpenRC имеет поддержку cgroups.
grossws
06.05.2017 15:09Параллельный запуск процессов уже многие иниты умеют (nosh, openRC, runit), также как и отслеживать зависимости.
Параллельный запуск сам по себе не является ценностью пока нет нормального отслеживания зависимостей. Пробовал когда-то сделать зависимости на runit'е. Не лучшие воспоминания, скажу я вам.
OpenRC имеет поддержку cgroups.
Которая там была чисто для галочки "поддерживает cgroups". Возсожно сейчас что-то изменилось, но тогда это была профанация.
temujin
08.05.2017 14:55Никак не скажешь, что поддержка
cgroups
экспериментальная или профанация. Если есть конкретные примеры, давайте, а то так можно многие не доведенные до ума фичи назвать профанацией.grossws
08.05.2017 15:31Посмотрите, что было к моменту голосования: https://wiki.gentoo.org/index.php?title=OpenRC/CGroups&oldid=17977. Была ровно одна фича, которая долгое время являлась киллер-фичей systemd.
NaHCO3
06.05.2017 23:21> openRC
Официально не поддерживает параллельный запуск. Эта фича является сильно экспериментальной. Некоторые сервисы не способы стартовать или остановиться корректно, и это отражено в багтрекере. Проблема так и не решена, вместо этого стоит пометка «а мы и не обещали вам, что всё будет работать».
funca
Кажется, этот его подход — на каждый чих своя утилита — в итоге и привел к тому, что сообщество выбрало другие решения.
worldxaker
так-то это идеология UNIX на те-же архиваторы посмотри, tar обьединяет фалы в архив, bz2 сжимает архив.
temujin
Но для распаковки *.tar.bz2 достаточно команды
tar -axvf file
.redfs
Запаковать тоже можно одной командой. tar -cjfv file.tar.bz2 file… Но при этом все равно работает связка tar + bzip2. Думаю, что worldxaker это имел в виду.
mistergrim
У tar изначально вообще другое предназначение было, никакого сжатия там не подразумевалось.
redfs
По большому счету сжатия у tar и сейчас нет. Все сжатие ( -z, -Z, -j… -I ) осуществляется подключаемыми внешними фильтрами. Сам по себе tar компрессией не занимается.