2006年05月10日
Linuxの断末魔の叫び(kernel oops)をリモートホストで採取する方法
Linuxには、kernl oopsが出た場合に自動的にrebootできる仕組みがあります。
この機能を使えば、OSが不意に停止してしまった場合に自動的にrebootしてサービスを再開することができます。
ただ、kernelメッセージは障害原因特定のヒントになりうるので採取できた方が好ましいのですが、自動rebootの場合はコンソールなどに出力されているかもしれないkernelメッセージを採取することができません。
というわけで、今回はkernel 2.6で実装されたnetconsoleを使い、kernel oops発生時にそれを記録しつつ自動rebootする方法を紹介したいと思います。
まずは、kernel oopsが発生した場合、動作を継続するのではなくkernel panicになるようにするのと、kernel panicが発生したらrebootするように設定します。
この設定は/procファイルで行うので、/etc/sysctl.confに以下のように書きます。
書いたら反映して、設定後の値を確認します。
これで、kernel oopsが出たらpanicし、panicした10秒後にrebootするようになりました。
次にkernel oopsを採取するため、netconsoleの設定をします。
netconsoleとは次のような用途で実装されたものです。(linux/Documentation/networking/netconsole.txtより引用)
つまり、発信側(netconsole)の他に、受信するマシンも必要になります。
では発信側(netconsole)のマシンの設定からいきます。
まずはkernelをnetconsoleとsysrqを有効にしてビルドします。sysrqはnetconsoleを使うのに必須ではありませんが、後述の動作確認のためや、ログインすらできない場合でもkernelにsyncやrebootを発行でき、何かと有益ですので有効にします。
以下のように、netconsoleをモジュールで、sysrqを組み込みで有効にします。
さて、netconsoleを使う際には、受信側の情報を設定してあげる必要があります。設定できる箇所は次の2箇所です。
grubなどから指定するブートパラメータはnetconsoleを組み込みにした場合で、モジュールロード時指定はnetconsoleをモジュールにした場合です。
さて、netconsoleはNICのドライバロードと同時に初期化されるようなので、NICドライバをモジュール、netconsoleを組み込みにした場合、NICドライバの前にnetconsoleがロードされてしまうのでうまく使えないようです。
まとめるとこんな風になります。
今回はnetconsoleをモジュールにしたので、ネットワークインターフェースが有効になるタイミングで、設定を指定しつつnetconsoleモジュールをロードするようにします。
Debianの場合は、/etc/network/interfacesに次のように書きます。
10.10.2.4と00:0c:6e:56:b5:d6は、それぞれ受信側のIPアドレスとMACアドレスです。
RedHat系の場合は、(よく知らないのですが)/sbin/ifup-localでやるのがよさそうです。
前述の表中でも書きましたが、NICもnetconsoleも組み込みにした場合は、自分のIPアドレス(以下の例の場合は10.10.2.3)も指定する必要がある点に注意してください。
これで送信側の設定は終わりました。次に受信側です。
受信側は、送信側で指定したUDPのポート(6666)を受ければよいので、とりあえずテストするだけなら、netcatを使って nc -u -l -p 6666でよいのですが、今回はsyslog-ngを使って受信するようにしてみます。
以下、syslog-ng.confから該当箇所の抜粋です。
設定したら、syslong-ngを再起動して変更反映します。
これで送信側、受信側ともに準備ができました。
では、kernelメッセージを出してみましょう。
簡単なのは、送信側でsysrqを使う方法です。
これで、受信側の/var/log/netconsoleに
と記録されれば成功です。
これで目的は達せられたのですが、最後にいくつか落ち穂拾いを。
netconsoleの設定で、受信側のMACアドレスを指定しました。さて、このMACアドレスを省略した場合どうなるのでしょうか。
この場合、netconsole.txtにも書いてあるように、宛先MAXアドレスはff:ff:ff:ff:ff:ffになります。これはブロードキャストのときなどにも使われるのですが、同一サブネットの全ノードにパケットが送られてしまいますので、省略せずに受信側マシンのMACアドレスを設定すべきでしょう。
次にちょっとイタズラして、受信アドレスを指定でIPアドレスはホストA、MACアドレスはホストBというように食い違う設定にした場合どうなるか試してみました。L2, L3 の知識のある方なら想像通りの結果だと思うのですが、このパケットは(スイッチングハブの場合)ホストBだけに届くのですが、このパケットの宛先IPアドレスはホストAのものなので、通常、ホストBはこのパケットを廃棄します。
というわけで、今回はnetconsoleによりkernelの断末魔の叫びを他のホストで記録する方法を紹介しました。こういった方法でログの採取を仕込んでおけば、突然のkernel panicの際の原因究明に有益ではないかと思います。(ひ)
この機能を使えば、OSが不意に停止してしまった場合に自動的にrebootしてサービスを再開することができます。
ただ、kernelメッセージは障害原因特定のヒントになりうるので採取できた方が好ましいのですが、自動rebootの場合はコンソールなどに出力されているかもしれないkernelメッセージを採取することができません。
というわけで、今回はkernel 2.6で実装されたnetconsoleを使い、kernel oops発生時にそれを記録しつつ自動rebootする方法を紹介したいと思います。
まずは、kernel oopsが発生した場合、動作を継続するのではなくkernel panicになるようにするのと、kernel panicが発生したらrebootするように設定します。
この設定は/procファイルで行うので、/etc/sysctl.confに以下のように書きます。
kernel.panic_on_oops = 1
kernel.panic = 10
書いたら反映して、設定後の値を確認します。
# sysctl -p
kernel.panic = 10
kernel.panic_on_oops = 1
# cat /proc/sys/kernel/panic_on_oops
1
# cat /proc/sys/kernel/panic
10
これで、kernel oopsが出たらpanicし、panicした10秒後にrebootするようになりました。
次にkernel oopsを採取するため、netconsoleの設定をします。
netconsoleとは次のような用途で実装されたものです。(linux/Documentation/networking/netconsole.txtより引用)
このモジュールは、ディスクへの記録に失敗し、しかもシリアルコンソールが役に立たないような問題をデバッグできるように、UDP 経由でカーネル printk メッセージを記録します。
つまり、発信側(netconsole)の他に、受信するマシンも必要になります。
では発信側(netconsole)のマシンの設定からいきます。
まずはkernelをnetconsoleとsysrqを有効にしてビルドします。sysrqはnetconsoleを使うのに必須ではありませんが、後述の動作確認のためや、ログインすらできない場合でもkernelにsyncやrebootを発行でき、何かと有益ですので有効にします。
以下のように、netconsoleをモジュールで、sysrqを組み込みで有効にします。
Device Drivers --->
Network device support --->
Network console logging support (EXPERIMENTAL)
Kernel hacking --->
[*] Magic SysRq key
さて、netconsoleを使う際には、受信側の情報を設定してあげる必要があります。設定できる箇所は次の2箇所です。
- ブートパラメータ
- モジュールロードの際のパラメータ指定
grubなどから指定するブートパラメータはnetconsoleを組み込みにした場合で、モジュールロード時指定はnetconsoleをモジュールにした場合です。
さて、netconsoleはNICのドライバロードと同時に初期化されるようなので、NICドライバをモジュール、netconsoleを組み込みにした場合、NICドライバの前にnetconsoleがロードされてしまうのでうまく使えないようです。
まとめるとこんな風になります。
NICドライバ | netconsole | netconsole設定箇所 | 備考 |
---|---|---|---|
組み込み | 組み込み | ブートパラメータ | netconsoleの設定で、デバイスだけでなく自分のIPアドレスも指定する必要がある。ブート時のkernelメッセージも採取できる。 |
組み込み | モジュール | ロードモジュールのパラメータ | |
モジュール | 組み込み | × | |
モジュール | モジュール | ロードモジュールのパラメータ |
今回はnetconsoleをモジュールにしたので、ネットワークインターフェースが有効になるタイミングで、設定を指定しつつnetconsoleモジュールをロードするようにします。
Debianの場合は、/etc/network/interfacesに次のように書きます。
auth eth0
iface eth0 inet dhcp
post-up modprobe netconsole netconsole=6665@/eth0,6666@10.10.2.4/00:0c:6e:56:b5:d6
pre-down rmmod netconsole
10.10.2.4と00:0c:6e:56:b5:d6は、それぞれ受信側のIPアドレスとMACアドレスです。
RedHat系の場合は、(よく知らないのですが)/sbin/ifup-localでやるのがよさそうです。
#!/bin/sh
DEVICE=$1
case $DEVICE in
eth0)
modprobe netconsole netconsole=6665@/eth0,6666@10.10.2.4/00:0c:6e:56:b5:d6
;;
esac
前述の表中でも書きましたが、NICもnetconsoleも組み込みにした場合は、自分のIPアドレス(以下の例の場合は10.10.2.3)も指定する必要がある点に注意してください。
modprobe netconsole netconsole=6665@10.10.2.3/eth0,6666@10.10.2.4/00:0c:6e:56:b5:d6
これで送信側の設定は終わりました。次に受信側です。
受信側は、送信側で指定したUDPのポート(6666)を受ければよいので、とりあえずテストするだけなら、netcatを使って nc -u -l -p 6666でよいのですが、今回はsyslog-ngを使って受信するようにしてみます。
以下、syslog-ng.confから該当箇所の抜粋です。
source s_netconsole {
udp(ip(0.0.0.0) port(6666));
};
destination d_netconsole {
file("/var/log/netconsole");
};
log {
source(s_netconsole);
destination(d_netconsole);
flags(final);
};
設定したら、syslong-ngを再起動して変更反映します。
これで送信側、受信側ともに準備ができました。
では、kernelメッセージを出してみましょう。
簡単なのは、送信側でsysrqを使う方法です。
# echo s > /proc/sysrq-trigger
これで、受信側の/var/log/netconsoleに
May 10 16:51:57 SENDER SysRq :
May 10 16:51:57 SENDER Emergency Sync
と記録されれば成功です。
これで目的は達せられたのですが、最後にいくつか落ち穂拾いを。
netconsoleの設定で、受信側のMACアドレスを指定しました。さて、このMACアドレスを省略した場合どうなるのでしょうか。
この場合、netconsole.txtにも書いてあるように、宛先MAXアドレスはff:ff:ff:ff:ff:ffになります。これはブロードキャストのときなどにも使われるのですが、同一サブネットの全ノードにパケットが送られてしまいますので、省略せずに受信側マシンのMACアドレスを設定すべきでしょう。
次にちょっとイタズラして、受信アドレスを指定でIPアドレスはホストA、MACアドレスはホストBというように食い違う設定にした場合どうなるか試してみました。L2, L3 の知識のある方なら想像通りの結果だと思うのですが、このパケットは(スイッチングハブの場合)ホストBだけに届くのですが、このパケットの宛先IPアドレスはホストAのものなので、通常、ホストBはこのパケットを廃棄します。
というわけで、今回はnetconsoleによりkernelの断末魔の叫びを他のホストで記録する方法を紹介しました。こういった方法でログの採取を仕込んでおけば、突然のkernel panicの際の原因究明に有益ではないかと思います。(ひ)
klab_gijutsu2 at 18:46│Comments(2)│TrackBack(1)
トラックバックURL
この記事へのトラックバック
1. まとまりのない日記でごめんなさい [ 馬写真日記 ] 2008年03月22日 01:10
Linux kernel が Oops を吐くところを見てしまいました。NTFS フォーマットの USB 外付ハードディスクに対して ls を実行した時に発生したエラーです。環境は、Vine Linux 4.1 に PCI ボード経由で USB 2.0 接続。こんな感じ:
この記事へのコメント
1. Posted by hyoshiok 2006年05月13日 15:38
はじめまして。通りすがりのものです。大変興味深く拝見しました。
やはり、この手の場合は、crash dumpを設定するのが王道かと思いますが、いかがお考えでしょう?
やはり、この手の場合は、crash dumpを設定するのが王道かと思いますが、いかがお考えでしょう?
2. Posted by DSASの中の人 2006年05月13日 21:12
まさしく。実はこの話のネタとなったマシンは既に稼働中でして、lkcdをはじめとする多くのクラッシュダンプ採取ツールは専用パーティションが必要なため導入しづらい、という背景があったのです。なので、とりあえずnetconsoleで情報を採取してみて、それで解決できなければnetdumpかlkcdを導入しようかなぁと思ってます。