デーモンの起動・終了にはserviceコマンドを利用しよう

斎藤です。こんにちは。

今日は、デーモンの起動・終了に際してはserviceコマンドを使った方がいいよ、というお話です。

※CentOS 6.4 (x86_64) でテストしています。尚、特記がある所を除きます。

デーモンの起動・終了には大きく2手

Linux、特にRedHat, CentOS, Fedora, Ubuntu[1]などでは、デーモンの起動・終了の制御の際に、大きく分けて次の2つの方法を用いる事ができます。

  • "/etc/init.d/"配下のスクリプト(以下、スクリプト)を直接実行する
  • "service"コマンドを実行する

一見、同じ事をやっているように思えるのですが、実は違う部分があります。それは、起動スクリプトが扱う環境変数が違うのです。次の節で、検証してみます。

扱う環境変数が違う

スクリプトを直接実行する場合は、実行時の際の環境変数をそのまま利用します。一方、serviceコマンドを利用すると、LANGとTERM変数以外の環境変数は引き継がれないとあります[2]。これが、本当かどうか検証してみます。

まず、環境変数一覧をファイルへ出力するだけのbashスクリプトを作成し、"/etc/init.d/"ディレクトリに保存します。コードは次の通りです。

$ cat /etc/init.d/envtest 
#!/bin/bash
### BEGIN INIT INFO
# Provides:          envtest
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6

env > /tmp/$(date +%Y%m%d%H%M%S).log

続いて、スクリプトを直接実行した時の結果です。

$ sudo /etc/init.d/envtest start
:
HOSTNAME=saito-hb-vm101
SHELL=/bin/bash
TERM=xterm-256color
HISTSIZE=1000
QTDIR=/usr/lib64/qt-3.3
USER=root
LS_COLORS=(長いので省略)
SUDO_USER=saito
SUDO_UID=500
USERNAME=root
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAIL=/var/spool/mail/saito
PWD=/tmp
LANG=ja_JP.UTF-8
SHLVL=1
SUDO_COMMAND=/etc/init.d/envtest start
HOME=/root
LOGNAME=root
SUDO_GID=501
_=/bin/env

さらに、serviceコマンドから実行した場合の結果です。

$ sudo service envtest start
:
TERM=xterm-256color
PATH=/sbin:/usr/sbin:/bin:/usr/bin
PWD=/
SHLVL=1
_=/bin/env

行数がだいぶ違いますね。内容も、diffを用いて比較してみます。前がスクリプトを直接実行した時の結果、後がserviceコマンドを実行した時の結果です。

@@ -1,20 +1,5 @@
-HOSTNAME=saito-hb-vm101
-SHELL=/bin/bash
 TERM=xterm-256color
-HISTSIZE=1000
-QTDIR=/usr/lib64/qt-3.3
-USER=root
-LS_COLORS=(長いので省略)
-SUDO_USER=saito
-SUDO_UID=500
-USERNAME=root
-PATH=/sbin:/bin:/usr/sbin:/usr/bin
-MAIL=/var/spool/mail/saito
-PWD=/tmp
-LANG=ja_JP.UTF-8
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+PWD=/
 SHLVL=1
-SUDO_COMMAND=/etc/init.d/envtest start
-HOME=/root
-LOGNAME=root
-SUDO_GID=501
 _=/bin/env

以上より、serviceコマンドを実行した場合、扱う環境変数は一部を除き実行しているユーザに依存しない事がわかります。

しかし、manの内容と違う部分があります。PATH, LANGは引き継ぐはずなのですが、引き継がれていません。どうしたものでしょうか。原因を調べるため、テストした環境の"/sbin/service"コマンドの中を確認してみました。その結果、次の2点がわかりました。

  • PATH変数が上書きされている。2行目(最初の段階) で"/etc/init.d/functions"が呼び出され、この中で行われている。
  • 38, 47, 48, 62行目(※1)でPATH, TERM変数のみ引き継ぐようにしている。"env -i"を用いて環境変数をクリアした後、PATH, TERM変数のみ再定義し、スクリプトを実行している。

以上より、CentOS 6.4ではmanの記述とは違い、serviceコマンドを用いるとTERM変数のみが引き継がれており、LANG変数は引き継がれていない事がわかりました。

Ubuntuではどうか?

念のため、他のOSも調べてみました。Ubuntu 12.04 LTS (Desktop)はどうでしょうか。先ほどのテストで用いた"/etc/init.d/envtest"スクリプトを基に、実行結果をdiffを用いて比較してみます。前がスクリプトを直接実行した時の結果、後がserviceコマンドを実行した時の結果です。

@@ -1,20 +1,6 @@
-SHELL=/bin/bash
 TERM=xterm
-USER=root
-LS_COLORS=(長いので省略)
-SUDO_USER=saito
-SUDO_UID=1000
-USERNAME=root
-MAIL=/var/mail/root
 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
-PWD=/home/saito
+PWD=/
 LANG=ja_JP.UTF-8
 SHLVL=1
-SUDO_COMMAND=/etc/init.d/envtest start
-HOME=/home/saito
-LOGNAME=root
-SUDO_GID=1000
-DISPLAY=:0
-XAUTHORITY=/home/saito/.Xauthority
-COLORTERM=gnome-terminal
 _=/usr/bin/env

PATH, TERM, LANG変数が引き継がれています。あわせて、"/usr/sbin/service"コマンドの中も確認してみます。CentOS 6.4との違いが2点ありました。

  • PATH変数は上書きしていない。
  • 82, 102, 103, 139 行目で、PATH, TERM, LANG変数を引き継いでいる。"env -i"を用いて環境変数をクリアした後、これら3つの変数のみ再定義し、スクリプトを実行している。

以上より、Ubuntu 12.04 LTSでは、manに記述されている通りTERM, LANG変数、更にmanに記述が無いPATH変数が引き継がれる事がわかりました。

OS起動時と動きが違う...なんて事が無いように

先の2つのOSの動作確認結果から、スクリプトを直接実行すると、プログラム実行に影響しかねない部分に影響が出ている事がわかります。例えば、CentOS 6.4なら、PWD(カレントディレクトリ)やLANG変数が違います。

もし、スクリプト内部やスクリプトが実行するプログラムでこれらの変数の影響を受ける部分があると、どうなるでしょうか。OS起動時はうまく動くのに、手動でデーモンを再起動をしようとすると失敗する、等の問題が出かねません。また、LANG変数は設定によってメッセージ・エラーログの出力言語が変わることがあり、トラブル発生の際の解読時やプログラムからのパース時に問題が出るかもしれません。要注意です。

なお、今回のdiffには含まれていませんが、例えばRubyが扱う環境変数が変わってしまうと、Rubyのプログラムの動きが変わってしまうことがあります。ここも、注意する必要があります。

まとめ

以上より、次の3点の事がわかりました。

  • serviceコマンドを用いると、実行ユーザの環境変数の設定状況に影響を受けにくくなる。
  • OSによっては、環境変数の引き継ぎ方がmanの記述とは違う動きをする。
  • 直接スクリプトを実行すると、実行ユーザ毎に設定した環境変数によって動きが変わりかねないのでできるだけ避ける。

もし、普段の運用時の操作、及び手順等で、スクリプトを直接実行する方法で操作されている方は、serviceコマンドを利用するよう切り替えて行くと良いでしょう。トラブルの種が少しでも減らせるはずです。

それでは、ごきげんよう。

※2013/06/27追記: serviceのデーモン名を補完する方法について補足の記事を書きました。あわせてご覧ください。『bash-completionでserviceコマンドなどの補完を強化しよう

※1 47, 48行目が連続なのは、--full-restart(stop→startする)オプションの処理が書かれている箇所のためです。

参考文献