Runit: Overview
...THIS IS WORK IN PROGRESS...
Runit состоит из трёх независимых частей:
- загрузчик - замена sysvinit
- сервисы - замена скриптам /etc/init.d/*
- логи - запись на диск; фильтрация; ротация (замена logrotate)
К сожалению, конфигурация получается довольно сильно связанная между собой, например скрипт загрузчика /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". Практика показала, что при установке этого скрипта в нём достаточно прописать:
- hostname
- нужные модули ядра (modprobe)
- настройки сети (ifconfig/route)
- раскладку клавиатуры (loadkeys)
- Выполняемые команды разделены на несколько блоков.
- Ускоренная параллельная загрузка (аналог initng). Можно управлять параллельным/последовательным выполнением блоков.
- По каждому блоку при загрузке выводится либо краткая информация если блок выполнен без ошибок, либо полная информация по всем командам этого блока и возникших ошибках.
- Если возникают ошибки то предлагается "press any key in 5 seconds to open shell" для исправления этих ошибок вручную перед продолжением загрузки/выключения.
- Ведение лога всего что выводится на экран при загрузке и выключении (удобно для диагностирования проблем на удалённых серверах). Логи сохраняются в файлы /var/log/boot и /var/log/shutdown .
- Поддержка выбора runlevel при загрузке через параметры ядра "runlevel=YOURUNLEVELNAME" или "S" ("S" идентичен "runlevel=single").
Runlevels
По большому счёту необходимость в такой фиче как "runlevel" достаточно сомнительна. Но runit её поддерживает, и иногда она может быть удобна (обычно - когда пол системы не работает и её нужно чинить... :)). Для её использования вам нужно:
- Создать подкаталоги для каждого runlevel в /etc/runit/runsvdir/ с любыми именами (кроме current и default).
- Создать в этих подкаталогах симлинки на нужные сервисы из /service/*/.
- Сделать /etc/runit/runsvdir/default симлинком на тот runlevel, который должен запускаться по умолчанию.
Starting all services: /etc/runit/2
Здесь указываются:
- путь к каталогу с сервисами которые нужно запускать: /var/service
- FIFO куда сохранять STDOUT/STDERR этого скрипта: /var/log/all/.log
- в каком формате выводить STDERR запускаемых сервисов в заголовке самого процесса runsvdir (чтобы можно было просматривать сообщения о ошибках через ps в ситуации когда сервисы отвечающие за логи сами не запускаются)
В результате сообщения направляются следующим образом:
- 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
Логи я разделяю на три категории, в зависимости от их назначения:
- для отладки
- для статистики
- для чтения админом
Логи "для отладки" должны быть максимально детальными, но долго хранить их необходимости нет - эти логи будут просматриваться только для того, чтобы диагностировать текущую проблему с сервисом.
Логи "для статистики" должны быть максимально конкретизированы. Например, у 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
Это лог "для чтения админом", куда собирается информация их всех остальных логов.