Введение
Я работаю в небольшой транспортной компании, занимаюсь администрированием зоопарка из win/linux машин. Выход нового дистрибутива Debian совпал с необходимостью внедрить несколько новых серверов на предприятии, точнее заменить старые работающие под win на новые под linux. Таким образом для внедрения был выбран Squeeze, я думал что управлюсь за день, но новый Debian, по мимо нового ПО, принес еще и сюрпризы.
Осознание проблемы
Утром я поставил систему, установил все необходимые пакеты, дальше начинаю прикручивать к системе скрипты (собственно сервер же заводим не для того чтобы им любоваться, а для работы). Сразу же уточню: cкрипт настраивал проксирование пользователей в интернет, все настройки он брал из PostgreSQL. Таким образом он должен запускаться после того как будет сконфигурирована сеть и запущен демон СУБД. И тут начинается мистика, создаю символическую ссылку на скрипт:
ln /usr/proxy/scripts/start /etc/rc1.d/S99proxy
ln /usr/proxy/scripts/start /etc/rc2.d/S99proxy
ln /usr/proxy/scripts/start /etc/rc3.d/S99proxy
ln /usr/proxy/scripts/start /etc/rc4.d/S99proxy
ln /usr/proxy/scripts/start /etc/rc5.d/S99proxy
Перезагружаю сервер, и обнаруживаю что скрипт не запустился, во всяком случае нужных мне действий произведено не было. Начинаю хмуриться, в это время вспоминаю о статье, которую читал несколько дней назад. Помещаю симлинк в другое место:
ln /usr/proxy/scripts/start /etc/network/if-up.d/proxy
Перезагружаю систему, скрипт начинает выполняться, но снова провал. Скрипт настройки сети запускается раньше чем стартует демон СУБД, и соответственно содержимое /etc/network/if-up.d выполняется тоже раньше (собственно оно так и должно быть). Далее решаю поместить в директории rc*.d скрипт со следующим содержанием:
#!/bin/bash
echo "Run test" >> /var/log/test.log
Снова перезагружаю систему, результат нулевой. Помня все туже статью, в голову приходи дикая мысль о том что локальная файловая система не успела примонтироваться. Применяю метод админа-шамана:
#!/bin/bash
sleep 20
echo "Run test" >> /var/log/test.log
После перезагрузки обнаруживаю отсутствие файла /var/log/test.log. От такого облома я решаю схитрить — дописываю в скрипт запуска демона ssh строку с echo. После перезагрузки как и полагается обнаружил то что ждал, файл создался и строчка «Run test» в нем присутствует. Начинаю активно шевелить мозгами, в уме появляется уравнение: запуск скрипта не работает + новая система запуска скриптов при старте системы = неожиданное поведение. Вывод напрашивается один: надо
Вот где собака зарыта
В ходе исследования было выяснено что в Debian Squeeze используется система LSBInitScripts для запуска скриптов при загрузке ОС. Ее особенностью является — по возможности параллельный запуск скриптов. В предыдущем предложении слова «по возможности» являются ключевыми. Нельзя просто взять и параллельно запустить все скрипты, часть скриптов имеет определенные зависимости от других служб. В моем случае скрипт должен выполняться только после запуска СУБД и настройки сети. Для этого LSBInitScripts строит граф зависимостей. Его графическое представление можно увидеть выполнив команды:
aptitude install insserv graphviz
/usr/share/insserv/check-initd-order -g > boot.dot
dotty boot.dot
В моем случае это получилась полнейшая кракозябра. Встал вопрос о том где же хранятся эти зависимости. В вики Debian'на выяснилось, что зависимости указываются в комментариях у скриптов в директории /etc/init.d, вот пример такого комментария для ssh:
### BEGIN INIT INFO
# Provides: sshd
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: OpenBSD Secure Shell server
### END INIT INFO
Provides — это имя или несколько имен того с чем работает(запускает/останавливает) скрипт. Имена должны быть уникальны.
Required-Start— а вот это как раз и есть наша зависимость, которая указывает что потребуется скрипту для запуска.
Default-Start — показывает на каких уровнях нужно выполнять загрузку скрипта.
Более детальное описание параметров есть здесь.
Создаем свой скрипт
Теперь, когда мы знаем нашего врага в лицо можно попробовать написать пару простеньких скриптов. Вот первый, назовем его test:
#!/bin/sh
### BEGIN INIT INFO
# Provides: test-service
# Required-Start: $local_fs
# Required-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Test-service
### END INIT INFO
echo "Run test" >> /var/log/test.log
Вот второй:
#!/bin/sh
### BEGIN INIT INFO
# Provides: test2-service
# Required-Start: $local_fs test-service
# Required-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Test-service
### END INIT INFO
echo "Run test2" >> /var/log/test.log
Файлы называются test и test2 соответственно. В данном примере test2 зависит от test, и поэтому test будет запускаться первым, а затем только test2. Поместим их в директорию /etc/init.d. Но этого еще мало, и даже мало будет создания симлинков в /etc/rc*.d. Что бы заставить все это дело работать необходимо выполнить построение нового графа зависимостей, делается это командами:
update-rc.d test defaults
update-rc.d test2 defaults
На полное изучение документации команды update-rc.d пока времени не было. Данная команда переписывает файлы .depend.boot, .depend.start, .depend.stop (это файлы с графом зависимостей, хранятся в /etc/init.d) и создает все нужные симлинки в /etc/rc*.d. Теперь пробуем перезагрузиться, и обнаруживаем что скрипты выполнились. И не просто выполнились абы как, а в определенной последовательности. Если поменять зависимость между test и test2 на обратную то и загрузятся они по другому.
Со своим скриптом я больше сегодня не экспериментировал — голова уже начала плохо думать и время рабочее закончилось, займусь им завтра.
Заключение
В данной статье я не пытался полностью описать новый процесс запуска скриптов в Debian. Моей целью было дать отправную точку для изучения проблемы, сэкономить время других людей (у меня ушло несколько часов на поиск причины, чтение манов и эксперименты).
Источники которыми я пользовался
citforum.ru/news/25677
www.opennet.ru/opennews/art.shtml?num=23318
wiki.debian.org/LSBInitScripts/DependencyBasedBoot
wiki.debian.org/LSBInitScripts
man update-rc.d
man insserv