daemontoolsはデーモンを制御するためのツール群である。指定された1つの デーモンを監視するsupervise、複数のsuperviseを管理するsvscan、 superviseに指示を与えるためのコマンドインタフェースsvc、ログ収集を行 うmultilogなどから構成されている。
svscanおよびsuperviseは、デーモンの起動制御を行う。起動したデーモンの 状態を監視し、何らかの不測の原因でデーモンが停止した場合には自動的に 再開させる。また、新たに登録されたデーモンがあればそれを直ちに起動す る: svscanやsuperviseに対して明示的に指令を与える必要はない。
デーモンを登録するためには、そのデーモン用のディレクトリを作成し、そ の中に起動スクリプトを置く必要がある。svscanおよびsuperviseは、そのディ レクトリ内に一連の管理ファイル群を置いてデーモンの制御を行う。
必要があれば、multilogを用いて各デーモンごとにログ収集を行うことがで きる。
各デーモンの制御にはsvcコマンドを用いる。停止/再開/シグナル送信などの 操作を行うことができる。状態問い合わせにはsvstatコマンドを用いる。
よくあるデーモン実装では、コマンドラインから起動されたプロセスは forkしてデーモン本体となる子プロセスを生成し、起動された方の親プロ セスはそのまますぐに終了するという方式を採っている。これにより、 forkの返り値で返される子プロセスのプロセスIDは永遠に失われてしまう。 それゆえ、pidファイルやkillallコマンド、デーモンが起動していること を示すロックファイルなどのある種トリッキーな手段に頼ったプロセスID 管理メカニズムを用いなければならない。このプロセスIDの管理方法はデー モンによってまちまちであるため、システム管理者への負荷が増大する。
デーモンを監視するデーモンsuperviseはこの子プロセスのプロセスIDを放 棄しない。親プロセスであるsuperviseは死ぬことなく子プロセスを監視し 続ける。
multilogは可能な限りログを取りこぼさないよう設計されている。指定さ れたファイルにログを書き続け、指定されたサイズを越えると自分自身で ログのローテーションを行う。これにより、指定されたディスク容量以上 のログ領域を使用しないことが保証される。また、何らかの要因によりロ グの書き出しができなくなると動作を休止するが、プロセス自体は停止せ ずメモリ上に可能な範囲でログを保持し続ける: 書き込み可能になり次第 動作を再開する。
UNIX標準といってよいログ収集デーモンsyslogdは、高負荷時にログを取り こぼす不具合がある。自分自身でログのローテーションを行うことができ ないため、ローテーションのためにcron等の手助けが必要である。ログファ イルのサイズを監視しないため、ファイルシステムを溢れさせてしまうこ とを正しく防止できない。
デーモンに与える権限や資源を制限するためのツールが揃っている。ユー ザIDやグループIDの変更、使用メモリ量の制限などを一元的に指示できる。
以下の記述はdaemontools-0.76のインストール方法である。最新情報は 本家のdaemontoolsのインストール方法 を参照されたい。
インストール先は${prefix}で示されている。例えば/usr/localなど、好みに 応じて選択する。
wgetコマンドなどで daemontools-0.76.tar.gz を取ってくる。
取ってきたソースファイルを展開する。ちょっと変わったディレクトリ構 成をしているので適当に修正する。
% tar xvzf daemontools-0.76.tar.gz % mv admin/daemontools-0.76 .; rmdir admin % cd daemontools-0.76
コンパイル方法をsrc/conf-ccに、リンク方法をsrc/conf-ldに記述する。
% ./package/compile
# mkdir -p ${prefix}/bin
# cp command/* ${prefix}/bin
svscan(およびsupervise)は、デーモンの起動制御を行うデーモンである。 daemontools環境でデーモン制御を行うためには、まずsvscanを起動する必 要がある。svscanは監視対象のデーモンごとにsuperviseを起動し、デーモ ンの状態を監視させる。
svscan管理用のディレクトリ(daemontools推奨は /service)を作成し、そ のディレクトリ内にセットアップ済のデーモン用のディレクトリへのシン ボリックリンクを張る。svscanだけを起動するなら何もなくてもよい。
# mkdir /service
以下のようにバックグラウンドで実行させる。残念ながら、svscanだけは 通常のバックグラウンド起動のデーモンと同様の起動方法を用いる必要が ある。
# sh -c 'svscan /service &'
監視しているデーモンがいる場合、svscanの停止は少々面倒である: 以下 の手順で停止させなければならない。
以下に停止手順の例を示す。
# kill ${svscan's pid}
# for i in /service/*; do
# test -x $i/run && svc -dx $i
# test -d $i/log -a -x $i/log/run && svc -dx $i/log
# done
起動と停止を 自動で行うスクリプトを記述し、システムに登録する。起動スクリプトの サンプルはこちら: RedHat用 Debian用
管理対象のデーモンは、以下の条件を満たさなければならない。
forkしてバックグラウンドで実行されてしまうデーモンは監視できない。 ただし、fghackコマンドで回避できる場合がある。
自分の子プロセス以外のプロセスに干渉するデーモンは正しく監視でき ないことがある。
また、ログ収集の対象とするデーモンは、以下の条件を満たさなければな らない。
標準出力だけがログ収集の対象である。標準エラー出力は簡単な操作で ログ収集の対象にできる。syslog出力やその他の独自ファイルへの出力 などは、daemontoolsによるログ収集の対象にできない。
1つのデーモン制御用に1つディレクトリを作成する。場所に特に制限はな いが、筆者は好みで /var 以下にディレクトリを作成している。
# mkdir /var/openssh
作成したディレクトリの下に run という実行可能ファイルを作成する。 runが目的のデーモンとなって動作するように記述する。例えば以下:
#!/bin/sh exec 2>&1 exec /usr/sbin/sshd -De
作成後、runを実行可能にする。
# chmod +x /var/openssh/run
目的のデーモンをrunスクリプトの子プロセスとして起動するのではなく、 runを実行したプロセス自身を当該デーモンに変身させなければならない。 これを実現するために、exec指定を忘れないこと。
ログを収集する場合は、さらにその下に log/というディレクトリを作成す る。log/内にも上位ディレクトリと同様、runという実行可能ファイルを作 成する。log/とlog/runが存在すると、svscanとsuperviseはrunとlog/run を対で起動し、両者をパイプで接続する。runが標準出力に出力すると、 log/runの標準入力に入力される。log/runでmultilogを起動することで、 ログ収集を行うことができる。
multilog のためのグループとユーザを選定し、ログ出力先ディレクトリを 作成する。ファイル/ディレクトリの許可情報を正しく設定する。
以下では、ユーザsshlog(グループnofiles)を使用している。
# mkdir /var/openssh/log # mkdir /var/openssh/log/main # ログ出力先ディレクトリ # chown sshlog:nofiles /var/openssh/log/main # sshlog:nofiles 権限でログを収集 # chmod go-rx /var/openssh/log/main
/var/openssh/log/runを編集、実行可能にする:
#!/bin/sh exec setuidgid sshlog multilog t ./main # chmod +x /var/openssh/log/run
なお、daemontools-0.70もしくはそれ以前でログ収集を行う場合は、以下 のように登録用のトップディレクトリのstickyビットを設定しておく必要 がある。daemontools-0.76以降ではこの操作は不要である。
# chmod +t /var/openssh
/service以下に、作成したデーモン用ディレクトリへのシンボリックリン クを張る。この操作により、目的のデーモンが自動的に起動される。
# ln -s /var/openssh /service
/service 以下のシンボリックリンクを消去し、デーモンとsuperviseを停 止させる。もしあればログ用のデーモンとsuperviseも停止させる。
# rm /service/openssh # svc -dx /var/openssh # svc -dx /var/openssh/log # もし log/ があれば
サービスを開始する、もしくは停止していたものを再開する。
# svc -u /service/openssh
サービスを停止させる。デーモンにはTERMシグナル、それでも停止しなけ ればCONTシグナルが送信される(superviseによる再起動は行われない)。
# svc -d /service/openssh
TERMシグナルを送信してデーモンを停止させ、superviseに再び起動させる。
# svc -t /service/openssh
TERMシグナルで停止しないデーモンに対しては別のシグナルを用いること。
svcに与える引数でシグナルの種類を選択する。対応は以下の通り:
| 引数 | シグナル |
|---|---|
| -p | STOP |
| -c | CONT |
| -h | HUP |
| -a | ALRM |
| -i | INT |
| -t | TERM |
| -k | KILL |
# svc -h /service/openssh # HUPシグナルを送信する
multilogは、標準入力からのメッセージを読み込んで任意のファイルに出 力するツールである。
multilogに対する指示はコマンドライン引数で与える。multilogは入力さ れた行に対して指示を順に適用していく。指示には、タイムスタンプ付加、 選択/非選択、出力先指定、ファイル数/ファイルサイズ指定などがある。 以下に説明的な例を示す(書き方としてはやや冗長である)。
multilog t -'*' +'*Accepted*' ./login +'*' ./main
結果として、すべての出力がmain/に、'Accepted'というパターンを含む行 はlogin/にも出力される。両ディレクトリ内には複数のログファイルが置 かれ、それぞれ独立にログのローテーションが行われる。
タイムスタンプを付加する。タイムスタンプはTAI64N形式で出力される: tai64nlocalコマンドで可読文字列に変換することができる。
'pattern' にマッチする行を、'+' なら選択、'-' なら非選択にする。 pattern は '*' とそれ以外の文字からなる文字列で、'*' は直後に指定 された文字以外の文字からなる任意の文字列に、それ以外の文字はその 文字そのものにマッチする。終端の'*'はすべての文字列にマッチする。 pattern はシェルにとっての特殊文字を含むことが多いので、常にシン グルクォートで囲うようにするとよい。
ディレクトリ 'dir'/ 以下にログを出力する。'dir'/ 以下には複数のファ イルが置かれる:
s'size' は数字'size'でそれぞれのログファイルの最大ファイルサイズ を、n'num' は数字'n'で出力ディレクトリ内のログファイルの数を指定 する。この指定よりも後に現れる ./'dir' 指定に適用される。
ファイル 'file' の中身をその行で置き換える。
ログのローテーションを行う際にコマンド 'script' をフィルタとして 適用する。
multilogにALRMシグナルを送信することで、ログを強制的にローテートさ せることができる。
# svc -a /service/openssh/log
ログの各行に通常付加されるタイムスタンプは人間に可読ではない。 tai64nlocalコマンドで読みやすい形に変換する。
% tai64nlocal < current
setuidgidコマンドはユーザIDとグループIDを変更する。root権限が不要な プロセスに使用する。例えばsshd用のmultilogを sshlog というアカウン トのユーザID・グループIDで実行するなら、以下のようにする:
setuidgid sshlog multilog ...
envuidgidコマンドは指定したアカウントのユーザIDとグループIDを環境変 数$UIDと$GIDに設定する。起動するプログラム側で環境変数を読み込んで 使用する。
envuidgid sshlog <program>
softlimitコマンドは、引数で指定されたソフト資源制限(soft resource limits)を設定する。例えば1プロセスあたりのデータセグメントを1Mバイ トに制限するなら、以下のようにする:
softlimit -d 1048576 <program>
引数と資源の対応は以下の通り。
| 引数 | 意味 |
|---|---|
| -m | -d, -s, -l, -a を同時に指定するのと同じ |
| -d | 1プロセスあたりのデータセグメントのバイト数 |
| -s | 1プロセスあたりのスタックセグメントのバイト数 |
| -l | 1プロセスあたりのロックされた物理ページのバイト数 (OSによっては無効) |
| -a | 1プロセスあたりの全セグメントの総バイト数 (OSによっては無効) |
| -o | 1プロセスあたりのオープン可能なファイル記述子数 (OSによっては無効) |
| -p | 1ユーザIDあたりのプロセス数 |
| -f | 出力するファイルのバイト数 |
| -c | coreのバイト数 |
複数の環境変数を設定するときにはenvdirコマンドが便利。1つのディレク トリ内に置かれたファイル群から環境変数情報を読み込み、まとめて設定 してくれる。ディレクトリには、ファイル名が環境変数名、中身が環境変 数の値であるファイルを任意の数だけ置いておく。以下はdnscacheでの例 である。
# cd /var/dnscache # mkdir env # echo 3000000 > env/DATALIMIT # echo 1000000 > env/CACHESIZE # echo 127.0.0.1 > env/IP # echo 0.0.0.0 > env/IPSEND # echo /var/dnscache/root > env/ROOT
デーモンの起動スクリプトで読み込むように記述する。
#!/bin/sh exec < seed exec 2>&1 exec env - PATH="/var/dnscache/bin:$PATH" \ envuidgid dnscache \ envdir ./env \ sh -c 'exec \ softlimit -o250 -d"$DATALIMIT" \ dnscache '
以下の例では、次のような方針で設定を行っている。
各サーバのための個別の設定が入っている場合は、以下ではデフォルト値 を用いている。必要に応じて書き換える必要があるかもしれない。
${clockspeed prefix}/bin へのリンクにする。clockspeedを prefix=/var/clockspeed としてインストールしてもよい。
#! /bin/sh
exec < /dev/null
exec > /dev/null 2>&1
exec env - PATH="/var/clockspeed/bin:$PATH" \
clockspeed
${clockspeed prefix}/bin へのリンクにする。
#! /bin/sh
exec < /dev/null
exec > /dev/null 2>&1
exec env - PATH="/var/taiclockd/bin:$PATH" \
taiclockd
${djbdns prefix}/bin へのリンクにする。djbdnsを prefix=/var/dnscache としてインストールしてもよい。
#! /bin/sh
exec < seed
exec 2>&1
exec env - PATH="/var/dnscache/bin:$PATH" \
envuidgid dnscache \
envdir ./env \
sh -c 'exec \
softlimit -o250 -d"$DATALIMIT" \
dnscache
'
#! /bin/sh
exec setuidgid dnslog multilog t \
n10 s99999 ./main
空ディレクトリを作成。ownerはdnslog。group/otherはアクセス不能に 設定。
${djbdns prefix}/bin へのリンクにする。djbdnsを prefix=/var/tinydns としてインストールしてもよい。
#! /bin/sh
exec < /dev/null
exec 2>&1
exec env - PATH="/var/tinydns/bin:$PATH" \
envuidgid tinydns \
envdir ./env \
sh -c 'exec \
softlimit -d"$DATALIMIT" \
tinydns
'
#! /bin/sh
exec setuidgid dnslog multilog t \
n10 s99999 ./main
空ディレクトリを作成。ownerはdnslog。group/otherはアクセス不能に 設定。
300000
#! /bin/sh
exec < /dev/null
exec 2>&1
exec env - PATH="/var/qmail/bin:$PATH" \
qmail-start ./Maildir/
#! /bin/sh
exec setuidgid qmaill multilog t \
n10 s99999 ./main \
-'*' \
+'* status: *' \
=status
空ディレクトリを作成。ownerはqmaill。group/otherはアクセス不能に 設定。
空ファイルを作成。ownerはqmaill。group/otherはアクセス不能に設定。
/var/qmail/bin へのリンクに。
#! /bin/sh
exec < /dev/null
exec 2>&1
exec env - PATH="/var/qmail-smtpd/bin:$PATH" \
envdir ./env \
envuidgid qmaild \
sh -c 'exec \
softlimit -d"$DATALIMIT" \
tcpserver -DHRv -b20 -c40 -l0 -x./tcprules.cdb -U 0 smtp \
qmail-smtpd
'
all: tcprules.cdb
tcprules.cdb: tcprules.conf
tcprules $@ $<.tmp < $<
127.0.0.1:allow,RELAYCLIENT=""
:allow
makeを実行して作成。
2097152
#! /bin/sh
exec setuidgid qmaill multilog t \
n10 s99999 ./main \
-'*' \
+'* status: *' \
=status
空ディレクトリを作成。ownerはqmaill。group/otherはアクセス不能に 設定。
空ファイルを作成。ownerはqmaill。group/otherはアクセス不能に設定。
${pure-ftpd prefix}/bin へのリンクにする。pure-ftpdを prefix=/var/pure-ftpd としてインストールしてもよい。
#! /bin/sh
exec < /dev/null
exec 2>&1
exec env - PATH="/var/pureftpd/sbin:$PATH" \
tcpserver -DHRv -b20 -c40 -l0 -x./tcprules.cdb 0 ftp \
pure-ftpd -A -e -H -i
all: tcprules.cdb
tcprules.cdb: tcprules.conf
tcprules $@ $<.tmp < $<
以下では 192.168.1.* からのアクセスを許可、他は禁止。
192.168.1.:allow
:deny
makeを実行して作成。
#! /bin/sh
exec setuidgid ftplog multilog t \
n10 s99999 ./main \
-'*' \
+'* status: *' \
=status
空ディレクトリを作成。ownerはftplog。group/otherはアクセス不能に 設定。
空ファイルを作成。ownerはftplog。group/otherはアクセス不能に設定。
${openssh prefix}/sbin へのリンクにする。opensshを prefix=/var/openssh としてインストールしてもよい。
${openssh prefix}/etc へのリンクにする。opensshを prefix=/var/openssh としてインストールしてもよい。
#! /bin/sh exec < /dev/null exec 2>&1 exec env - PATH="/var/openssh/sbin:$PATH" \ sshd -De -f ./etc/sshd_config
#! /bin/sh
exec setuidgid sshlog multilog t \
n10 s99999 ./main
空ディレクトリを作成。ownerはsshlogなど。group/otherはアクセス不 能に設定。