- #1: OSとサービス、daemon、Linuxのinitソフトウェア
- #2: SysVInit、ハンズオン、SysVInitの問題(本記事)
- #3: Systemd、ハンズオン、Dockerでの問題と対応
SysVInit/Upstartの詳細
SysVInitとUpstartは比較的近いので、まとめて説明します。
/etc/inittab
に記述されているランレベルに従って起動を進めるシンプルな設計です。以下は典型的なランレベルの一例ですが、LinuxのディストリビューションやOSの種類によって異なりますのでご注意ください(特にランレベル2〜5)。
※Upstartの場合、runlevelによる設定はSysVInitとの互換のため実装されているという扱いのようです
ID | 名称 | 説明 |
---|---|---|
0 | 停止 | システムの停止、またはシャットダウン |
1 | シングルユーザモード | ネットワーク機能(デーモン)を起動しない |
2 | (マルチユーザーモード) | (ネットワークを起動しない) |
3 | (ネットワーク有のマルチユーザーモード) | (通常のモード) |
5 | (ネットワーク有のマルチユーザーモード、GUIを起動する) | (デスクトップ環境などで利用) |
6 | リブート | システムのリブート |
設定ファイルは以下を使います。サービスを起動するには/etc/init.d/サービス名 start
を実行します。実体はただのシェルスクリプトです。
/etc/inittab
- ランレベル定義ファイル
/etc/init.d/
- サービススクリプト群がこの下に置かれる
/etc/rcN.d
- ランレベルごとに起動するスクリプト群(
N
はランレベルを表す数値)
上の/etc/init.d/
以下のファイルに対するsymlinkになっている
起動/停止は頭文字がS
かどうかで判断する
/etc/init.d/
以下の様子伝統のSysVInitは当該ランレベルの/etc/rcN.d/S*
を順番に起動する仕様のため、起動の優先順位があるサービスを使う場合のために、慣例的にS10hoged
やS99piyod
などの数値2桁の名前が付けられてきました。
また、SysVInitの起動スクリプトは直列に一つずつ実行されるため、例えば、mysqldが起動した後にunicornを起動して欲しい場合には、mysqldをS90mysqld
、unicornをS99unicorn
などとすることで、mysqlの後にunicornが起動するよう保証することも行われていました。
SysVInit系の管理コマンドはSysVInit共通というものがなく、Linuxディストリビューションごとに実装が異なっています。
chkconfig
: RedHat系update-rc.d
: Debian系
chkconfig
もupdate-rc.d
も、/etc/init.d/
及び/etc/rcN.d
以下にあるファイル名を変更するだけのスクリプトなので、手動で変更するのと変わりません。
ハンズオン: SysVInit/Upstartを実際に見てみる
以下はCentOS 6.10で確認しました。VirtualBoxなどで自分で動かして試してみましょう。
sbin/init
まずはsbin/init
がどのinitかを確認してみましょう。
/sbin/init --version
/etc/inittab
続いて/etc/inittab
の中身です。表示してみれば1画面で収まる程度です。
cat /etc/inittab
画面の下の方にランレベルが記載されています。この場合通常のランレベルはここにある3
に該当します。なお、/etc/inittab
は編集に失敗すると起動しなくなることもあるのでご注意ください。その場合は起動時にブートローダーでランレベルを直接指定するなどの方法で回避することになります。
ついでですが、rootユーザーが/sbin/init
に以下↓のようにランレベルを与えて実行するとその場でランレベルを変更できます。0
を与えればシャットダウンと同じ動作です(実際にはまず使いませんが)。ここからわかるように、/sbin/init
の動作は意外に素朴です。
/sbin/init 0
/etc/init.d/
続いて/etc/init.d/
の下を見てみましょう。
ここに置かれているのは個別のdaemonの起動や終了を制御するスクリプトで、中身は単なるシェルスクリプトです。その中からsshdのスクリプトを見てみましょう。
~£ cat /etc/init.d/sshd
#!/bin/bash
#
# sshd Start up the OpenSSH server daemon
【中略】
start()
{
[ -x $SSHD ] || exit 5
[ -f /etc/ssh/sshd_config ] || exit 6
# Create keys if necessary
if [ "x${AUTOCREATE_SERVER_KEYS}" != xNO ]; then
do_rsa_keygen
if [ "x${AUTOCREATE_SERVER_KEYS}" != xRSAONLY ]; then
do_rsa1_keygen
do_dsa_keygen
fi
fi
echo -n $"Starting $prog: "
$SSHD $OPTIONS && success || failure
RETVAL=$?
[ $RETVAL -eq 0 ] && touch $lockfile
echo
return $RETVAL
}
stop()
{
echo -n $"Stopping $prog: "
killproc -p $PID_FILE $SSHD
RETVAL=$?
# if we are in halt or reboot runlevel kill all running sessions
# so the TCP connections are closed cleanly
if [ "x$runlevel" = x0 -o "x$runlevel" = x6 ] ; then
trap '' TERM
killall $prog 2>/dev/null
trap TERM
fi
[ $RETVAL -eq 0 ] && rm -f $lockfile
echo
}
reload()
{
echo -n $"Reloading $prog: "
killproc -p $PID_FILE $SSHD -HUP
RETVAL=$?
echo
}
【中略】
case "$1" in
start)
rh_status_q && exit 0
start
;;
stop)
if ! rh_status_q; then
rm -f $lockfile
exit 0
fi
stop
;;
restart)
restart
;;
reload)
rh_status_q || exit 7
reload
;;
【中略】
*)
echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}"
RETVAL=2
esac
exit $RETVAL
~£
sshスクリプトの中にはstart
やstop
やreload
などがシェルスクリプトとして定義されています。実は他のスクリプトも、start
やstop
以外は割と自由に書かれていたりします。
/etc/rc.d/
次は/etc/rc.d/
を見てみましょう。
その中から、rc3.d/
ディレクトリの下を見てみます。
rc3.d/
ディレクトリの下には、K
と数字で始まるスクリプトと、S
と数字で始まるスクリプトがあります。
Sxx
: 起動するスクリプトKxx
: 起動しないスクリプト(実際にはなくても同じだが、変更時renameだけすれば良いように配置されている)
そして、スクリプトはxx
の数字の順(注↓)に起動されます。非常に単純なつくりであることがわかります。つまりこの数値を変更すれば起動の順序を変えられるわけです。
「数字の順」は、実際にはls /etc/rcN.d/S*
した順と思って良いです。そのため、もし同じ数字のスクリプトがある場合にはアルファベット順になります。
chkconfig
CentOSのSysVInitにはchkconfig
というコマンドがあります。chkconfig
だけを実行すると、rc*.d/
のランレベル(ここでは1から6)ごとにサービスのオンオフ状態が表示されます。
たとえばchkconfig --del iptables
と実行するとrc*.d/
の下にあるiptablesスクリプトへのsymlinkが削除されます(スクリプトそのものはこのコマンドでは削除しません)。chkconfig --add iptables
とすればsymlinkが作成されます。
chkconfig
コマンドで設定及び閲覧できる情報は、あくまで起動時(ランレベル変更時)の設定です。
chkconfig
しただけでは当該サービスは起動しませんので、Linuxを再起動せずに当該サービスを起動したい場合には別途service hoged start
する必要があることに注意しましょう。
以上のようにSysVInitは驚くほどシンプルなつくりです。そして今でも必要になることが多々ありますので、やはり知っておくのがよいでしょう。
たとえば自分で何かサービス(daemon)を追加するなら、/etc/init.d/
の下にある他の起動スクリプトを真似て、start
/stop
/restart
オプションぐらいは取れるように書いておけばだいたい間に合うでしょう。
SysVInit系の問題
SysVInitは上から順に1つずつ直列に起動する、素朴とも言えるシンプルなつくりなので、比較的わかりやすい代わりに非効率です。具体的には、起動するスクリプト群の中に起動に時間のかかるものがあると、それに引きずられてシステム全体の起動時間が長くなってしまいます。
たとえばsshdはたいてい起動順の後ろの方にあるので、Linuxシステムを再起動するとsshdが起動するまでなかなかログインできないなんてこともあります。「LANケーブルが挿さっていないとIPアドレスが取れないので起動が終わらない」などというトラブルもだいたいSysVInitです(手前の
dhclient
(DHCPでIPを取得する)がタイムアウトするまで次の処理に進まないあるある)
Upstartはイベント実行をサポートすることでその部分が改良されていて、指定のサービスを並列起動できます。
SysVInitの/etc/init.d/
の下に記載するのは単なるシェルスクリプトなので、サービスごとにstartスクリプトやstopスクリプトを書くのに手間がかかります(ある程度の雛形はありますが)。依存関係を指定して起動するしくみもサポートされないので、先ほどのS*
ファイルの数字を自力で変更して起動順を制御するなどしなければなりません。
実際にはRedHat系のchkconfig
には/etc/init.d
のスクリプトに以下の形式の「コメント」を書くことで、起動順序の数字についてはそれを見てくれます。コメントである点にご注意。
# chkconfig: - 起動の番号 終了の番号
参考: Writing System V init scripts for Red Hat Linux
参考: IT技術の処方箋:起動スクリプトの順番
SysVInitやUpstartには「モニタリング機能がない」のも問題です。このため、monit
やsupervisor
などを用いて別途監視体制を構築しないと、エラーで止まったdaemonは自動起動されません。
(次回「#3: Systemd、ハンズオン、Dockerでの問題と対応」に続く)
主要ディストリビューションでは
service
コマンドが提供されているので、通常は直接/etc/init.d
以下のスクリプトを実行するのではなくservice
コマンドを利用するのが良いでしょう。参考: デーモンの起動・終了にはserviceコマンドを利用しよう