■ はじめに
Red Hat 7 系 Linux における、カーネルクラッシュダンプの基本的なことや、取り方 (Kdump の設定方法) について書きました。"カーネルクラッシュダンプを取ったことがない人向け" の記事です。
この記事は、カーネルクラッシュダンプの基本的な説明、カーネルクラッシュダンプを取るのに必要なツール類の説明、システムクラッシュの方法、カーネルパニックに関するパラメータについての紹介、という構成になっています。
追記: 解析編を書きました ➔ カーネルクラッシュダンプの解析方法
■ カーネルクラッシュダンプとは
システム (カーネル) クラッシュ時点でのメモリ内容が保存 (ダンプ) されたファイルのことです。
通常、カーネルパニックなどにより、システムがクラッシュした場合、ログなどの情報は残らず原因解析が困難ですが、カーネルクラッシュダンプ (以降、クラッシュダンプと表現) があれば、メモリ内容から、クラッシュ時のコールトレース、仮想メモリ、プロセスやオープンファイルなど、原因解析の上で重要な手がかりとなる情報を確認することができます。
クラッシュの原因を知ることはとても重要です。原因が分からなければ対策も取れませんし、再発させてしまう可能性があります。特に商用環境など、クラッシュによる被害が大きい環境では、クラッシュダンプを取る設定をしておいた方が良いと思います。
クラッシュダンプを取るには Kdump という機能を使用します。
■ Kdump とは
クラッシュ時にメモリ内容をファイルへ保存 (ダンプ) する 機能です (Linux カーネル 2.6.13 以降)。
Kdump でクラッシュダンプを取るには、システムカーネル と ダンプキャプチャカーネル の2つを使用します。
1. システムカーネルとは
通常の操作で使用しているカーネルのことです。
このカーネルがクラッシュ (広義のシステムクラッシュ) すると、通常はメモリの内容が失われてしまいます。これを回避するために、第二のカーネルとして、ダンプキャプチャカーネルを起動し、メモリ内容を保存します。
2. ダンプキャプチャカーネルとは
メモリ内容をファイルとして保存するために起動される第二のカーネル (セカンドカーネルとも呼ぶ) のことです。
このカーネルは、Kdump からキックされる Kexec という機能により起動されます。前述のとおり、ダンプキャプチャカーネルでは、通常はクラッシュにより失われてしまうメモリ内容をファイルとして保存します。ただし、ダンプキャプチャカーネルは予めシステムカーネルのメモリ領域に読み込ませておく必要があります (設定方法は後述)。
3. クラッシュダンプ取得の流れ (ざっくり)
1 システムカーネルのメモリ領域にダンプキャプチャカーネルを読み込ませておく
↓
2 システムカーネルがクラッシュ
↓
3 Kdump (Kexec) により、ダンプキャプチャカーネルが起動
↓
4 ダンプキャプチャカーネルにより、システムカーネルのメモリ内容をファイルとして保存
↓
5 システム再起動後、クラッシュダンプが生成されていることが確認できる
■ Kdump の設定方法
前述のとおり、クラッシュダンプを取るには Kdump を使用します。
1. Red Hat 7 系 Linux では
Kdump を使用するためのパッケージである kexec-tools
がデフォルトでインストールされており、デフォルトで有効になっています。
そのため、Red Hat 7 系 Linux OS のインストールの際に Kdump の機能を無効化していなければ、Kdump の設定は不要です。まずは、Kdump が有効かどうかだけ確認してください (確認方法は後述)。
なお、Kdump の有効化と無効化は、インストール時に通る、次の画面にて変更できます。
2. Kdump が有効かどうかは
次のコマンドの結果が active であるかどうかで判断します。active なら有効なので、2章はスキップして大丈夫です。
[root@localhost ~]# systemctl status kdump.service
● kdump.service - Crash recovery kernel arming
Loaded: loaded (/usr/lib/systemd/system/kdump.service; disabled; vendor preset: enabled)
Active: active (exited) since Thu 2019-04-25 07:04:28 JST; 24min ago
・
・
3. Kdump を無効化していた場合には
設定を変更・反映の後、kdump.service
を start
することで有効化できます。
まずは、次のように設定を変更します。
・
・
GRUB_CMDLINE_LINUX="...(略)... crashkernel=auto"
・
・
この設定は、Kexec により起動されるダンプキャプチャカーネルを読み込むために必要なメモリ量の設定です。必要なメモリ量は auto
と指定するこで自動で計算されますが、128M
のように任意の値を設定することもできます。
設定を反映させるには、/boot/grub2/grub.cfg
を再生成した後、システムを再起動します。
※ ご使用のファームウェアによって手順が異なります。
[root@localhost ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
[root@localhost ~]# reboot
[root@localhost ~]# grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg
[root@localhost ~]# reboot
kdump.service
を起動します (自動起動も設定)。
[root@localhost ~]# systemctl start kdump.service
[root@localhost ~]# systemctl enable kdump.service
Kdump が起動しているか確認します。
[root@localhost ~]# systemctl status kdump.service
● kdump.service - Crash recovery kernel arming
Loaded: loaded (/usr/lib/systemd/system/kdump.service; disabled; vendor preset: enabled)
Active: active (exited) since Thu 2019-04-25 07:04:28 JST; 24min ago
...(snip)...
起動に失敗した場合は、status
に表示されるエラーログを確認の上、対応してください。よくあるものとしては、crashkernel
のスペルミスや不要な空白の混入、メモリ領域の確保に必要なメモリの不足などです。後者に関しては仮想マシンでメモリ量を少なく設定している場合 (1GBなど) によく見かけます。
4. ちなみに
ダンプキャプチャカーネル用に確保されたメモリ量は、次のとおり dmesg
から確認できます。この場合は、161MB です。後ろの 672MB はオフセット値のようです。
[root@localhost ~]# dmesg | grep Reserving
[ 0.000000] Reserving 161MB of memory at 672MB for crashkernel (System RAM: 3071MB)
また、ダンプキャプチャカーネが読み込まれているかどうかは /sys/kernel/kexec_crash_loaded
が 1 かどうかで判断できます。
[root@localhost ~]# cat /sys/kernel/kexec_crash_loaded
1
■ クラッシュダンプを取ってみる
前提として kdump.service
の起動が成功しているものとします。
1. クラッシュダンプを取るには
実際にシステムをクラッシュさせる必要があります。
クラッシュさせるには、Magic SysRq Key の機能を利用するのが簡単です。
次の入力では、1つ目で、Magic SysRq Key の全機能を有効化(一時的)し、2つ目で、NULL ポインタ参照によるシステムクラッシュを発生させます。これにより、即座にシステムクラッシュが発生します。ご注意ください。
$ echo 1 > /proc/sys/kernel/sysrq
$ echo c > /proc/sysrq-trigger
2. クラッシュダンプが出力される場所は
/var/crash/
配下です。クラッシュすると、address-YYYY-MM-DD-HH:MM:SS
形式のディレクトリが生成され、クラッシュダンプが出力されます。 vmcore がクラッシュダンプで、vmcore-dmesg.txt は、クラッシュ時のリングバッファの内容 (dmesg コマンドで見れるやつ) が保存されたテキストです。
[root@localhost ~]# tree /var/crash/
/var/crash/
└── 127.0.0.1-2019-04-25-21:24:51
├── vmcore
└── vmcore-dmesg.txt
なお、クラッシュダンプの出力先は /etc/kdump.conf
の path
から変更できます。デフォルトではコメントアウトされており、その場合は /var/crash/
に出力されます。
...(snip)...
# path <path>
# - "path" represents the file system path in which vmcore
# will be saved. If a dump target is specified in
# kdump.conf, then "path" is relative to the specified
# dump target.
#
# Interpretation of "path" changes a bit if the user didn't
# specify any dump target explicitly in kdump.conf. In this
# case, "path" represents the absolute path from root. The
# dump target and adjusted path are arrived at automatically
# depending on what's mounted in the current system.
#
# Ignored for raw device dumps. If unset, will use the default
# "/var/crash".
...(snip)...
3. クラッシュの大まかな原因を把握するには
vmcore-dmesg.txt
を確認するのが手っ取り早いです。
クラッシュした原因を解析するには crash
コマンドにより vmcore
を解析する必要がありますが、これには色々と設定が必要なので、やや面倒です。手っ取り早く、大まかな原因を把握したい場合には、vmcore-dmesg.txt
を確認するのが良いです。
これは単なるテキストなので、cat
なり vim
なりで見れます。
[root@localhost ~]# cat /var/crash/127.0.0.1-2019-04-25-21:24:51/vmcore-dmesg.txt
[ 0.000000] Initializing cgroup subsys cpuset
[ 0.000000] Initializing cgroup subsys cpu
[ 0.000000] Initializing cgroup subsys cpuacct
[ 0.000000] Linux version 3.10.0-957.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) ) #1 SMP Thu Nov 8 23:39:32 UTC 2018
...(snip)...
[51745.467192] SysRq : Trigger a crash
次の2行から「NULL ポインタ参照」が直接の原因であることと、「sysrq_handle_crash()」 でそれが発生したということが分かります。
[51745.467352] BUG: unable to handle kernel NULL pointer dereference at (null)
[51745.467356] IP: [<ffffffff9d461bf6>] sysrq_handle_crash+0x16/0x20
この情報だけでも、次に何を調べれば良いか検討がつきます。今回は Magic SysRq Key で意図的にクラッシュさせているため原因は明白ですが、通常は、まずバグの可能性を考えて、たとえば「NULL pointer dereference sysrq_handle_crash 」とかで Web 検索して類似の事象が発生していないか (既知のバグ) を確認するのが良いと思います。運がよければ、バグレポートから解決策や回避策を知ることができます。
なお、以降の情報からは他にも読み取れることがありますが、今回は解析がメインではないので割愛します。
[51745.467403] PGD 8000000098f52067 PUD a9306067 PMD 0
[51745.467411] Oops: 0002 [#1] SMP
[51745.467420] Modules linked in: tcp_lp fuse uinput xt_CHECKSUM ipt_MASQUERADE nf_nat_masquerade_ipv4 tun devlink ip6t_rpfilter ipt_REJECT nf_reject_ipv4 ip6t_REJECT nf_reject_ipv6 xt_conntrack ip_set nfnetlink ebtable_nat ebtable_broute bridge stp llc ip6table_nat nf_conntrack_ipv6 nf_defrag_ipv6 nf_nat_ipv6 ip6table_mangle ip6table_security ip6table_raw iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack iptable_mangle iptable_security iptable_raw ebtable_filter ebtables ip6table_filter ip6_tables iptable_filter sunrpc ppdev iosf_mbi crc32_pclmul ghash_clmulni_intel snd_hda_codec_generic aesni_intel lrw gf128mul glue_helper ablk_helper cryptd joydev pcspkr snd_hda_intel virtio_balloon snd_hda_codec snd_hda_core sg snd_hwdep snd_seq snd_seq_device snd_pcm parport_pc parport
[51745.467499] snd_timer snd soundcore i2c_piix4 ip_tables xfs libcrc32c sr_mod cdrom virtio_blk virtio_console virtio_net crct10dif_pclmul crct10dif_common crc32c_intel ata_generic serio_raw qxl pata_acpi drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops ttm floppy drm ata_piix libata virtio_pci virtio_ring virtio drm_panel_orientation_quirks dm_mirror dm_region_hash dm_log dm_mod
[51745.467550] CPU: 0 PID: 5013 Comm: bash Kdump: loaded Not tainted 3.10.0-957.el7.x86_64 #1
[51745.467555] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[51745.467559] task: ffff927722ec1040 ti: ffff92771e70c000 task.ti: ffff92771e70c000
[51745.467563] RIP: 0010:[<ffffffff9d461bf6>] [<ffffffff9d461bf6>] sysrq_handle_crash+0x16/0x20
[51745.467576] RSP: 0018:ffff92771e70fe58 EFLAGS: 00010246
[51745.467580] RAX: ffffffff9d461be0 RBX: ffffffff9dce4c60 RCX: 0000000000000000
[51745.467583] RDX: 0000000000000000 RSI: ffff92773fc13898 RDI: 0000000000000063
[51745.467585] RBP: ffff92771e70fe58 R08: ffffffff9dfe38bc R09: ffffffff9dfee37b
[51745.467591] R10: 000000000000025c R11: 000000000000025b R12: 0000000000000063
[51745.467594] R13: 0000000000000000 R14: 0000000000000004 R15: 0000000000000000
[51745.467598] FS: 00007fbedb513740(0000) GS:ffff92773fc00000(0000) knlGS:0000000000000000
[51745.467601] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[51745.467603] CR2: 0000000000000000 CR3: 000000009ce18000 CR4: 00000000001606f0
[51745.467610] Call Trace:
[51745.467617] [<ffffffff9d46241d>] __handle_sysrq+0x10d/0x170
[51745.467628] [<ffffffff9d462888>] write_sysrq_trigger+0x28/0x40
[51745.467645] [<ffffffff9d2b7f30>] proc_reg_write+0x40/0x80
[51745.467650] [<ffffffff9d2410a0>] vfs_write+0xc0/0x1f0
[51745.467654] [<ffffffff9d241ebf>] SyS_write+0x7f/0xf0
[51745.467680] [<ffffffff9d774ddb>] system_call_fastpath+0x22/0x27
[51745.467682] Code: eb 9b 45 01 f4 45 39 65 34 75 e5 4c 89 ef e8 e2 f7 ff ff eb db 0f 1f 44 00 00 55 48 89 e5 c7 05 21 57 7e 00 01 00 00 00 0f ae f8 <c6> 04 25 00 00 00 00 01 5d c3 0f 1f 44 00 00 55 31 c0 c7 05 9e
[51745.467750] RIP [<ffffffff9d461bf6>] sysrq_handle_crash+0x16/0x20
[51745.467755] RSP <ffff92771e70fe58>
[51745.467757] CR2: 0000000000000000
■ カーネルパニックに関するパラメータ
カーネルパニック (システムクラッシュ)を発生させる条件や、カーネルパニック後の再起動にかかる時間などは、パラメータによって制御できます。Kdump の設定と併せて、適宜設定しておくと良いと思います。
- 01. /proc/sys/debug/panic_on_rcu_stall
- 1 なら RCU (Read Copy Update) ストール発生時にパニック
- 02. /proc/sys/vm/panic_on_oom
- 1 なら OOM (Out Of Memory) 発生時かつ、物理メモリが枯渇している場合に限りパニック。2 なら OOM 発生時、強制的にパニック
- 03. /proc/sys/kernel/panic
- パニック後の再起動の待ち時間(秒)。watchdog を使用している場合は 60 (秒) が推奨値。
※追記: kdump 使用時、このパラメータは無視される。 - 04. /proc/sys/kernel/hung_task_panic
- 1 ならハングタスク発生時にパニック (CONFIG_DETECT_HUNG_TASK 有効時)
- 05. /proc/sys/kernel/softlockup_panic
- 1 ならソフトロックアップ発生時にパニック
- 06. /proc/sys/kernel/hardlockup_panic
- 1 ならハードロックアップ発生時にパニック
- 07. /proc/sys/kernel/panic_on_unrecovered_nmi
- 1 なら修正不可能なパリティや ECC エラーによる NMI 発生時にパニック
- 08. /proc/sys/kernel/panic_on_io_nmi
- 1 なら I/O エラーによる NMI (Non Maskable Interrupt) 発生時にパニック。この場合、システムは深刻な状態であり、IO データが破損する可能性があるため、システムをそのまま継続させるより、パニックさせた方が無難
- 09. /proc/sys/kernel/unknown_nmi_panic
- 0 以外なら未定義の NMI 発生時にパニック
- 10. /proc/sys/kernel/panic_on_warn
- 1 なら WARN() 呼び出し後にパニック
- 11. /proc/sys/kernel/panic_on_oops
- 1 なら oops もしくは BUG() 呼び出し時にパニック。ただし、/proc/sys/kernel/panic が 0 以外なら再起動
- 12. /proc/sys/kernel/panic_on_stackoverflow
- 1 ならスタックオーバーフロー (カーネル、IRQ、およびユーザースタックを除く、例外のスタック) の発生時にパニック (CONFIG_DEBUG_STACKOVERFLOW 有効時)
これらを設定するには /etc/sysctl.conf
に kernel.panic = 60
のようにパラメータと値を書き、sysctl -p
を実行するだけです。
■ 補足情報
1. 検証環境
- カーネル:
3.10.0-957.el7.x86_64
- ディストリビューション:
CentOS Linux release 7.6.1810 (Core)
- kexec-tools:
kexec-tools-2.0.15-21.el7.x86_64