Runit: Overview


...THIS IS WORK IN PROGRESS...

Runit состоит из трёх независимых частей:

  1. загрузчик - замена sysvinit
  2. сервисы - замена скриптам /etc/init.d/*
  3. логи - запись на диск; фильтрация; ротация (замена logrotate)
Я использую всё это по максимуму: загрузчик использует мои собственные скрипты вместо предоставляемых дистрибутивом в /etc/init.d/; все сервисы работают через runit, ни один скрипт из /etc/init.d/ не используется; runit'овские логи используются в том числе и для тех сервисов, которые их явно не поддерживают, напр. apache2 и mysql.

К сожалению, конфигурация получается довольно сильно связанная между собой, например скрипт загрузчика /etc/runit/2 для работы требует сервиса log-all, а тому нужен специальный каталог для логов с FIFO-файлом /var/log/all/.log ... возможно именно по этой причине автор runit и не разделил его на три независимых пакета.

Directory layout

Runit позволяет выбирать используемые при работе каталоги, и единого стандарта что где лучше располагать пока нет, поэтому опишу свои каталоги (если у вас другое расположение, возможно вам нужно будет немного подправить мои скрипты):

/service/
Каталог с сервисами.
/var/service
Симлинк на /etc/runit/runsvdir/current. Используется как каталог содержащий активные на текущем runlevel сервисы
/etc/runit/runsvdir/current
Симлинк на текущий (выбранный при загрузке?) runlevel (например: "single", "default" или "YOURNAME"). Этот симлинк изменяется автоматически при переключении runlevel - либо при загрузке, либо через запуск runsvchdir.
/etc/runit/runsvdir/default
Симлинк на дефалтовый runlevel который будет использован если при загрузке ядру не был указан runlevel (например: "single" или "YOURNAME"). Этот симлинк вы настраиваете сами.
/etc/runit/runsvdir/single/
Каталог содержащий симлинки на подкаталоги /service/* с сервисами которые нужно запускать на runlevel=single.
/etc/runit/runsvdir/YOURNAME/
Аналогично /etc/runit/runsvdir/single только список сервисов другой (вы можете создать любое кол-во таких каталогов с любыми именами).
/var/log/all/
Каталог где будут храниться логи с сообщениями самого runit (например о невозможности запустить какой-то сервис), а так же отфильтрованные из других логов важные записи.

Boot & Shutdown: /etc/runit/{1,3}

Инициализация системы - не такая уж и сложная задача, с ней вполне справляется sh-скрипт строчек на 30-40. Но в современных дистрибутивах "для универсальности и гибкости" эта задача разбита на море отдельных скриптов и вспомогательных библиотек общим размером в несколько тысяч строк. Для настройки этого монстра используется ещё одна куча файлов в /etc/sysconfig/ или /etc/conf.d/. При этом, даже когда вы чётко знаете, какой параметр нужно передать одной из команд при загрузке, вам нужно ещё найти в каком из файлов в /etc/conf.d/, в какой переменной и в каком формате вам нужно это указать... причём иногда оказывается что передача именно этого параметра именно этой команде не предусмотрена этими "универсальными и гибкими" монстрами, и вам приходится делать мелкие хаки тут и там. Кстати, чтобы узнать что и где нужно прописать зачастую документации не хватает, и приходится вычитывать эти раздутые скрипты чтобы понять как ими пользоваться.

Я использую другой подход. Моя система загружается одним небольшим скриптом (~200 строк) и использует при этом небольшую библиотечку (~80 строк). Для настройки вы просто редактируете этот скрипт прописывая в нём нужные вам команды и их параметры "as is". Практика показала, что при установке этого скрипта в нём достаточно прописать:

От минималистичной версии в 40 строк эта версия отличается дополнительными фичами:

Runlevels

По большому счёту необходимость в такой фиче как "runlevel" достаточно сомнительна. Но runit её поддерживает, и иногда она может быть удобна (обычно - когда пол системы не работает и её нужно чинить... :)). Для её использования вам нужно:

  1. Создать подкаталоги для каждого runlevel в /etc/runit/runsvdir/ с любыми именами (кроме current и default).
  2. Создать в этих подкаталогах симлинки на нужные сервисы из /service/*/.
  3. Сделать /etc/runit/runsvdir/default симлинком на тот runlevel, который должен запускаться по умолчанию.

Starting all services: /etc/runit/2

Здесь указываются:

В результате сообщения направляются следующим образом:

STDOUT/STDERR из /etc/runit/2
/var/log/all/.log
STDERR из /var/service/YOURSERVICE/ и /var/service/YOURSERVICE/log/
заголовок процесса runsvdir
STDOUT из /var/service/YOURSERVICE/
на STDIN /var/service/YOURSERVICE/log/run
STDOUT из /var/service/YOURSERVICE/log/
/var/log/all/.log
Вот схема для наглядности:
./run STDERR -----------------------------> PROCTITLE runsvdir
./run STDOUT --> STDIN ./log/run ---------------------------------------> /var/log/SERVICE/*/current
                       ./log/run STDERR --> PROCTITLE runsvdir
                       ./log/run STDOUT ------>------ runsvdir STDOUT --> /var/log/all/.log
                                                      runsvdir STDERR --> /var/log/all/.log

Special service: expirelog

Этот сервис используется для очистки заголовка процесса runsvdir от устаревших сообщений об ошибках, чтобы легче было замечать новые сообщения. Используется этот сервис следующим образом: изначально он выключен и автоматически не запускается (из-за файла /service/expirelog/down), а каждый раз когда вам нужно очистить заголовок runsvdir вы запускаете "однократно" expirelog (sv o expirelog).

Special service: log-all

Этот сервис читает из FIFO /var/log/all/.log и пишет прочитанное в лог /var/log/all/current. Этот сервис обязательно должен быть включен, т.к. если из FIFO /var/log/all/.log никто не будет читать, то процессы пишущие в него повиснут через некоторое время (как только полностью забьют буфер ядра для этого FIFO).

Special service: notify

TODO: Этого сервиса пока нет. Он должен получать отфильтрованные сообщения из /var/log/all/current и доставлять их админу через email/sms/osd_cat или любым другим способом.

Services

Helper script: term-getty-service

Helper script: up-wait4dep

Helper script: loginas

Helper script: srvstat

Logs

Логи я разделяю на три категории, в зависимости от их назначения:

  1. для отладки
  2. для статистики
  3. для чтения админом

Логи "для отладки" должны быть максимально детальными, но долго хранить их необходимости нет - эти логи будут просматриваться только для того, чтобы диагностировать текущую проблему с сервисом.

Логи "для статистики" должны быть максимально конкретизированы. Например, у pppd может быть несколько таких логов - один будет содержать статистику по времени соединения:

2006-04-17_06:52:57.70453 Connect time 2035.6 minutes.
2006-04-17_06:52:57.70460 Sent 11660759 bytes, received 37133308 bytes.
а второй по ошибкам:
2006-05-25_07:04:45.96936 LCP: timeout sending Config-Requests
2006-05-25_07:04:49.96956 pppoe: Timeout waiting for PADO packets
2006-05-25_08:24:13.25784 pppoe: read (asyncReadFromPPP): Session 15325: Input/output error
Как долго нужно хранить эти логи должно определяться индивидуально для каждого случая.

Логи "для чтения админом" должны быть читабельными! Это значит: кол-во таких логов должно быть минимальным, их размер - небольшим, и они должны при этом содержать только интересующую админа информацию. У меня такой лог один: /var/log/all/current и в него собирается информация из всех остальных логов.

Настраиваются логи следующим образом. Для каждого сервиса создаётся каталог /var/log/SERVICENAME/all/ для хранения логов "для отладки" плюс опционально несколько каталогов /var/log/SERVICENAME/stat1/, /var/log/SERVICENAME/stat2/, etc. для хранения логов "для статистики". Избранные записи "для чтения админом" отправляются на STDERR из all-лога (отбор осуществляется исключением ненужных строк, чтобы в лог "для чтения" попадали в том числе все новые/странные/неожиданные записи).

ФайлСодержимоеКомментарий
all/config n2
e*
Enon-interested1
Enon-interested2
хранить не более двух файлов
все строки продублировать админу "для чтения"
... кроме строки non-interested1
... и строки non-interested2
stat1/config -*
+statistic1
исключить все строки
... кроме строки statistic1
stat2/config n5
s1000000
-*
+statistic2
+statistic3
кол-во файлов со старыми логами ...
и их размер - настраиваются индивидуально
исключить все строки
... кроме строки statistic2
... и строки statistic3

Special case: all

Это лог "для чтения админом", куда собирается информация их всех остальных логов.

Special case: qmail

Special case: apache2

Special case: mysql