CTO, Release manager, Teamlead
0,0
рейтинг
10 февраля 2010 в 19:23

Администрирование → Использование runit для своих сервисов

Супервизор сервисов runit позиционируется как замена стандартным скриптам инициализации Unix.

Но на практике оказалось, что runit идеален для управления сервисами безотносительно инициализации и т.п.

Введение


Супервизор берёт на себя такой функционал, как:
  • превращение любого процесса в демон;
  • логгирование вывода процесса и ротирование логов;
  • запуск, остановка, рестарт, запрос состояния, управляющие скрипты для init.d;
  • выключение и запуск сервисов автоматически при появлении новых сервисов в списке либо удалении старых из списка;
  • возможность ведения нескольких независимых списков сервисов одновременно (например, для каждого пользователя отдельно и для системы в целом);
  • удобный API для управления сервисами.

Для большинства операционных систем runit уже входит в репозитории пакетов (apt-get install runit). Кроме того, мы имеем уже готовый набор рецептов для популярных сервисов (nginx, apache etc.).

Далее рассмотрим стандартную схему runit (которая используется по умолчанию):

Демонизация


Каждый сервис описывается отдельным каталогом /etc/sv/<название сервиса>.

обычно достаточно иметь в этом каталоге исполняемый скрипт run вида
#!/bin/bash
exec 2>&1

exec your_running_command


Важно, чтобы your_running_command не демонизировала себя (не отсоединялась от стандартных потоков stdin, stdout, stderr).

Переадресация ошибок в стандартный вывод нужна для их логгирования. Выполняется логгирование, если в каталоге /etc/sv/<название сервиса>/log/ разместить файл run вида
#!/bin/bash
LOG_FOLDER=/var/log/<название сервиса>
mkdir -p $LOG_FOLDER
exec svlogd -tt $LOG_FOLDER


Сервисы, расположенные в каталоге /etc/sv/, не выполняются, пока ссылки на них не будут размещены в каталоге /etc/service/.

Как только вы сделаете ln -s /etc/sv/<название сервиса> /etc/service/<название сервиса>, сервис runsvdir увидит новый сервис, и запустит его. Более того, в случае остановки сервиса он будет автоматически перезапущен. Это даёт гораздо более быструю реакцию на остановку сервиса по сравнению с использованием сервисов мониторинга (god или monit).

Ротирование логов


При использовании svlogd логи размещаются в папке, которую Вы указали при её запуске. При этом текущий лог находится в файле current, и периодически выполняется ротирование логов в этой папке.

Управление


Запускать, останавливать, перезапускать сервисы можно с помощью команды sv (start|stop|restart...) <название сервиса>.

Кроме того, при запуске сервиса появится каталог /etc/service/<название сервиса>/supervise, в котором будут расположены очень полезные файлы и потоки:
  • pid — идентификатор процесса Unix;
  • stat — человеко-читаемое состояние сервиса
  • status — машинно-читаемое состояние процесса
  • control — поток управления
  • и так далее...

Можно отметить, что для остановки или запуска процесса достаточно открыть поток control на запись, и отправить туда символ d (от down) или u (от up) соответственно.

В стиле init.d

Нет ничего проще, чем поддержать управление в стиле init.d.

Просто делаем ln -s /usr/bin/sv /etc/init.d/<название сервиса>. sv поймёт, что его вызвали в стиле init.d, и готов будет принимать команды вида /etc/init.d/<название сервиса> start etc. Совсем немного магии, правда?

Настройка сервисов


В комплекте с runit поставляется утилита chpst, которая позволяет выполнять сервисы с дополнительной настройкой (ограничивать размер памяти, запускать из под определенного пользователя, с другим уровнем nice и т.д.).

Резюме


runit оказался настолько удобен и надёжен для организации своих сервисов, что мы стараемcя переводить все наши демоны под runit, заодно отказываясь от различного геморроя в виде пакетов демонизации а ля daemons gem. Также на части машин мы избавились от Monit (где требовался лишь мониторинг процессов).

Крайне рекомендую к прочтению комментарий от powerman, а также статью про Web-интерфейс к runit.
Акжан @akzhan
карма
24,0
рейтинг 0,0
CTO, Release manager, Teamlead
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Администрирование

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

  • 0
    Спасибо! Похоже полезная вещица, нужно попробовать для своих самоделок.
    • +1
      Забыл ещё дописать про управление в стиле init.d. Дописал, очень удобная магия :)
      • 0
        спасибо большое… завтра-же буду экспериментировать на моей железяки…
  • 0
    /о пользе пробелов/

    Прочит название как r-unit, думал: юнит-тестирование добралось до демонов…
    • 0
      Поскольку с runit демонами становятся обычные процессы, то их отладка становится таким же простым делом, как и для остальных недемонических процессов.

      и никто не мешает писать юнит-тесты вне зависимости от природы программного продукта :)
  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    С upstart мирно соседствует? У нас в перемешку debian и ubuntu server, поэтому сейчас опакечиваю в стандартные init скрипты. А так можно было бы про runit задуматься.
  • 0
    Не ради холиварчика, а ради объективности :) Проверил в репозитариях ContOS, нет runit. Что подразумевается под большинством?
    • 0
      Например, Debian Lenny, MacOS X, FreeBSD, OpenBSD, Fedora.

      CentOS сейчас не использую, так что даже не знал, что его там нет.
      • 0
        Сейчас проверил, в народно любимой ubuntu есть.
  • 0
    очень похоже на daemontools

    cr.yp.to/daemontools.html
  • 0
    Акжан, а как насчет например reload'a типа nginx/apache — сначала тестим конфиг, потом, если всё ок — релоад? можно/нужно отдельные скрипты типа reload/restart/whatever класть?
    • +1
      У nginx есть уникальная фича обновления/рестарта сервиса без обрыва текущих соединений — к сожалению, с runit она не совместима.

      Что касается простых вещей типа проверки синтаксиса конфига перед рестартом сервиса — это должно делаться без проблем т.к. runit позволяет установить юзерские скрипты-хуки на отправку сервису конкретных команд/сигналов. Так что вы можете написать скрипт в пару строк, который перехватит отправку сервису команды t(erm), проверит синтаксис конфига и либо пропустит команду t(erm), либо нет. Впрочем, лично я думаю, что это лишнее: если админ допускает синтаксические ошибки при редактировании конфига веб-сервера, то лучше всего этого админа не допускать до продакшн серверов, чем писать такие хуки.
      • 0
        powerman ответил полноценно.

        runit знает о сигналах hup, usr1 etc. и позволяет управлять с их помощью.

        но, касательно nginx, runit не позволяет работать с помощью сигнала WINCH.
  • 0
    Спасибо, пойду соберу rpm-ку для CentOS :)
    • 0
      надеюсь, попадёт в официальную репу :)
  • +20
    Было бы неплохо упомянуть, что пакет runit это развитие (форк) DJBшного пакета daemontools. Софт DJB отличается надёжностью и простотой, но DJB очень не любит добавлять ненужные лично ему фичи — дабы не усложнять и не плодить баги. Пакет runit разрабатывается с учётом принципов DJB, автор тоже крайне неохотно добавляет новые фичи и стремится сохранить простоту кода, но всё-таки фич там больше и пользоваться runit удобнее, чем daemontools.

    Стабильность runit, в принципе, не хуже, чем у daemontools — фактически я знаю только про один баг, который проявляется буквально у нескольких пользователей (включая меня, к сожалению), и который много лет не могут поймать (автору runit этот баг повторить не удаётся). Впрочем, для этого бага есть workaround, так что в результате всё работает много лет без нареканий.

    Было бы неплохо упомянуть, что runit состоит из нескольких независимых частей: процесс N1 (т.е. замена стандартному /sbin/init), супервизор сервисов (который описан в статей) и утилита для управления логами. И можно пользоваться как всеми тремя составляющими, так и отдельно любой из них.

    Мы много лет используем runit и для загрузки систем (вместо стандартного /sbin/init) и для запуска всех сервисов (вместо скриптов в /etc/init.d/). Для Gentoo пакет с runit, с примерами скриптов для загрузки системы и пакеты со скриптами для типовых сервисов доступны в моём оверлее для portage.

    Так же мы используем супервизор сервисов из runit для запуска и управления всеми сервисами необходимыми для наших веб-проектов, при этом и супервизор и сервисы запускаются под обычным юзерским аккаунтом.

    В статье не упомянуто одно из основных достоинств runit — он не использует .pid-файлы для контроля сервисов, т.к. это не атомарно и не надёжно. Вместо этого каждый сервис контролируется отдельным процессом-родителем, который в случае креша сервиса гарантированно и моментально получит SIGCHLD и перезапустит сервис.

    Что касается каталогов /etc/sv/ и /etc/service/ — насколько я помню официальную позицию автора runit, рекомендуется использовать немного другие каталоги. Но вообще пути к каталогам нигде не прошиты, указываются параметром и можно выбирать их по своему вкусу — например, у меня это /service/ и /var/service/.

    Что касается «демонизирования», то в статье это описано некорректно. Во-первых runit ничего не демонизирует (процесс демонизации несколько шире чем просто запуск процесса в фоне). Во-вторых runit не требует того, чтобы сервисы не демонизировали себя, в частности не отцеплялись от STDIN/OUT/ERR — он требует только того, чтобы сервисы не форкались в фоновый процесс. Логи сервисы не обязаны выводить на STDOUT/ERR, хотя это предпочтительный вариант. Впрочем, даже если сервисы выводят логи в файлы самостоятельно (как apache и mysql, к примеру), то это не мешает на месте файлов access_log, etc. разместить FIFO-шки, из которых будут читать svlogd-сервисы и выводить логи в традиционном для runit формате (у нас всё работает именно так).
    • 0
      Спасибо за столь хороший комментарий.
  • –1
    Хорошая статья. Спасибо.
  • 0
    Что там с демонизацией? Что то типа tcpserv есть? Если да то как работает — форк на каждый запрос?
    • 0
      Демонизации (в чистом виде) нет.

      Подробнее в комменте от poweman выше.
    • 0
      Если я правильно догадался, что такое tcpserv, то такая утилита есть в DJBшном пакете ucspi-tcp, называется tcpserver.
      • 0
        ну да
        кстати, чем runit превосходит набор DJB?
  • 0
    runit c вебинтерфесом это supervisord.org/ для серверных ферм гораздо удобнее за счет управления через xml-rpc
    • 0
      Никто не мешает использовать REST-сигналы с runit-man.

      решения наподобие supervisord обладают своими преимуществами и недостатками.

      очень долго их расписывать. В общем случае нужна как система мониторинга локально на машине (runit), так и удалённая (например, cacti/nagios).
      • 0
        где написано что что то мешает?

        зачем использовать runit + нашлепку сбоку на ruby если можно поставить supervisord который все умеет сам без постороенней помощи. По возможностям они абсолютно идентичны. runit хорошь там где мало ресурсов в embedded или на vps, но он не предназначен для распределенного управления.

        runit это не мониторинг, это супервайзер для процессов.

        • 0
          runit — железобетонный супервайзер, вы правы.

          для полноты картины, использует ли supervisorctl SIG_CHILD?
          • 0
            вы хотите чтобы вам документацию пересказали?
          • 0
            Использует.
  • 0
    Коллеги!
    А что я делаю не так:

    $ cat /etc/sv/spawn-fcgi/run
    
    #!/bin/sh
    exec 2>&1
    PHP_FCGI_CHILDREN=5 \
    PHP_FCGI_MAX_REQUESTS=1000 \
    exec /usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -u www-data -g www-data -n -- /usr/bin/php5-cgi
    


    Запускаем:

    # sv up spawn-fcgi
    


    Проверяем:
    # sv status spawn-fcgi
    run: spawn-fcgi: (pid 435) 1s
    # sv status spawn-fcgi
    run: spawn-fcgi: (pid 446) 1s
    # sv status spawn-fcgi
    run: spawn-fcgi: (pid 450) 1s
    # sv status spawn-fcgi
    run: spawn-fcgi: (pid 454) 1s
    


    Где я ошибся?
    • 0
      попробуйте оставновить сервис, перейти в каталог сервиса и выполнить ./run
      Всё увидите.

      Навскидку всё в порядке, опция -n прописана.
      • 0
        Спасибо.
        Я сам дурак, забыл chmod +x на run сделать.

        P.S. спасибо за статью.
        • 0
          на досуге попробуйте поставить ruby и rubygems, сделать
          gem install thin runit-man
          runit-man -p 14000 -r

          и потом посмотреть на страницу localhost:14000
          • 0
            Еще вчера.
            Спасибо ;)
  • 0
    А что делать с сервисами, не умеющими не отвязываться от консоли?
    zabbix-server например.
    • 0
      Вообще-то для запуска сервисов runit отвязка от консоли не нужна и даже вредна.

      Я наоборот, убираю всякие опции демонизации. Мне удобнее, чтобы ошибки сыпались в stdout/stderr, их оттуда подхватит svlogd.
      • 0
        Так о том и речь — он НЕ умеет НЕ отвязываться.
        Он сразу после запуска форкается и уходит в бэкграунд.
        Runit его тут же теряет есессна.
        • 0
          подстава :) в таком случае сам процесс в runit не подсунуть, к сожалению.

          либо оставить как есть, либо писать оберточный процесс (daemon controller), либо писать feature request авторам zabbix.

          всё-таки у них должна быть фича запускать сервер в фореграунде, хотя бы для отладки.
  • 0
    orion www # /etc/init.d/unicorn start
    fail: unicorn: runsv not running

    Если же перед этим сделать
    runsv /var/service/unicorn
    То всё ок, только это же не выход
    • +1
      у вас, видимо, не запущен супервизор runsvdir.
      • 0
        Возможно я что-то неверно делаю, но запуск runsvdir запускает мне всех моих демонов
        • +1
          так и должно быть.

          /etc/sv — все сервисы
          /etc/service — симлинки на активные.
          • 0
            Видимо я что-то не улавливаю.
            Ещё раз: что мне надо сделать, чтобы при загрузке демоны не поднимались, но /etc/init.d/демон start работал без проблем?
            • 0
              Боюсь, это выбивается из обычного сценария.

              В вашем случае init.d-скрипт должен активировать сервис в случае его неактивности и т.д… То есть писать руками.
              • 0
                Печаль, понравилась идея использовать runit, как дополнение к гентушному openrc, но видимо не судьба(
        • +1
          Всегда можно для активного сервиса сделать
          sv stop service-name.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.