rc.d
Так как все rc.d-конфиги являются shell-скриптами, можно не ограничиваться файлом /etc/rc.conf
и его собратом /etc/rc.conf.local
, а поместить фрагменты в каталоги /etc/rc.conf.d
и /usr/local/etc/rc.conf.d
. Единственная рекомендация — файлы должны называться так же, как использующие их службы. Это позволяет избежать превращения конфигурационных файлов в нечитабельных монстров из сотни с лишним строк, в которых без grep’а не разберёшься. Проблема в другом: если у службы есть несколько экземпляров, ВСЕ их настройки формально должны находиться в одном файле (имя которого, напомню, совпадает с именем службы). Чтобы решить и эту проблему, нужно разобраться, как работают скрипты запуска служб.
Обычно этот процесс описывают примерно следующим образом: " При старте системы выполняется скрипт /etc/rc
. Он считывает настройки из файла /etc/rc.conf
(а также /etc/rc.conf.local
— при наличии такового)…" А вот и нет! Вместо этого он включает в себя файл /etc/rc.subr
, определяющий кое-какие вспомогательные функции и точно так же включающий в себя файлы системных настроек:
... if ${_rc_conf_loaded:-false}; then : else if [ -r /etc/defaults/rc.conf ]; then debug "Sourcing /etc/defaults/rc.conf" . /etc/defaults/rc.conf source_rc_confs elif [ -r /etc/rc.conf ]; then debug "Sourcing /etc/rc.conf (/etc/defaults/rc.conf doesn't exist)." . /etc/rc.conf fi _rc_conf_loaded=true fi if [ -f /etc/rc.conf.d/"$_name" ]; then debug "Sourcing /etc/rc.conf.d/${_name}" . /etc/rc.conf.d/"$_name" fi ...
В свою очередь, файл /etc/defaults/rc.conf
содержит следующий код:
... rc_conf_files="/etc/rc.conf /etc/rc.conf.local" ... ############################################################## ### Define source_rc_confs, the mechanism used by /etc/rc.* ## ### scripts to source rc_conf_files overrides safely. ## ############################################################## if [ -z "${source_rc_confs_defined}" ]; then source_rc_confs_defined=yes source_rc_confs () { local i sourced_files for i in ${rc_conf_files}; do case ${sourced_files} in *:$i:*) ;; *) sourced_files="${sourced_files}:$i:" if [ -r $i ]; then . $i fi ;; esac done } fi
После загрузки всех настроек составляется список системных служб, и для каждой службы вызывается скрипт запуска — вне зависимости от того, включена она или нет. Поэтому настоятельно не рекомендуется помещать файлы пользовательских служб в каталог /etc/rc.d
— это увеличит время загрузки системы. Для пользовательских служб есть каталог /usr/local/etc/rc.d
— находящиеся в нём скрипты вызываются в последнюю очередь, когда основная система уже сконфигурирована и готова к работе. Но настоящая чёрная магия начинается дальше: каждый из этих скриптов снова включает скрипт /etc/rc.subr
, загружающий относящиеся к нему переменные. И вот это-то даёт возможность делать ещё более модульные файлы настроек: достаточно включить в основной файл в каталоге /etc/rc.conf.d
код вроде следующего (сразу на примере unicorn):
unicorn_enable="YES" unicorn_profiles="" for p in $(grep -rlE '^unicorn_[0-9a-zA-Z]+_enabled="[Yy][Ee][Ss]"$' /usr/local/etc/unicorn.d); do bn=$(basename $p) if [ -n "$unicorn_profiles" ]; then unicorn_profiles="$unicorn_profiles $bn" else unicorn_profiles="$bn" fi . $p done
Unicorn
Первым делом поставим sudo
— это намного удобнее, чем «лепить» команду для запуска через su, напрашиваясь на shell injection.
cd /usr/ports/security/sudo && make install clean
Ещё мы поставим утилиту под названием portmaster — это облегчит дальнейший процесс:
cd /usr/ports/ports-mgmt/portmaster && make install clean
Весь остальной софт будем ставить с её помощью:
LN='ln -f' portmaster devel/bison textproc/libxml2 textproc/libxslt textproc/diffutils devel/gmake security/openssl devel/automake devel/git devel/subversion shells/bash
Для ленивых — установка окружения по умолчанию:
portmaster lang/ruby19 sysutils/rubygem-bundler www/rubygem-unicorn
Описание дальнейших шагов можно найти в оригинальной статье, а мы переходим к следующей части — установке всего этого добра в качестве службы.
Для того, чтобы иметь возможность управлять множеством экземпляров службы, в скрипт запуска необходимо включить следующий код:
is_unicorn_profile() { local profile for profile in $unicorn_profiles; do if [ "$profile" = "$1" ]; then return 0 fi done return 1 } if [ -n "${unicorn_profiles}" ]; then if [ -n "$2" ]; then profile="$2" if ! is_unicorn_profile $profile; then echo "$0: no such profile defined in unicorn_profiles." exit 1 fi eval unicorn_socket=\${unicorn_${profile}_socket:-"/tmp/${name}-${profile}.sock"} eval unicorn_config=\${unicorn_${profile}_config} eval unicorn_dir=\${unicorn_${profile}_dir} eval unicorn_flags=\${unicorn_${profile}_flags:-"${unicorn_flags}"} eval unicorn_environment=\${unicorn_${profile}_environment:-"${unicorn_environment}"} eval unicorn_rails=\${unicorn_${profile}_rails:-"${unicorn_rails}"} eval unicorn_user=\${unicorn_${profile}_user:-"${unicorn_user}"} eval unicorn_procname=\${unicorn_${profile}_procname:-"${unicorn_procname}"} eval unicorn_bundler=\${unicorn_${profile}_bundler:-"${unicorn_bundler}"} eval unicorn_rvm=\${unicorn_${profile}_rvm:-"${unicorn_rvm}"} eval unicorn_ruby=\${unicorn_${profile}_ruby:-"${unicorn_ruby}"} if checkyesno unicorn_rvm; then unicorn_procname="~${unicorn_user}/.rvm/rubies/ruby-${unicorn_ruby}/bin/ruby" fi elif [ -n "$1" ]; then for profile in ${unicorn_profiles}; do echo "Processing ${name} profile: ${profile}" $0 $1 ${profile} done exit 0 fi fi
Использование RVM создаёт проблему: для разных приложений необходимо подстраивать окружение. Можно, конечно, после запуска каждого экземпляра переинициализировать окружение, но проще воспользоваться вспомогательным скриптом:
if checkyesno unicorn_rvm; then if [ -d "~${unicorn_user}/.rvm/${unicorn_ruby}" ]; then /usr/local/bin/sudo -u $unicorn_user /usr/local/bin/unicorn-wrapper ${unicorn_ruby} $(basename ${command}) ${command_args} rc=$? else echo "Ruby version ${unicorn_ruby} not found by RVM for user ${unicorn_user}" rc=1 fi else /usr/local/bin/sudo -u $unicorn_user ${command} ${command_args} rc=$? fi
Файл /usr/local/bin/unicorn-wrapper
— простая обёртка, устанавливающая нужную версию Ruby и выполняющая заданную команду с аргументами. Полную версию скрипта запуска можно взять рядом. Пользоваться очень просто:
service unicorn <action> [<app>]
Пример конфига:
unicorn_redmine_enabled="YES" unicorn_redmine_dir="/var/www/sites/mycoolsite.tld/redmine" unicorn_redmine_rails="NO"
По-хорошему, надо бы оформить Port Request, но пока руки не дошли.
P. S. Остался открытым только один вопрос: как это добро интегрировать с тем же capistrano, чтобы при обновлении не слетали настройки?
ссылка на оригинал статьи http://habrahabr.ru/post/166807/
Добавить комментарий