До середины 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, умеет параллельно запускать и останавливать службы.

Использованные материалы


  1. Celebrating Daemontools
  2. Daemontools Intro
  3. Введение в Daemontools
  4. A history of modern init systems (1992-2015)
Поделиться с друзьями
-->

Комментарии (20)


  1. funca
    05.05.2017 00:36
    -1

    Кажется, этот его подход — на каждый чих своя утилита — в итоге и привел к тому, что сообщество выбрало другие решения.


    1. worldxaker
      05.05.2017 04:30
      +1

      так-то это идеология UNIX на те-же архиваторы посмотри, tar обьединяет фалы в архив, bz2 сжимает архив.


      1. temujin
        05.05.2017 08:16

        Но для распаковки *.tar.bz2 достаточно команды tar -axvf file.


        1. redfs
          05.05.2017 10:19

          Запаковать тоже можно одной командой. tar -cjfv file.tar.bz2 file… Но при этом все равно работает связка tar + bzip2. Думаю, что worldxaker это имел в виду.


      1. mistergrim
        05.05.2017 09:22
        +3

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


        1. redfs
          05.05.2017 10:11
          +1

          По большому счету сжатия у tar и сейчас нет. Все сжатие ( -z, -Z, -j… -I ) осуществляется подключаемыми внешними фильтрами. Сам по себе tar компрессией не занимается.


  1. dmitry_ch
    05.05.2017 10:59
    +3

    но тут случился systemd


    Точно — «случился».


  1. zersh
    05.05.2017 12:37

    Было интересно. спасибо
    в логировании видимо опечатка: cd log && mkdir mail
    main?


    1. temujin
      05.05.2017 19:47

      Действительно, и спеллчек не заметил. Спасибо.


  1. miga
    05.05.2017 20:19
    +2

    Можно сколько угодно твердить мантру про философию UNIX, но правда в том, что _простые_ запускалки стартовых скриптов на самом деле не решают проблему инициализации и управления сервисами.

    Нужно знать взаимные зависимости сервисов (чтобы при загрузке запускать их параллельно), неплохо бы воспользоваться всеми ништяками cgroups и т.д. Например, простейшая, казалось бы, задача — по PID процесса узнать, какой сервис за него отвечает — не решается нигде, кроме как в systemd. Так что как бы не говнили systemd за переусложненность, архитектурные проблемы и чего там еще, его появление — это _очень_ большое дело (и, безусловно, хорошее) для развития всей платформы.


    1. NaHCO3
      06.05.2017 03:46

      > Например, простейшая, казалось бы, задача — по PID процесса узнать, какой сервис за него отвечает — не решается нигде, кроме как в systemd.

      Либо двумя строчками башскрипта. Взять полученный pid и записать его в файл.


      1. miga
        06.05.2017 10:02
        +1

        К сожалению, все не так просто и для SysV однозначного решения нет. Не все сервисы утруждают себя созданием PID-файлов, кроме того, очевидно, что у сервиса может быть больше чем один процесс. Да и вообще, PID-файлы решают обратную задачу — по имени сервиса найти процесс, а не наоборот. В общем, итоговое решение у меня заняло строчек двести, с использованием различных угадаек. В systemd же это решается тривиально, так как каждый сервис запускается в своей cgroup.


    1. temujin
      06.05.2017 12:50

      Философия или принципы Unix — это не мантра, а стратегия разработки ПО, которая по сей день актуальна. В этом отличие от мантры, которую повторяют, но ничего не делают или делают наоборот.


      Как мне кажется многие обиды и непонимание возникли оттого, что стратегию стали разменивать ради тактических соображений. Именно это и означает отказ от принципов Unix, ради того, чтобы инит умел находить сервис по PID и стартовал ОС на 17% быстрее.


      Параллельный запуск процессов уже многие иниты умеют (nosh, openRC, runit), также как и отслеживать зависимости. OpenRC имеет поддержку cgroups.


      1. grossws
        06.05.2017 15:09

        Параллельный запуск процессов уже многие иниты умеют (nosh, openRC, runit), также как и отслеживать зависимости.

        Параллельный запуск сам по себе не является ценностью пока нет нормального отслеживания зависимостей. Пробовал когда-то сделать зависимости на runit'е. Не лучшие воспоминания, скажу я вам.


        OpenRC имеет поддержку cgroups.

        Которая там была чисто для галочки "поддерживает cgroups". Возсожно сейчас что-то изменилось, но тогда это была профанация.


        1. temujin
          08.05.2017 14:55

          Никак не скажешь, что поддержка cgroups экспериментальная или профанация. Если есть конкретные примеры, давайте, а то так можно многие не доведенные до ума фичи назвать профанацией.


          1. grossws
            08.05.2017 15:31

            Посмотрите, что было к моменту голосования: https://wiki.gentoo.org/index.php?title=OpenRC/CGroups&oldid=17977. Была ровно одна фича, которая долгое время являлась киллер-фичей systemd.


            1. temujin
              08.05.2017 19:24

              Revision as of 06:36, 22 June 2013 by

              К моменту голосования чего именно: инит системы Debian?


              1. grossws
                08.05.2017 19:28

                Да, извиняюсь, забыл указать. Изменения в этой части openrc очень удачно «совпали» с подготовкой к голосованию, емнип


      1. NaHCO3
        06.05.2017 23:21

        > openRC

        Официально не поддерживает параллельный запуск. Эта фича является сильно экспериментальной. Некоторые сервисы не способы стартовать или остановиться корректно, и это отражено в багтрекере. Проблема так и не решена, вместо этого стоит пометка «а мы и не обещали вам, что всё будет работать».


  1. NaHCO3
    05.05.2017 23:54

    Ещё monit есть на поприще служб-бебиситеров.