================================================================================ $Id: ПредложениеПоЗаменеИнит-Скриптов.txt,v 1.3 2003/11/04 09:38:00 cray Exp $ -------------------------------------------------------------------------------- Преамбула : Существующее решение для запуска сервисов, основанное на т.н. "init-" скриптах, приводит к некоторым проблемам, первая из которых - отсутствие единообразия: практически один и тот же код приходится писать для каждого демона. В тоже время, ряд необходимых функциональных особенностей не могут быть реализованы на основе init-скриптов и требуют поддержки со стороны запускаемого демона. Цель данного документа - предложить вариант решения, основанный на запуске специального приложения, назначе6ние которого - запуск и управление другими демонами. Текст составлен Андреем Орловым на основе обсуждения с Дмитрием Левиным и Александром Боковым. Общие предложения по архитектуре : Главным результатом обсуждения было то, что я отказался от идеи замены процесса init, и общая схема запуска будет выглядеть несколько иначе : init стартует специальный процесс, назовём его монитор, который стартует все остальные демоны. Существует несколько проблем, связанных с тем, что init выполняет некоторые специальные системные функции, которые должны быть делегированы процессу "монитор". Первая из них - init получает SIGCHLD от любого процесса в системе, не имеющего родителя. Эта информация крайне интересна процессу-монитору, так как позволяет обнаружить досрочное завершение демона и выполнить его рестарт. В то же время, уведомление процесса "монитор" о всех процессах, завершившихся в системе, может породить уязвимость связки init-монитор, используемую посредством специального процесса, порождающего кучу внезапно завершающиеся потомков. Для защиты от этого предлагается найти место в дескрипторе процесса под размещение специального флага "процесс под наблюдением", и рапортовать монитору только о завершении тех процессов, у которых этот флаг установлен. Естественно, такой флаг может быть установлен только одним процессом - монитором. Подробнее, см. ниже пункт "рестарт демонов". Следующая функция - переключение между runlevel'ами - в настоящее время делегирована инит-скриптам: все переключение runlevel'ов выполняют именно init-скрипты, а не сам init. Наше обсуждение привело к тому, что концепция runlevel'ов будет, видимо, сильно усложнена, так как современное состояние runlevel'ов имеет, похоже, лишь историческое значение. В мониторе будет введено некоторое расширенное множество runlevel'ов, некоторые из которых (0,1,2,3,4,5 и 6 как все уже поняли), будут соответствовать runlevel'ам из унаследованной концепции. Единственным правильным способом переключения runlevel'а станет использование монитора, а для совместимости в inittab какое-то время строчки :: l0:0:wait:/etc/rc.d/rc 0 l1:1:wait:/etc/rc.d/rc 1 l2:2:wait:/etc/rc.d/rc 2 l3:3:wait:/etc/rc.d/rc 3 l4:4:wait:/etc/rc.d/rc 4 l5:5:wait:/etc/rc.d/rc 5 l6:6:wait:/etc/rc.d/rc 6 Будут заменены на что-то вроде :: l0:0:wait:/sbin/monitor.chrl initrl_0 l1:1:wait:/sbin/monitor.chrl initrl_1 l2:2:wait:/sbin/monitor.chrl initrl_2 l3:3:wait:/sbin/monitor.chrl initrl_3 l4:4:wait:/sbin/monitor.chrl initrl_4 l5:5:wait:/sbin/monitor.chrl initrl_5 l6:6:wait:/sbin/monitor.chrl initrl_6 Иными словами, init связан с монитором также, как ранее были связаны init & init-скрипты. Подробнее об этом см. п. "Профили работы" Аналогичная замена будет выполнена и в других строках inittab, фактически, все функции init делегируются процессу монитор, кроме двух: старт процесса монитор и первичная обработка SIGCHLD. В нашей беседе нигде явно не всплывало следующее утверждение, но, из нашего разговора оно следовало, хотя и не было очевидно для меня с самого начала. Новая концепция старта демонов и мониторинга их состояние разбивается на несколько различных задач, выполнение каждой из которых может быть организовано различным образом в конкретной среде, или же эта задача может не выполнятся вообще. Удобно, чбы каждая такая задача решалась отдельным процессом - таким образом, мы приходим к схеме, аналогичной postfix: 1. Существует единственный процесс (собственно монитор) выполняющий базовые функции - старт других процессов, обслуживающих монитор, а также вышеизложенные служебные функции; 2. Существует стандартизованный интерфейс взаимодействия между служебными процессами монитора, общие конфигурационные файлы, etc. Старт демонов операционной системы берет на себя монитор (или, возможно, один из служебных процессов), остальные задачи распределяются между служебными процессами. Если в данной среде для данной задачи процесс не запущен - эта задача в ней не решается. Подробнее все служебные процессы будут описаны ниже, в соответствующих пунктах. Чбы было понятно, все их будем именовать как :: "monitor." <ИМЯ_СЛУЖЕБНОГО_ПРОЦЕССА> Тестовая задача : Запустить любое приложение как демон, независимо от того были или не были в этом приложении выполнены специальные действия, для того чбы быть демоном, и предоставить для этого приложения следующие сервисы : 1. Собственно старт ("демонизация": закрыть std*, сделать fork, etc); 2. Ведение логов (в том числе переназначение логов на syslogd etc); 3. Идентификация демона (необходимая, например, чбы прибить его); Одна из наших целей - избавится от аналогичного кода внутри самих демонов вообще. Следовательно, как тест можно предложить возможность запуска не интерактивной программы не имеющей какого-либо "демонизирующего" кода внутри себя как демона. Такой программой может быть, например, tcpdump (да знаю, знаю, сказал же - "например"). Известны реальные задачи, которые решаются написанием специальной обёртки вокруг процесса (у нас даже есть соотв. пакеты): - autossh; - вечно сохнущий arpwatch; - менее широко известная, но используемая лично мной, ipaudit, которую я запускаю через inittab; В системе есть аналогичный пример - xinetd - который предоставляет стандартную обёртку для "интерактивного" процесса, даже порт слушает вместо него. В нашем случае, нужно сделать обёртку для процесса не интерактивного. Функциональные особенности : Единая конфигурация старта : Сейчас в системе есть каталог - /etc/sysconfig - в который помещаются конфигурационные файлы, используемые для старта различных демонов. Внутри этих файлов полный бардак ;), который отчасти вызван аналогичным бардаком в написании скриптов для старта самих демонов. Однако интересно другое (лично мне это стало ясно после выхода Zope27, включающем в себя свою собственную систему конфигурировании), существуют две разновидности конфигурационных параметров, мало пересекающихся между собой. Первая из них - конфигурация собственно демона - неотторжима от самого демона и его отличительной особенностью является то, что она не меняется при переносе демона с одного "хостинга" на другой. Существует и другая - конфигурация среды демона - неотторжима от хостинга, и её отличительной особенностью является то, что она не меняется при замене одного демона другим. По историческим причинам, множества этих параметров пересекаются, т.е. конфигурационные файлы демонов содержат параметры конфигурации среды, поэтому реальный перенос демона с одного хостинга на другой или замена одного демона другим потребует правки его собственных конфигурационных параметров, хотя всегда есть возможность "правильно" написать конфигурационные параметры, что минимизирует сложности такого перехода: использовать относительные пути, алиасы и т.п.: не буду писать подробно, т.к. зависит от конкретных случаев. А теперь, собственно, параметры второй группы : - Параметры, включающие в себя настройку монитора для обслуживания демона данного типа; - Путь к рабочему каталогу демона; - Порты, которые слушает демон или ресурсы, которые он - использует; - Настройки логгинга; - Пользователь, под которым работает демон; - Профили, в которые входит демон (см.ниже); - Зависимости между демонами; Предполагается собрать все такие параметры в общей подсистеме конфигурирования, постаравшись обеспечить простоту замены одного демона другим или перенос демона с хостинга на хостинг (см. приложение 1). ПРИМЕР 1 : Ну, с переносом демона с хостинга на хостинг, неясностей, видимо, нет. А вот замену одного демона другим я проиллюстрирую: Допустим, мы используем ipaudit чбы считать пролетающие мимо пакеты. При этом : - ipaudit стартует, слушает интерфейс и складывает логи в файл у себя в рабочей зоне, рестартуя каждые 500 пакетов. Оные логи потом куда-то (не суть) утилизируются. Конфигурация демона : командная строка : ipaudit -c $npacket-o $workdir/$(date \ +%Y%m%d%H%M%S).log $interface В конфигурации старта описываются параметры $interface, $npacket, $workdir : : interface = eth0 npacket = 1000 workdir = /var/lib/ipaudit Демон можно легко перенести с одного хостинга на другой (хотя в случае с ipaudit это сомнительное преимущество); Допустим, у нас не оказалось под рукой тулзы ipaudit, зато нашлась тулза tcpdump : Конфигурация демона : командная строка : tcpdump -c $npacket -o $workdir/$(date \ +%Y%m%d%H%M%S).log $interface А вот конфигурация старта осталась неизменной ;), так что замена одного демона другим прошла легко и непринуждённо (за исключением того, что то что пишет ipaudit и то, что пишет tcpdump - это все-таки разные данные, но для простоты обсуждения оставим это за скобками этого примера). Виртуальные сервисы : Из предыдущего пункта чётко просматривается абстрагирование демона от среды. Следовательно, возможен запуск одного демона многократно, в различных средах. Об этом можно говорить как о виртуальном сервисе. Продолжая эти размышления придём к тому, что нарисовано в приложении 1 (не путать с рисунком 1): 1. Есть конфигурация демона, есть конфигурация виртуальной службы; 2. Стартует только виртуальная служба; 3. Для оказания услуг виртуальная служба использует того или иного демона; ПРИМЕР 2 : Я бы мог написать его более близким к реальности, на основе Zope, который сейчас именно так и стартует. Но, пусть примеры будут простыми. В ПРИМЕР 1 был введён сервис, который можно назвать "мониторинг сетевого устройства". Предоставим на его основе несколько виртуальных служб : eth0 : daemon = "pcaplistener" interface = eth0 npacket = 1000 workdir = /var/lib/ipaudit/eth0 eth1 : daemon = "pcaplistener" interface = eth1 npacket = 1000 workdir = /var/lib/ipaudit/eth1 lo : daemon = "pcaplistener" interface = lo npacket = 1000 workdir = /var/lib/ipaudit/lo Неплохо ;), добавим понятие шаблона : template : daemon = "pcaplistener" interface = $interface npacket = 1000 workdir = /var/lib/ipaudit/$interface eth0 : template = "template" interface = eth0 eth1 : template = "template" interface = eth1 lo : template = "template" interface = lo Совсем хорошо ;), даже коротко. Предстартовая подготовка среды : То, что реально делают init-скрипты, и что реально сложно заменить, это подготовка среды, в которой процесс будет запущен. Если посмотреть на init- скрипты для таких демонов как apache, zope, mysql задача начинает казаться трудно разрешимой. Но, если уж скрипты написаны ;) то почему бы их не использовать? Разумеется, вынеся оттуда те инварианты, который возможно вынести. К таким вариантам можно отнести : Подготовка чрутизированной среды -- chrooted-*-rpm мантейнит Дима, ему и слово ; Установка переменных окружения -- это уже достаточно проблематично, само по себе, так как получение значений этих переменных может потребовать запуска специального приложения (например, hostname); Открытие и передача открытых дескрипторов -- кажется, об этом Дима тоже напишет лучше чем я. Как приемлемое решение можно предложить указывать в конфигурационных параметрах демона (виртуальной службы) старт одного или нескольких вспомогательных процессов для подготовки среды. Эти процессы могут быть monitor-совместимыми (т.е. специально для этого написанными, понимающими конфигурационные файлы монитора и т.п.) и standalone - т.е. всеми остальными. Пример monitor-совместимого процесса - подготовка чрутизированной среды monitor.chrootenv (достаточно общее приложение, чбы его стоило написать). Отслеживание зависимостей между процессами : Собственно, это то, с чего начиналось наше обсуждение. На первый взгляд все просто: что бы запустить данный процесс, нужно чбы уже был запущен другой. Список процессов, список зависимостей, топологическая сортировка - готово. Однако есть два места, в которых лежат грабли : 1. Что бы запустить данный процесс, нужно дождаться чбы другой был готов обслуживать запросы. Что, например, в случае процесса Zope, может занять 5-10 минут времени после того, как Zope выполнит fork(). Причём, ни один процесс не имеет встроенных средств рапортовать о своей готовности. Таким образом, нужны некие средства диагностики завершённости старта процесса, которые подробно рассмотрены в п. "Старт процесса". 2. Зависимости между процессами это не только _старт_ необходимых процессов, но это ещё и _останов_ ненужных. К сожалению, я не вижу средств гарантирующих то, что за то время, которое mysqld был запущен чбы отвечать на запросы zope, к нему не подрубится какой-нибудь апач, который вовсе не анонсировал потребности в нем. Я вижу только два решения: счётчики доступа и файервол. Счётчик доступа увеличивается монитором после того, как какой-либо процесс затребует для своего старта данный процесс (или процесс будет стартован вручную), а файервол открывает для сетевых сервисов доступ к порту, который слушает данный процесс только для тех процессов, которые затребовали доступ к нему (есть у iptables такие возможности). Т.к. ни mysqld, ни postgresql сетевые сокеты (в норме) не слушают, обсуждение файервола оставим за скобками и сосредоточимся собственно на зависимостях и счётчиках доступа. Тем или иным образом, монитор получает запрос на старт группы виртуальных служб - либо как профиль работы, либо как команду оператора. Где-то летом у нас пробегало неплохое описание обслуживания такого процесса, надо будет переписать этот пункт с учётом его, а пока я дам самое простое описание : 1. Каждая виртуальная служба имеет: virtual_required - который содержат список служб, которые требуются для работы данной виртуальной службы; 2. Список виртуальных служб дополняется до тех пор, пока это возможно; 3. Выполняется топологическая сортировка списка, в процессе которой из списка выкидываются службы с неразрешёнными зависимостями или имеющие циклические зависимости (последнее, вообще гря, не совсем верно, но для простоты будет пока так), а также уже стартованные службы; 4. Для каждой службы, вытянутой в список по зависимости, инкрементируется счётчик доступа; 5. Выполняется старт необходимых служб. Если старт какой-либо службы проваливается, в логи пишется warning, и все службы зависящие от нее исключаются из списка кандидатов на старт. Если какие-либо службы были затребованы исключёнными процессами, их счётчик доступа уменьшается, если счётчик доступа достигает нуля - служба останавливается. При приходе запроса на останов службы логика прокручивается в обратную сторону: 1. Декрементируется счётчик доступа службы, если он достигает нуля - служба останавливается; 2. Отдаётся запрос на останов служб, зависящих от данной; Есть дополнительный вариант останова: форсированный останов службы. В этом случае, останавливаются все службы, зависящие от данной, причём монитор запоминает, какие службы были остановлены и их счётчики доступа. Форсированный останов, предположительно, будет использоваться администратором для проведения профилактических работ и будет сопровождаться последующим запросом на восстановление службы, что вызовет старт всех служб, остановленных ранее. Подробнее см. "Профили работы" и "Старт процесса по требованию". Старт процесса : Насколько я понимаю, без запуска специального процесса-обёртки, открывающего дескрипторы, устанавливающего переменные окружение и отдающего вызов chroot не обойтись, поэтому в конфигурации можно указать с помощью какого процесса выполнять запуск демона. До тех пор, пока эти процессы monitor-совместимы, из них можно делать цепочку, но один стандартный процесс monitor.start может покрыть 95% потребностей, если будет делать следующее: Переназначать (переоткрывать) std* туда, куда надо; Устанавливать переменные среды (возможно, получив их значение как вывод другого запущенного приложения); Сделать, если указано, chroot, setuid, setgid, setlimit, etc; Запустить целевое приложение; Ну и наконец, как заключительная опция переходного периода, у нас есть init-скрипты, большая часть из которых уже завязана на вызовы стандартной библиотеки /etc/init.d/functions. Для тех демонов, мантейнер которых не нашел времени на их перенос на новую схему, можно в качестве последнего этапа запуска писать "service что-то-там start". Кстати, functions по этому случаю можно переписать так, чбы обнаруживать то, что старт производится процессом monitor.start и выполнять упрощённый, совместимый с монитором, вариант обработки. Рестарт процессов : Имеется ввиду рестарт процесса в случае внезапного завершения. Вообще говоря, это едва ли не основное свойство, которое у инит-скриптов отсутствует напрочь, и ради которого авторы демонов регулярно пишут какие-то свом приблуды. Итак, задача: 1. Обнаружить аварию; 2. Чисто завершить процесс; 3. Чисто стартовать процесс; Обнаружение аварии - это сложное понятие, так как процесс может не только "рухнуть", но и "повиснуть" или перейти в иное недозволенное состояние ("извратится"?). Обнаружение такого проблема, в общем случае, выходит за рамки монитора и должно выполнятся специализированным приложением, возможно - специализированным для данного демона. Но один частный случай может (и должен) быть решён монитором: обнаружение внезапного завершения процесса, что покроет 95% потребностей. Такое внезапное заверение обслуживает inittab, и чгря, того что он делает вполне достаточно, но можно кое-что дополнить: 1. Процесс может быть рестартован при внезапном останове; 2. Процесс не будет рестартован, если он "хронически падает", причём критическая частота падений настраивается (например, 5сек для X-ов - явно недостаточно); 3. Процесс не будет рестартован, если он завершился с неким кодом завершение (код настраивается); 4. Некий администратор будет оповещён об упавшем процессе; Как нетрудно заметить, первые два пункта у нас уже есть в init. Отдельная тема - обнаружение внезапного завершения демона. Вспомним, что демон может сам по себе форкнутся и т.д.т.п. Выше упоминалось о некоем решении этого: записать в дескриптор процесса специальный флаг, в том числе указывающий (каким-либо образом) номер виртуальной службы, которой принадлежал данный процесс и т.п. Мне кажется, что это хоть и хорошее, но не необходимое решение, и в большинстве случаев достаточно просто рестартовать процесс, порождённый монитором, в момент получения монитором SIGCHLD. А все обслуживание эзотерического поведения форкающихся процессов (типа апача) возложить либо на сами эти процессы (которые содержат специальные средства для этого), либо на отдельный процесс, обнаруживающий аварии (о котором говорилось выше). Не смотря на то, что процесс рухнул, задачей монитора является обрушить его до конца - чбы после него не осталось никаких воспоминаний. Отчасти это все та же задача подготовки среды, отчасти - нет. Есть специальные проблемы, такие как завершить всех потомков данного процесса, явным отсылом SIGKILL (что критично, например, для Zope: рушится диспетчер, а восемь штук расчётных тредов продолжают лочить базу). Другая специфичная проблема возникает в связи с наличием зависимостей: нужно, как минимум, оповестить зависящие службы, и, возможно, остановить их, если повторные попытки рестарта рухнувшей служба показали ее неработоспособность. Ну и наконец - повторный старт процесса. Опять-же, зависимости, опять-же зависящие процессы должны быть оповещены. Способ оповещения зависимого процесса зависит от самого процесса. Это может быть генерация сигнала, рестарт, запуск специального приложения или какой-то иной способ. Обслуживание профилей работы : Профиль работы - это список служб, которые должны быть запущены, в случае когда данный профиль активен. Кроме того, это список служб, запуск которые должны быть остановлены при переходе к данному профилю. В этом смысле профили аналогичны runlevel'ам. Есть и отличия: 1. Количество пофилей неограничено. Для удобства конфигурации, можно попробовать упорядочить проили в древовидную структуру, где каждый профиль-потомок заимствует списки служб у своего родителя; 2. При переходе от старого профиля к новому происходит останов всех активных служб, перечисленных в старом профиле и не упомянутых в новом, все незапущенные службы, упомянутые в новом профиле запускаются; 3. На самом деле, списка запускаемых служб достаточно, чбы получить функциональность init-скриптов, список оставнавлваемых служб используется монитором иначе: это службы, которые будут остановлены при активации профиля, даже в том случае, если эти службы были запущены по требованию; 4. Состояние всех других служб (не упомянутых в старом или новом профиле) при переходе остается неизменным; 5. Обработка п. 2-4 включает в себя запуск и останов служб по зависимостям, а при останнове запрещенных служб, запущенных оператором в период активности старого профиля, останавливает все службы, запущенные по зависимостям (см. также п. Отслеживание зависимостей, там где про инкременталный счетчик). 6. Профиль обладает способностью сохрананения состояния, что выражается в том, что при отдаче команды форсированного запуска или останова служб, эту команду можно отменить - т.е. запустить остановленные по требованию службы (и их зависимости) либо наоборот - остановить запущенные. Возможность запуска служб по требованию включает в конфигурацию профиля еще одну возможность - перечисление служб, запускаемых по требованию. Старт службы по требованию : В отличие от традиционного init, монитор помнит свое состояние и, благодаря обработке зависимостей, гарантрует его целостность. Это дает возможность активного использование возможности запуска служб по требованию и даже ввести функционал, сходный с xinit, только не для прослушивания портов, а для прослушиваний неких виртуальных "событий". События, в нашем случае, могут быть "продолженными" - т.е. на самом деле такое событие превращается в пару событий - начало и конец. Интересны следующие типы событий: 1. Наступление временного промежутка. Например, демон smbd должен быть запущен в период 10:00 - 19:00, остальное время остановлен. Это может быть не слишком интересно для smbd, зато представляет интерес, скажем, для Zope, который в период работы отъедает изрядно памяти. Генерация таких событий может осуществлятся стандарными средствами - crond, at, etc. Все, что необходимо предоставить - утилиту monitor.event; 2. Начало пользовательской сессии. Например, демон postgresql стартует в момент логина на сервере разработки хотя бы одного из пользователей, входящего в группу разработки, и останавливается при выходе последнего из них. Хороший способ поддержки стандартными средствами - использование pam_session. 3. Начало пользовательских сессий в хостящихся серверах приложений. Не слишком интересно в случае Zope, зато ощутимо интересно в случае SMB: работничек притопал в офис, залогинился на своей винде, после чего на сервере был запущен ряд обслуживающих его служб. От 2-ого пункта это отличается тем, что не все сервера приложений в состоянии поддерживать понятие сессии. СКажем Zope, в силу специфики http-протокола, в состоянии обозначить только событие начала и событие продолжения ранее начатой сессии. Это вынуждает ввести специальный вид сессий - сессии, завершаемые по таймауту. Собыие начала сессии, в отличие от п.2., не инкрементирует счетчик доступа к запускаемым демона, а обнуляет время активности сессии. Если время активности достигает таймаута завершения - демоны останавливаются. Такую обработку сессии удобно мапить на стандартный сессионный механизм, те. события начала и конца сессии. 4. В предыдущих разделах уже был описан форсированный запуск и останов службы админиcтратором; Может показаться, что такие механизмы не слишком интересны для традиционных интернет-серверов, так как в силу специфики интернет (большое количество потенциальных пользователей, которые действуют независимо друг от друга), сервер находится в некотором "усредненном" состоянии и не имеет ярко выраженных переходов от одного состояния к другому. Но даже в этом большом количестве пользователей могут начинаться организованные процессы, приводящие к нарушению наивной вероятностной модели: яркий пример - 11 сентября, когда состояние аудитории интернет скачкообразно изменилось на некий хорошо различимый промежуток времени, продолжительностью, если мне не изменяет память, около 12ти часов. Тем не менее, обнаружение __таких__ событий уже выходит за рамки монитора, хотя возможность их обслуживания очень интересна. А вот для корпоративных серверов ценность поддержки таких сессионных механизмов сомнений не вызывает: так или иначе, для корпораций существует регламент работы, который отнюдь не всегда можно редуцировать к потребностям шедулера: скажем, из 5ти компаний, которым я так или иначе оказываю услуги администрирования, только в одной сотрудники более-менее работают "по часам", и по крайней мере в двух компаниях организация труда категорически не допускает использование шедулера вместо контроля сессий. Мониторинг работоспособности процессов : Как уже отмечалось, законченное решение задачи мониторинга работоспособности выходит за рамки управления службами и все, о чем нужно позаботится - это предоставить способы взаимодействия с монитором другим продуктам. Удалённое управление монитором : Учитывая возможность использования специальных средств мониторинга работоспособности служб, а также учитывая то, что для таких средств характерен запуск их на другом хосте, нужно предусмотреть интерфейс для отдачи монитору запроса на рестарт службы. В том число - удалённого запроса. Мне представляется очевидным решением написание специального демона monitor.notify, слушающего AF_UNIX сокет, или TCP over SSL, или другие аналогичные источники, предоставляющем через них информацию о работе служб и принимающим запросы на их рестарт. Как перейти на новую схему : В настоящий момент для каждого демона написан init-скрипт и просто так отказаться от этого наследия кажется слишком страшным шагом. Хотелось бы отметить: у каждого демона есть мантейнер, и так или иначе все мантейнеры основательно затачивают свои инит-скрипты под дистрибутив. Только что некие изменения были внесены в инит-скрипты, и все мантейнеры довольно резво на это отреагировали, переписав свои скрипты, особых проблем, насколько я знаю, ни у кого не возникло. Таким образом, даже если мы просто потребуем без всяких промежуточных шагов перейти на новую схему, больших проблем возникнуть не должно. В то же время, такой переходный период можно несколько облегчить: 1. Можно использовать унаследованные init-скрипты, оставив в конфигурации монитора специальные слоты под них. Например, под названием "командная строка запуска демона", "командная строка останова демона"; 2. Можно переписать /etc/init.d/functions так, чбы факт запуска под монитором обнаруживался и выполнялись некие иные действия по старту / останову процесса; 3. Разумеется, процесс service должен быть заменён на специальный процесс, передающий команду монитору (telmonitor (по аналогии с telinit)); Так как монитор становится точно на то место, где до сих пор были инит скрипты, то особых проблем с переходом возникнуть не должно. Просто небольшой кусок дополнительной работы для всех. Заключение : Получилось значительно больше, чем изначально предполагалось, но есть некоторое подмножество, реализации которого достаточно для того чбы перейти на новую схему, и это подмножество реально реализуемо за месяц-полтора. Я не останавливался в изложении на подробностях конфигурации, вместо этого написал приложения 2, 3 - содержащие примерный список команд конфигурации и пример файла конфигурации. Кроме того, приложение 4 содержит список тем, требующих дополнительной проработки специалистом (например, как идентифицировать процесс?). Приложение 1, Хранение конфигурации : Это очень быстрый набросок : 1. /etc/monitorrc или /etc/sysconfig/monitorrc -- конфигурация собственно монитора; 2. /etc/monitor.daemon.d -- каталог с файлами конфигурации демонов, подразумевается один бинарник - один демон - одна конфигурация, ни одна из этих конфигураций не является "достаточной" для старта сервиса, это всего лишь абстрактный слой, приводящий все разнородные приложения к общему виду; 3. /etc/monitor.virtual.d -- каталог с файлами конфигурации виртуальных служб, каждая виртуальная служба ссылается на один из файлов в /etc/monitor.d и является описанием старта виртуальной службы на основе одного из демонов, естественно, один демон может быть заменён на другой - такого же типа; 4. /etc/monitor.profile.d -- каталог с файлами конфигурации профилей загрузки. Каждый профиль описывает условия активации и список активных виртуальных служб; 5. /etc/monitor.aliasrc -- список условных имен для демонов и виртуальных служб, которые можно использовать вместо имен этих служб. Например "http_server = apache" или "http_server = zope". 6. /etc/monitor.template.d -- каталог с файлами темплейтов конфигурации виртуальных служб (см. ПРИМЕР 2). Понятно, что все эти *.d - лишь способ облегчить установку новых пакетов, в реальности это один конфигурационный файл порезанный на куски. Однако разбивка по каталогам соответствует некоторым концептуально различающимся секциям. Необходимо ввести некоторую типизацию, к сожалению, так как все демоны к одному виду привести невозможно, а возможность замены одного демона другим, в п.3, должна диагностироваться. Типизацию будем вводить на основе понятия "интерфейса": 1. В конфигурации демона будем указывать, что данный демон "provide" некий список интерфейсов. Интерфейс, в простейшем случае, абстрактное понятие, идентифицируемое уникальной строкой; 2. В конфигурации виртуальной службы будем указывать "require", некий список интерфейсов; 3. Будем говорить, что тип данного демона подходит для обслуживания данной службы, если данный демон "provide" все интерфейсы, "required" нашей службой. Возможно усложнение типизации, введением обратной ответственности виртуальной службы перед демоном, в этом случае служба декларирует обязательство предоставить демону некоторую требуемую ему среду. Приложение 2, Примерный список параметров конфигурации : cmd_start - командная строка старта демона, например, service zope start; provide_interfaces - интерфейсы, предоставляемые демоном; require_interfaces - интерфейсы, требуемые виртуальной службе; require - виртуальные службы, которые должны быть запущены до запуска данной службы. Существуют дополнительные параметры : commit_type - тип подтверждения, может быть strong & weak. Weak - означает, что достаточно отдать команду на старт. Strong - необходимо дождаться положительного завершения от программы диагностики старта; notify_type - способ уведомления о рестарте затребованной службы, может быть: ignore - нет уведомления; cmd - выполнить специальную команду; restart - рестартовать службу; signal - послать сигнал службе; require_type - strong или weak. Если strong - то невозможность старта затребованной службы делает невозможным старт данной службы, weak - попытка старта будет сделана, но ее результат безразличен; start_test - Тест, выполняемый для обнаружения завершённости старта. Может быть: ignore - нет теста; waitpid - дождаться завершения порождённого процесса; sleep - выждать интервал времени; waitsocket - дождаться открытия указанного сокета; greplog - дождаться появления указанной подстроки в лог-файле; log_type - Тип лога, может быть: self - служба сама разбирается куда девать лог; std - переназначение stdout, stderr в подсистему логгинга, для каждого потока указывается : log_storage - например, syslogd или путь к файлу; log_facility, log_priority - см. man syslog terminate_detect - Способ обнаружения внезапного завершения, может быть: ignore - нет способа; poll - опрос статуса по временному интервалу; wait_pid - SIGCHLD; terminate_frequency - Критическая частота завершения, если процесс падает чаше - он признается неработоспособным; terminate_code - Код ошибочного завершения, если процесс завершился с одним из перечисленных кодов - он признается неработоспособным; status_type - Способ получения статуса демона, может быть: run_pid - процесс хранит PID в /var/run lslk - процесс лочит некий файл; lsof - процесс получает доступ к некоторому файлу (например Zope - к файлу Data.fs); check_socket - процесс слушает сокет; cmd - должна быть запущена специальная утилита для получения статуса процесса, например service zope status; В принципе, может быть указано несколько статусов stop_type - Способ завершения демона, может быть: cmd - командная строка останова демона, например, service zope stop; signal - отправка сигнала демону, подразумевается, что PID демона получен с помощью status_type; template - для данной секции существует темплейт; prepare - список средств настройки среды. Каждой средство может иметь свои специализированные параметры конфигурации. Существуют стандартные средства : monitor.chrooted.env - подготовка чрутизированной среды; monitor.environment - установка переменных среды, для каждой переменной: <ИМЯ> '=' <ЗНАЧЕНИЕ> | '$' <ИМЯ> | '$(' <КОМАНДА> ')' monitor.fd - открытие файловых дескрипторов, видимо, параметры конфигурации - url + номер дескриптора; cmd - произвольная команда; Приложение 3, Пример файла конфигурации : У меня есть некая библиотечка для поддержки файлов конфигурации, предположим, что вся конфигурация реализована на ней. Тогда файл будет иметь следующий вид (на самом деле, он несколько упрощён по сравнению с возможностями той библиотечки, ну да не в ней суть):: [daemon zope] cmd_start = /usr/lib/zope/z2.py -f /var/lib/zope/$virtual/var/Data.fs provide_interfaces = 'http' start_test = waitsocket $port log_type = std [chanel stderr] log_storage = syslog log_facility = daemon log_priority = error [chanel stdout] log_storage = syslog log_facility = daemon log_priority = info terminate_detect = poll terminate_frequency = 10 status_type = lsof /var/lib/zope/$virtual/Data.fs stop_type = signal TERM prepare = monitor.chrooted.env zope [daemon postgresql] provide_interfaces = database authsource cmd_start = service postgresql start start_test = waitpid log_type = self terminate_detect = poll status_type = cmd service postgrsql status stop_type = cmd service postgrsql stop prepare = monitor.chrooted.env postgresql start_test = waitsocket /tmp/.PG.5234 [daemon mysql] provide_interfaces = database cmd_start = service mysql start start_test = waitpid log_type = self terminate_detect = poll status_type = cmd service postgrsql status stop_type = cmd service postgrsql stop prepare = monitor.chrooted.env mysql start_test = waitsocket /var/lib/mysql/mysql.sock [daemon postfix] provide_interfaces = mta [require database] commit_type = strong notify_type = restart require_type = strong cmd_start = service postfix start start_test = waitpid log_type = self terminate_detect = poll status_type = cmd service postfix status stop_type = cmd service postfix stop prepare = monitor.chrooted.env postfix [daemon sendmail] provide_interfaces = mta cmd_start = service sendmail start start_test = waitpid log_type = self terminate_detect = poll status_type = cmd service sendmail status stop_type = cmd service sendmail stop [service mta] require_interfaces = mta daemon = postfix start_test = waitsocket 25 [service database] require_interfaces = database daemon = postgresql [service zope_hosting] virtual = hosting require_interfaces = http [require database] commit_type = strong notify_type = restart require_type = strong [require mta] commit_type = weak notify_type = ignore require_type = weak port = 8080 [service zope_devel] virtual = hosting require_interfaces = http [require database] commit_type = weak notify_type = ignore require_type = weak port = 8180 [profile maintenance] database [profile hosting] zope_hosting exclude zope_devel [profile devel] zope_hosting zope_devel Идея конфига в том, что стартуют два Zope сервера, для обслуживания каждого из которых нужна база данных, а одного из - MTA. В качестве MTA может использоваться sendmail или postfix, причём последний требует базу данных. Есть три режима работы: обслуживания, хостинг и разработка. При обслуживании требуется только чбы база была активна, при хостинге должен быть активен zope_hosting, а при разработке - ещё и zope_devel. Причём при хостинге активность zope_devel запрещена. Длинновато, но не так плохо. Приложение 4, Вопросы для дополнительного изучения : 1. Как обнаружить внезапное завершение службы (особенно, в свете предложений по включению спец. флага в дескриптор процесса, есть ли там место и когда без этого можно обойтись); 2. Как идентифицировать процесс (/var/run/*pid, etc); 3. Как вести логгинг (например - переназначать весь stdout, stderr специальному процессу, передающему все на syslog (или ещё куда)); 4. Что входит в подготовку среды (открытие и передача сокетов, переменных etc); ================================================================================