daemontools HOW-TO (α版)
Copyright 2000 滝澤 隆史 <taki@cyber.email.ne.jp>
Last modified: Sun Nov 19 19:29:10 2000
この文書はDJB氏のdaemontoolsパッケージに興味を持たれる方やこれから導入・運用しようとするかたに向けて書かれたものです。daemontoolsパッケージの概要、導入・設定方法、使用例などをまとめています。しかし、各ツールを詳細に説明するものではありません。そのため、この文書を読んだ後に、マニュアルを読んでください。日本語訳もあります。 また、新山さんの daemontools FAQもありますのでそちらもご覧ください。
1.1. daemontools とは
Section titled “1.1. daemontools とは”daemontoolsはサービスを安全・確実かつ容易に管理するためのツール集です。 主にサービスの制御、ログの収集、環境変数・資源制限を行います。
1.2. パッケージの内容
Section titled “1.2. パッケージの内容”daemontoolsパッケージに含まれているプログラムには次のものがあります。
| プログラム名 | 説明 |
|---|---|
supervise | サービスを開始させ、監視します。何らかのトラブルでサービスが停止したら、自動的に再起動させます。 |
svc | supervise により監視されているサービスを制御します。 |
svok | supervise が起動しているかを調べます。 |
svstat | supervise により監視されているサービスの状態を出力します。 |
svscan | サービスの集まりを開始させ、監視します。 |
fghack | 自信をバックグランドに移すサービスがバックグランドに移るのを防ぐツールです。 |
multilog | 標準入力から一続きの行を読み、任意の数のログに選択された行を追加します。 |
tai64n | 各行に TAI64N 形式の正確なタイムスタンプを付けます。 |
tai64nlocal | TAI64N 形式のタイムスタンプを人が読める形式に変換します。 |
setuidgid | 指定されたアカウントの uid と gid で別のプログラムを起動します。 |
envuidgid | 指定されたアカウントの uid と gid を示す環境変数を設定して別のプログラムを起動させます。 |
envdir | 指定したディレクトリにあるファイルによって修正された環境を設定して別のプログラムを起動させます。 |
softlimit | 新しい資源制限を伴って別のプログラムを起動させます。 |
setlock | ファイルをロックして別のプログラムを起動させます。 |
1.3. 既存の他のプログラムとの違い
Section titled “1.3. 既存の他のプログラムとの違い”既存の他のプログラムとに違いは次のとおりです。
- 自身が常駐するサービスの場合、何らかのトラブルが生じてサービスが死んだとき、通常は死んだまま。しかし、daemontoolsの
superviseを使うと、サービスが突然死んでも、自動的に起動し直してくれる。 - サービスの中にはそのプロセスIDをservice
.pidのようなファイルに格納するものもあるが、そうでないものもある。一方、daemontoolsではサービスのプロセスIDをsuperviseが一括して管理している。そのため、その制御はsvcを使って簡単にできる。 - SVR4形式のinitスクリプトでも起動/停止/再起動の指令は出せるが、daemontoolsの
svcではさらに多くの種類のシグナルを送ることができる。 syslogdは負荷が高くなるとデータの取りこぼしがある。daemontoolsのmultilogでは取りこぼしは一切ない。また、ログのサイズの制限/循環、パターンマッチによりログの取捨選択ができるので効率のよいログの管理ができる。syslogdのタイムスタンプは一般に秒単位である。一方、multilogではTAI64N形式のタイムスタンプで管理していて、その精度はナノ秒(ただし、現状のマシンクロックの制限により実質マイクロ秒の精度)。
2. インストール
Section titled “2. インストール”2.1. daemontools のインストール
Section titled “2.1. daemontools のインストール”次のサイトからdaemontoolsのパッケージdaemontools-0.70.tar.gzを入手します。
ファイルを展開し、そのディレクトリに移動します。
$ gzip -dc daemontools-0.70.tar.gz | tar xvf -$ cd daemontools-0.70コンパイルして、インストールします。
$ make# make setup check試験します。何も出力しなかったら正常です。
$ ./rts > rts.out$ cmp rts.out rts.expタイムスタンプを確認します。各行の前の日時と後ろの日時は同じになります。ただし、端数の関係で1秒違うかもしれません。
$ date | ./tai64n | ./tai64nlocal2000-05-05 11:16:53.959932500 Fri May 5 11:16:53 JST 2000$ date | sh -c './multilog t e 2\>&1' | ./tai64nlocal2000-05-05 11:17:11.739003500 Fri May 5 11:17:11 JST 2000以上、何も問題が生じなければ、インストールは終了です。
2.2. svscan の起動
Section titled “2.2. svscan の起動”svscan が監視するディレクトリを作成します。svscan に関する説明は次章で行います。
# mkdir /service# chmod 755 /serviceブートスクリプトに svscan を起動するコマンドを登録します。BSD形式の起動スクリプトであれば、rc.local などに、次のコマンドを追加してください。PATHは必要なものを設定してください。
env - PATH=/usr/local/bin:/usr/bin:/bin csh -cf 'svscan /service &'SVR4形式の起動スクリプトであれば、次のようなスクリプト svscan を作成し、登録してください。OSにより少々修正する必要があります。
#!/bin/shPATH=/usr/local/bin:/usr/bin:/bin
case "$1" in start) echo -n "Starting svscan: " exec env - PATH="$PATH" \ csh -cf 'svscan /service &; echo $! > /var/run/svscan.pid' touch /var/lock/subsys/svscan ;; stop) if [ -f /var/run/svscan.pid ]; then echo -n "Stopping svscan: " kill `cat /var/run/svscan.pid` svc -dx /service/* svc -dx /service/*/log rm -f /var/run/svscan.pid rm -f /var/lock/subsys/svscan fi ;; *) echo "Usage: $0 {start|stop}" exit 1esac
exit 0なお、RedHat Linux 7.x用の起動スクリプトは svscan にあります。次のようにして登録してください。
# cd /etc/rc.d/init.d# cp /tmp/svscan .# chmod +x svscan# chkconfig --add svscan起動スクリプトの登録ができたら、起動スクリプトを個別に実行したり、再起動したりして、svscan を起動してください。
3. サービスの制御
Section titled “3. サービスの制御”3.1. svscan と supervise の動作
Section titled “3.1. svscan と supervise の動作”svscanは監視対象のディレクトリ/serviceにサブディレクトリsubがあるとき、そのディレクトリ名を引数にしてsuperviseを起動させます。superviseは引数で渡されたディレクトリsubに移動し、./runスクリプトを起動させ、監視します。この./runにはサービスを実行するスクリプトを記述します。さらに、subにsticky bitが立っていれば、svscanはsubに移動し、logを引数にしてsuperviseを起動させ、sub/runの出力とsub/log/runの入力をパイプでつなぎます。このときのsuperviseは引数で渡されたディレクトリlogに移動し、./runスクリプトを起動させ、監視します。この./runにはログを記録するプログラム(multilog)を実行するスクリプトを記述します。このように superviseはsvscanにより起動させられるので、スクリプト中にsuperviseを明示して記述する必要はありません。
それぞれのプログラムとその引数および作業するディレクトリを整理すると次の表のようになります。
| プログラム | 作業ディレクトリ |
|---|---|
svscan /service | /service |
supervisesub | /service/sub |
supervise log | /service/sub/log |
上記で述べたことに加えて、svscan には次のような特徴があります。
- 起動したディレクトリを監視する。引数(ディレクトリ名)が与えられたら、起動後にそのディレクトリに移動し、そのディレクトリを監視する。
- 5秒毎にサブディレクトリを調べる。
- 新しいサブディレクトリがあれば、
superviseプロセスを開始させる。 superviseプロセスが終了しているサブディレクトリを見つけたら、そのsuperviseプロセスを再開させる。- ドットで始まるサブディレクトリは無視する。
- 新しいサブディレクトリがあれば、
上記で述べたことに加えて、supervise には次のような特徴があります。
./runが終了したら、./runを再起動させる。ループを防ぐため、再起動まで1秒間待つ。superviseの起動時に./downが存在すれば、./runを起動させない。supervise自身は終了の指示がない限り終了しない。
3.2. 各サービスの起動
Section titled “3.2. 各サービスの起動”a) ログを取らない場合
Section titled “a) ログを取らない場合”起動させたいサービスのためのディレクトリ/path/to/fooを適当な場所に作成します。
# mkdir /path/to/foo次に、./run を作成し、サービスを実行するスクリプトを記述します。
/service/sub から /path/to/foo へのシンボリックリンクを作ります。
# ln -s /path/to/foo /service/sub5秒以内に supervise が起動するでしょう。svok を使って起動が成功しているか確認できます。また、svstat でもその起動の状態を確認できます。
# svok /service/sub; echo "$?"0# svstat /service/sub/service/sub: up (pid 1234) 20 secondsb) ログを取る場合
Section titled “b) ログを取る場合”起動させたいサービスのためのディレクトリ/path/to/fooを適当な場所に作成します。ログ用のディレクトリ /path/to/foo/log も作成します。さらに foo に対してsticky bitを立てます。ログを出力するUIDとGIDを変える場合は、logの所有者も変えます。
# mkdir /path/to/foo# mkdir /path/to/foo/log# chmod +t /path/to/foo# chown uid.gid /path/to/foo/log次に、./run を作成し、サービスを実行するスクリプトを記述します。また、log/run を作成し、ログを保存するスクリプトを記述します。
/service/sub から /path/to/foo へのシンボリックリンクを作ります。
# ln -s /path/to/foo /service/sub5秒以内に supervise が起動するでしょう。svok を使って起動が成功しているか確認できます。また、svstat でもその起動の状態を確認できます。
# svok /service/sub; echo "$?"0# svok /service/sub/log; echo "$?"0# svstat /service/sub /service/sub/log/service/sub: up (pid 1234) 20 seconds/service/sub/log: up (pid 1235) 20 seconds具体例(qmailの場合)
Section titled “具体例(qmailの場合)”まず、qmailの起動プログラム qmail-start 用のディレクトリを作成します。場所はどこでもかまいませんが、ここでは /var/qmail/supurvise/qmail-send とします。ログを保存するために、このディレクトリにsticky bitを立て、そのディレクトリの下にqmail-sendのログ保存用ユーザqmaill所有のディレクトリlogを作成します。
# mkdir /var/qmail/supervise/qmail-send# chmod +t /var/qmail/supervise/qmail-send# mkdir /var/qmail/supervise/qmail-send/log# chown qmaill.nofiles /var/qmail/supervise/qmail-send/log起動スクリプト ./run と log/run を作成します。このスクリプトの例は次節に述べます。
/service/qmail からのシンボリックリンクを作ります。
# ln -s /var/qmail/supervise/qmail-send /service/qmail5秒以内にsuperviseが起動するはずなので、svokあるいはsvstatで起動を確認してください。また、qmailのマニュアルに記述してある配送試験を行ってください。
次に、qmailのSMTPデーモンqmail-smtpd用のディレクトリを作成します。ここでは/var/qmail/supervise/qmail-smtpdとします。ログを保存するために、このディレクトリにsticky bitを立て、そのディレクトリの下にqmail-sendのログ保存用ユーザsmtplog所有のディレクトリlogを作成します。
# mkdir /var/qmail/supervise/qmail-smtpd# chmod +t /var/qmail/supervise/qmail-smtpd# mkdir /var/qmail/supervise/qmail-smtpd/log# chown smtplog.nofiles /var/qmail/supervise/qmail-smtpd/log起動スクリプト ./run と log/run を作成します。このスクリプトの例は次節に述べます。
/service/smtpd からのシンボリックリンクを作ります。
# ln -s /var/qmail/supervise/qmail-smtpd /service/smtpd5秒以内に supervise が起動するはずなので、svok あるいは svstat で起動を確認してください。
関連リンク
3.3. 起動スクリプト ./run の例
Section titled “3.3. 起動スクリプト ./run の例”ここでは、サービスの起動スクリプト./runの作成例を示します。ログの収集スクリプトlog/runに関してはここでは典型的な例しか示しません。応用例は次章に記述します。
./run を作成するに当たっての注意事項
Section titled “./run を作成するに当たっての注意事項”- シグナルをサービスに直接送るために、
execを使ってsh(./runのプロセス)を置き換える必要がある。 &を付けてバックグランドで走らせてはいけない。- 自身をforkしてバックグランドに移してしまうプログラムは
fghackを使うことができる。ただし、制御は行えない。 - サービスによっては
superviseからのシグナルでは制御できないものもある。
qmail-send
Section titled “qmail-send”qmailのメール配送のプログラムを起動させ、配送のログを取る場合の例を示します。ログはqmail-sendのログ保存専用のユーザqmaillにより保存されます。すべてのログはタイムスタンプを付けて./log/main/に保存されます。さらに現在の接続数は./log/statusに保存されます。
#!/bin/shexec env - PATH="/var/qmail/bin:$PATH" \qmail-start ./Maildir/./log/run
Section titled “./log/run”#!/bin/shexec \setuidgid qmaill \multilog t ./main '-*' '+* status: *' =statusqmail-smtpd
Section titled “qmail-smtpd”qmailのSMTPデーモンqmail-smtpdをtcpserverで起動させて接続制御を行い、接続状況のログを取る場合の例を示します。この場合はログ保存専用のユーザsmtplogによりログを保存します。また、接続制御ファイルtcp.cdbは同じディレクトリにあるものとします。
#!/bin/shexec env - PATH="/var/qmail/bin:$PATH" \tcpserver -vR -c40 -x./tcp.cdb -u7791 -g2108 0 smtp qmail-smtpd 2>&1./log/run
Section titled “./log/run”#!/bin/shexec \setuidgid smtplog \multilog t ./main '-*' '+* * status: *' =status3.4. サービスの制御
Section titled “3.4. サービスの制御”svc を使って次のことができます。
- サービスの起動・停止
- サービスへのシグナルの送信
superviseの終了
svc の使い方は次の通りです。
svc opts servicesoptsはgetopt形式のオプションです。複数のオプションを指定でき、前から順番に実行されます。servicesは制御対象のディレクトリ名です。複数のディレクトリを同時に指定できます。ここで、svcのオプションの一覧を示します。
| オプション | 意味 | 動作 |
|---|---|---|
-u | Up | サービスが起動していなければ、開始します。サービスが停止していれば、再開します。 |
-d | Down | サービスが起動していれば、TERM シグナルを送り、それから CONT シグナルを送ります。停止した後は再開しません。 |
-o | Once | サービスが起動していなければ、開始します。サービスが停止していれば、再開しません。 |
-p | Pause | サービスに STOP シグナルを送ります。 |
-c | Continue | サービスに CONT シグナルを送ります。 |
-h | Hangup | サービスに HUP シグナルを送ります。 |
-a | Alarm | サービスに ALRM シグナルを送ります。 |
-i | Interrupt | サービスに INT シグナルを送ります。 |
-t | Terminate | サービスに TERM シグナルを送ります。 |
-k | Kill | サービスに KILL シグナルを送ります。 |
-x | Exit | サービスがダウンしたらすぐに supervise は終了します。 |
シグナルの意味は次のとおりです(値は環境により異なることがあります)。詳細はsignal(7)を読んでください。
| シグナル | 値 | 意味 |
|---|---|---|
| SIGSTOP | 17 | プロセスの停止 |
| SIGCONT | 19 | 停止状態からの再開 |
| SIGHUP | 1 | 制御している端末のハングアップの検出。制御しているプロセスの死。 |
| SIGALRM | 14 | alarm(2)からのタイマーシグナル |
| SIGINT | 2 | キーボードからの割り込み |
| SIGTERM | 15 | 終了シグナル |
| SIGKILL | 9 | Killシグナル |
ここで、いくつかの使用例を示します。
qmail-sendの設定の変更を有効にするためのサービスの再起動
Section titled “qmail-sendの設定の変更を有効にするためのサービスの再起動”# svc -t /service/qmail./runの書き換え時の処理
Section titled “./runの書き換え時の処理”# mv ./run.new ./run; svc -t /service/smtpdサービスの一時的な停止および再開
Section titled “サービスの一時的な停止および再開”# svc -d /service/ftpd# svc -u /service/ftpdサービスの停止後、superviseの終了(ただし、5秒以内に superviseは再起動する)
Section titled “サービスの停止後、superviseの終了(ただし、5秒以内に superviseは再起動する)”# svc -dx /service/ftpd /service/ftpd/logサービスの停止後、superviseの終了(superviseを再開させない)
Section titled “サービスの停止後、superviseの終了(superviseを再開させない)”# mv /service/ftpd /service/.ftpd; svc -dx /service/.ftpd /service/.ftpd/logsvscanを終了させるとき(全てのサービスの停止後、superviseを終了し、svscanのプロセスを終了する)
Section titled “svscanを終了させるとき(全てのサービスの停止後、superviseを終了し、svscanのプロセスを終了する)”# svc -dx /service/*# svc -dx /service/*/log# kill pid_of_svscan4. ログの収集
Section titled “4. ログの収集”4.1. multilog
Section titled “4.1. multilog”前章で述べたログの収集スクリプトlog/runには収集プログラムとしてmultilogを使います。ここでは、そのmultilogの使い方について説明します。
multilog の使い方は次のとおりです。
multilog scriptscriptは動作(action)の集まりで、次の表に記述した動作の行の選択から出力までの組合わせを繰り返して指定できます。(恐らくシェルの限界まで)
| 動作の概要 | 動作 | 備考 |
|---|---|---|
| タイムスタンプ | t | 各行の先頭にTAI64N形式のタイムスタンプを付ける。(最初の動作として記述した場合のみ有効) |
| 行の選択 | -pattern | patternが行に合えばその行の選択が解除される。 |
| 〃 | +pattern | pattern が行に合えばその行は選択される。 |
| 自動切り替えの動作 | ssize | 最大ファイルサイズの設定。デフォルト99999。 |
| 〃 | nnum | ログファイルの最大数。デフォルト10。 |
| 〃 | !processor | プロセッサの設定 |
| ログ | dir | ログ dir へ選択された行を追加する。ドットやスラッシュで始まる必要がある。 |
| 警告 | e | 標準エラーに選択された各行(の最初の200バイト)を出力する。 |
| Statusファイル | =file | file の中身を選択された各行(の最初の1000バイト)で置き換える。 |
最初は各行が選択されています。行の選択指定はいくつでも記述でき、前から順番にそのパターンの選択・解除が追加されていきます。
patternの仕様は次のとおりです。
*とそれ以外の文字からなる文字列。*は直後に現れる文字を含まない任意の文字列に一致。- 最後にある
*は任意の文字列に一致。 - シェルのメタ文字を含めることができるが、引用符で囲む必要がある。
想定外の動作を防ぐために、pattern全体を引用符で囲んだほうが無難でしょう。
ここで、いくつか行の選択指定の例を示します。multilogの詳しい使用例に関しては次節をご覧ください。
+helloはhelloを選択します。hello worldは選択しません。
-'* * > *'は@400000003879ded713291cfc 4357 > -ERR authorization failedの選択を解除します。1つ目と2つ目の*はその直後のスペースを含まない任意の文字列に一致します。
'-*'はすべての行の選択を解除します。特定のパターンのみを選択する場合は通常、最初にこの動作を記述してすべての選択を解除してから、選択するパターンを追加していきます。
一続きの動作
Section titled “一続きの動作”-'*' +'* status: *' =statusは@400000003914053c22ab2904 status: local 0/10 remote 0/20を選択し、statusというファイルの内容を置き換えます。つまり、statusというファイルには常に最新のstatus:行が格納されることになります。
4.2. .log/run の作成例
Section titled “4.2. .log/run の作成例”qmail-send
Section titled “qmail-send”qmailのメール配送プログラムqmail-sendのログを取る場合の例を示します。ログはqmail-sendのログ保存専用のユーザqmaillにより保存されます。このスクリプトは次の3つの部分に分けることができます。
- タイムスタンプを付けてすべてのログを
./log/main/に保存する。 - 通常よく出てくるパターンに一致しないものは
./log/alert/に保存する。 - 現在の同時配送数を
./log/statusに保存する。
ちなみに、この例はDJB氏がメイリングリストに投稿した例を修正したものです。
#!/bin/shexec \setuidgid qmaill \multilog t ./main \-'* status: *' \-'* starting delivery *' \-'* delivery * success*' \-'* delivery * failure*' \-'* new msg *' \-'* info msg *' \-'* end msg *' \-'* bounce msg *' \-"* delivery * deferral: Sorry,_I_couldn't_find_any_host_by_that_name*" \-"* delivery * deferral: Sorry,_I_wasn't_able_to_establish_an_SMTP_*" \./alert \-'*' \+'* status: *' \=statusqmail-smtpd
Section titled “qmail-smtpd”qmailのSMTPデーモンqmail-smtpdを途中にrecordioを挟んでtcpserverで起動させて接続制御を行い、接続状況のログとコマンドの応答を取る場合の例を示します。この場合はログ保存専用のユーザsmtplogによりログを保存します。このスクリプトは次の3つの動作に分けることができます。
recordioによって記録されたもの以外のログをすべて./log/main/に保存する。recordioによって記録されたもの中でサーバへ送られる主要なコマンドとその応答を./log/tcp/に保存する。- 現在の接続数を
./log/statusに保存する。
ちなみにrecordioとは、プログラムの入出力を記録するプログラムで、ucspi-tcpパッケージに含まれています。これを用いれば、ログの出力を持たないサービスであっても、セッションのコマンドの応答から、不正中継の試みなどの様々な情報を記録できます。ただし、選択する行によってはプライバシの問題にもなるので扱いには注意が必要です。
#!/bin/shexec \setuidgid smtplog \multilog t \-'* * > *' \-'* * < *' \s200000 \./main \-'*' \+'* * < HELO *' \+'* * < EHLO *' \+'* * < MAIL *' \+'* * < RCPT *' \+'* * < DATA*' \+'* * < QUIT*' \+'* * < RSET*' \+'* * < NOOP*' \+'* * > 1*' \+'* * > 2*' \+'* * > 3*' \+'* * > 4*' \+'* * > 5*' \s200000 \./tcp \-'*' \+'* * status: *' \=statusThanks to M.Sugimoto.