概述
凌晨三点,你被告警吵醒,登录服务器发现屏幕上只有一行 Kernel panic - not syncing: Fatal exception,然后系统就重启了。等你好不容易连上去,崩溃现场什么都没留下——没有日志,没有 core dump,没有调用栈。你只能对着一句 systemd-logind: System is going down 发呆。
这种场景,每个干过几年运维的人都遇到过。内核崩溃本身已经够头疼了,但更头疼的是崩溃后什么都抓不到,问题根本没法定位。
kdump 就是解决这个问题的。它相当于给 Linux 服务器装了一台飞行记录仪——飞机坠毁时,黑匣子能告诉你最后几秒发生了什么;内核崩溃时,kdump 能把崩溃瞬间的完整内存状态保存下来,让你事后用 crash 工具逐帧分析。
这篇文章不讲虚的,从 kdump 的工作原理到生产环境的完整配置流程,再到 crash 工具的实际分析操作,全部覆盖。读完之后,你应该能在自己的服务器上搭一套可靠的崩溃捕获系统。
kdump 是怎么工作的
双内核机制
要理解 kdump,先搞懂一个关键概念:崩溃时主内核已经不可信了。你不能指望一个已经 panic 的内核去把自己的内存好好保存下来——它连正常执行代码都做不到。
kdump 的思路很巧妙:系统启动时,提前预留一块物理内存,在里面加载一个精简的"捕获内核"(capture kernel,也叫第二内核)。主内核正常运行时,这块内存被隔离,谁都不许动。一旦主内核崩溃,kexec 机制会直接把 CPU 控制权交给捕获内核——不走 BIOS,不重启硬件,直接在预留内存里启动。捕获内核接管后,主内核的内存内容还完好无损地躺在那里,捕获内核把它打包成 vmcore 文件,写到磁盘上。
整个过程像这样:
┌──────────────────────────────────────────────┐
│ 物理内存布局 │
│ │
│ ┌────────────────┐ ┌───────────────────┐ │
│ │ 主内核区域 │ │ 预留内存区域 │ │
│ │ (正常运行) │ │ (crashkernel) │ │
│ │ │ │ │ │
│ │ 用户进程 │ │ 捕获内核 + │ │
│ │ 内核模块 │ │ initramfs │ │
│ │ 页缓存... │ │ (待命状态) │ │
│ └────────────────┘ └───────────────────┘ │
│ │
│ 主内核崩溃 → kexec 切换 → 捕获内核启动 │
│ → 读取 /proc/vmcore → 写入 /var/crash/ │
└──────────────────────────────────────────────┘
kexec:不开机的热切换
kexec 是 kdump 的底层支撑技术。它允许在不经过 BIOS 启动流程的情况下,直接把一个内核镜像加载到内存并跳转执行。正常重启要走 BIOS POST、GRUB 引导、内核解压这一长串流程,可能要几分钟。kexec 跳过了这些,直接从一个内核跳到另一个内核,几毫秒的事。
这对崩溃捕获至关重要:主内核崩溃后,内存里的数据如果不赶紧保存,可能随着硬件复位而丢失。kexec 的快速切换让捕获内核能在内存数据被破坏之前就把现场冻结下来。
参考 Red Hat 内核崩溃转储指南,kdump 的工作流程可以拆成几步:
- 系统启动时,通过
crashkernel内核参数预留内存 - 主内核启动后,kdump 服务把捕获内核加载到预留内存
- 主内核正常运行,捕获内核处于待命状态
- 主内核崩溃,kexec 触发捕获内核启动
- 捕获内核挂载文件系统,将主内核内存转储为 vmcore
- 转储完成后,捕获内核重启系统
生产环境配置实战
安装必要组件
CentOS / RHEL 系:
# 安装 kexec-tools(含 kdump 服务和 kexec 工具)
sudo dnf install kexec-tools
# 安装 crash 工具(用于事后分析 vmcore)
sudo dnf install crash
# 安装内核调试信息包(crash 分析需要,包名跟内核版本对应)
sudo dnf install kernel-debuginfo-$(uname -r)
Ubuntu / Debian 系:
sudo apt install kexec-tools makedumpfile crash
sudo apt install linux-image-$(uname -r)-dbg
安装 kexec-tools 时,Ubuntu 会弹一个对话框问你要不要在系统崩溃时自动触发 kdump,选 Yes。
预留内存:crashkernel 参数
这是配置 kdump 最关键的一步,也是最容易出错的地方。
crashkernel 是内核启动参数,用来告诉内核"给我留多少内存给捕获内核用"。它在 GRUB 配置里设置。
编辑 /etc/default/grub:
# 编辑 GRUB 配置
sudo vi /etc/default/grub
# 在 GRUB_CMDLINE_LINUX 行中添加 crashkernel 参数
# 常见写法:
# crashkernel=256M 固定预留 256M
# crashkernel=auto 自动计算(RHEL 系支持,但有时不够准)
# crashkernel=128M@64M 预留 128M,起始地址 64M
# crashkernel=2G-16G:256M,16G-64G:512M 按系统内存大小分级预留
GRUB_CMDLINE_LINUX="crashkernel=256M rd.lvm.lv=centos/root rhgb quiet"
预留多少内存? 这是最常被问的问题。我的经验值:
| 系统物理内存 | 推荐 crashkernel | 说明 |
|---|---|---|
| 4G - 8G | 128M - 256M | 小内存机器,尽量少预留 |
| 8G - 16G | 256M | 常见配置,够用 |
| 16G - 64G | 256M - 512M | 中等规模,留点余量 |
| 64G - 256G | 512M - 768M | 大内存机器,vmcore 也大 |
| 256G+ | 768M - 1G | 超大内存,可能需要 1G+ |
实际需要多少取决于内核版本、加载的模块数量、以及你用 makedumpfile 做了多少过滤。最佳做法是先用推荐值配置,然后实际触发一次崩溃测试,看捕获内核能不能正常启动并完成转储。
更新 GRUB 并重启:
# RHEL/CentOS 7 系
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
# RHEL/CentOS 8+ / Fedora 系
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
# 或者(EFI 系统)
sudo grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg
# Ubuntu / Debian 系
sudo update-grub
# 重启使 crashkernel 预留生效
sudo reboot
重启后验证预留是否成功:
# 检查内核启动参数是否包含 crashkernel
cat /proc/cmdline | grep crashkernel
# 检查预留内存区域
dmesg | grep -i "crashkernel"
# 预期输出类似:
# Reserving 256MB of memory at 800MB for crashkernel
# 检查 kdump 服务状态
systemctl status kdump.service
kdump 配置文件
kdump 的主配置文件是 /etc/kdump.conf(RHEL/CentOS)或 /etc/default/kdump-tools(Ubuntu)。这里控制转储文件的存储位置、压缩方式、通知脚本等。
一个生产级配置示例:
# /etc/kdump.conf
# vmcore 存储路径(默认 /var/crash)
path /var/crash
# 核心采集器:makedumpfile
# -d 8: 过滤掉以下类型的页面(按位掩码):
# 1=zero pages, 2=cache pages, 4=cache private, 8=user data,
# 16=free pages
# -c: 压缩输出
# 用 -d 31 可以过滤掉所有可过滤的页面,显著减小 vmcore 大小
core_collector makedumpfile -l --message-level 1 -d 31
# 转储完成后执行的动作(默认 reboot)
# 可选:reboot / halt / poweroff / shell
# shell 模式在转储后进入 shell,方便手动检查
default reboot
# 转储目标覆盖(可选)
# 如果 /var/crash 在小分区上,可以指定写到其他分区
# ext4 /dev/sdb1
# nfs my.nfs.server:/export/crashdumps
# 转储失败时的行为
# failure_action 选项:continue / halt / reboot / shell
# 建议生产环境用 shell,转储失败时至少给你一个排查机会
failure_action shell
关于 makedumpfile 的过滤级别,这里展开说明一下。vmcore 如果直接把所有内存原样保存,一个 64G 内存的机器就会生成 64G 的转储文件,写入磁盘要很久,还可能撑爆 /var/crash 分区。makedumpfile 通过分析内核的页表结构,过滤掉不需要的页面:
| 过滤标志 | 过滤内容 | 节省空间 |
|---|---|---|
-d 1 | 全零页面 | 5-15% |
-d 2 | 内核页缓存 | 10-30% |
-d 4 | 私有页缓存 | 5-10% |
-d 8 | 用户态进程数据 | 30-60% |
-d 16 | 空闲页面 | 20-50% |
-d 31 | 以上全部 | 通常减小 70-90% |
生产环境推荐 -d 31,除非你需要在 vmcore 里检查某个用户态进程的内存内容。加上 -l 启用压缩,vmcore 通常能从原始内存大小的 100% 压缩到 5-15%。
配置 SSH 远程转储
如果你的服务器本地磁盘空间紧张,或者崩溃后本地文件系统可能不可用(比如根分区是 LVM 且出了问题),可以把 vmcore 直接转储到远程服务器:
# /etc/kdump.conf
# SSH 远程转储
ssh root@crash-collector.internal
sshkey /root/.ssh/id_rsa
path /data/crashdumps
core_collector makedumpfile -l --message-level 1 -d 31
default reboot
配置远程转储时,确保:
- 捕获内核的 initramfs 里包含了 SSH 客户端和网络驱动
- 目标服务器已添加到 crash-collector 的
authorized_keys - 网络配置在 initramfs 环境下可用(可能需要静态 IP 或 DHCP 配置)
重新生成 initramfs 使配置生效:
# RHEL/CentOS
sudo kdumpctl restart
# Ubuntu/Debian
sudo systemctl restart kdump-tools
验证 kdump 服务
# 查看服务状态
systemctl status kdump
# 检查捕获内核是否已加载到预留内存
# 方法1:查看 kexec 加载状态
kexec -p -l /boot/vmlinuz-$(uname -r) 2>&1
# 如果已加载,会提示 "kexec_load failed: File exists" 或类似信息
# 方法2:查看 kdump 内核加载日志
dmesg | grep -i kdump
# 预期输出:
# kdump: Loaded kdump kernel at 0x...
# kdump: kexec: kdump kernel loaded
# 方法3:检查 /sys/kernel/kexec_loaded
cat /sys/kernel/kexec_loaded
# 0 = 未加载, 1 = 已加载(kdump 正常工作时应为 1)
# 检查 kexec_crash_loaded(崩溃内核是否加载)
cat /sys/kernel/kexec_crash_loaded
# 1 = 崩溃捕获内核已就位
手动触发崩溃测试
配置好了不测一下,等于没配。这一步我在每台新上线服务器上都会做。
警告:这会让你的服务器立刻崩溃并重启。请确保没有业务在跑,或者在一台测试机器上操作。
# 第一步:确认 kdump 服务正常
systemctl status kdump
# 第二步:确认 panic_on_oops 已开启
sysctl kernel.panic_on_oops
# 值应为 1,如果不是:
sudo sysctl -w kernel.panic_on_oops=1
# 第三步:确保 panic 后自动重启
sysctl kernel.panic
# 值应大于 0(表示 panic 后多少秒重启),设为 0 不会自动重启
# 建议:
sudo sysctl -w kernel.panic=10
# 第四步:同步磁盘数据,避免文件系统损坏
sync && sync && sync
# 第五步:触发内核崩溃!
# 方法A:通过 SysRq 触发(推荐)
echo c > /proc/sysrq-trigger
# 方法B:如果 SysRq 被禁用,先开启
echo 1 > /proc/sys/kernel/sysrq
echo c > /proc/sysrq-trigger
echo c > /proc/sysrq-trigger 会触发一个空指针解引用,导致内核 panic。如果你的 kdump 配置正确,系统会:
- 打印 panic 信息到控制台
- kexec 切换到捕获内核
- 捕获内核启动,开始采集 vmcore
- 采集完成后自动重启
重启后检查:
# 查看 vmcore 是否已保存
ls -lh /var/crash/
# 预期看到类似:
# drwxr-xr-x 2 root root 4096 Jul 17 09:20 127.0.0.1-2026-07-17-09:20:01
# 里面应该有 vmlinux(或 vmlinuz)和 vmcore 文件
# 检查 vmcore 文件大小
ls -lh /var/crash/*/vmcore*
# 压缩后的 vmcore 通常在几百 MB 到几 GB 之间
常见配置失败排查
如果触发崩溃后没找到 vmcore,或者 kdump 服务压根起不来,按这个清单排查:
问题1:kdump.service 启动失败
# 查看具体错误
journalctl -u kdump.service -e
# 最常见原因:crashkernel 参数没配或预留不够
# 检查:
cat /proc/cmdline | grep crashkernel
# 如果没有 crashkernel= 参数,说明 GRUB 配置没更新
# 检查预留内存是否生效
dmesg | grep -i crashkernel
# 如果看到 "crashkernel reservation failed" 说明预留失败
# 可能原因:内存太小,或预留地址与其它区域冲突
参考 CSDN 上关于 kdump.service 启动失败的排查文章,crashkernel 参数配置不当是 kdump 启动失败的"头号元凶"。
问题2:崩溃后没有生成 vmcore
# 检查是否有 kdump 日志
journalctl -b -1 | grep -i kdump
# -b -1 表示上一次启动的日志
# 检查 /var/crash 是否有空间
df -h /var/crash
# 检查控制台输出(如果有串口或 IPMI 日志)
# 常见原因:
# 1. 捕获内核的 initramfs 缺少磁盘驱动
# 2. /var/crash 分区空间不足
# 3. makedumpfile 参数错误
问题3:crashkernel=auto 不生效
crashkernel=auto 在某些环境下(特别是虚拟机或自定义内核)可能无法正确计算预留大小。我建议直接写固定值,别用 auto。虚拟机环境尤其要注意,有些 hypervisor 不支持 crashkernel 内存预留。
用 crash 工具分析 vmcore
拿到 vmcore 只是第一步,不分析等于白拿。crash 工具是分析 vmcore 的标准工具,它结合内核调试信息(debuginfo),能让你像用 GDB 调试用户态程序一样调试内核。
启动 crash
# 基本用法:crash <vmcore> <vmlinux-debuginfo>
# vmlinux-debuginfo 通常在 /usr/lib/debug/lib/modules/$(uname -r)/vmlinux
crash /var/crash/127.0.0.1-2026-07-17-09:20:01/vmcore \
/usr/lib/debug/lib/modules/$(uname -r)/vmlinux
如果找不到 vmlinux debuginfo 文件:
# RHEL/CentOS
sudo dnf install kernel-debuginfo-$(uname -r)
# Ubuntu
sudo apt install linux-image-$(uname -r)-dbg
# 找 vmlinux 文件位置
find /usr/lib/debug -name vmlinux
常用 crash 命令
进入 crash 交互界面后,以下命令最常用:
crash> bt # 打印崩溃时的内核调用栈(最常用)
crash> bt -a # 打印所有 CPU 的调用栈
crash> ps # 列出崩溃时所有进程
crash> ps | grep -i "D" # 筛选 D 状态(不可中断睡眠)进程
crash> log # 打印内核日志缓冲区(dmesg 内容)
crash> sys # 显示系统信息(内核版本、CPU、内存等)
crash> files # 列出崩溃时打开的文件
crash> vm # 显示虚拟内存信息
crash> kmem -i # 内核内存使用概况
crash> dev -l # 列出已加载的设备驱动
crash> mod # 列出已加载的内核模块
crash> mod -s <名称> # 加载指定模块的调试信息
crash> struct <结构名> <地址> # 查看指定地址的结构体内容
crash> rd <地址> <数量> # 读取内存
crash> dis <地址> # 反汇编指定地址的代码
实战分析示例
假设我们触发了一次 panic,用 crash 分析崩溃原因:
# 1. 先看调用栈,确定崩溃发生在哪里
crash> bt
PID: 0 TASK: ffffffff81c10480 CPU: 0 COMMAND: "swapper/0"
#0 [ffff88003fc03c90] machine_kexec at ffffffff8105f7a0
#1 [ffff88003fc03ce0] crash_kexec at ffffffff810b0a72
#2 [ffff88003fc03db0] oops_end at ffffffff81009524
#3 [ffff88003fc03dd0] no_context at ffffffff8104b6a5
#4 [ffff88003fc03e20] __bad_area_nosemaphore at ffffffff8104b6e5
#5 [ffff88003fc03e70] bad_area at ffffffff8104b810
#6 [ffff88003fc03ea0] do_page_fault at ffffffff8104bd76
#7 [ffff88003fc03f30] page_fault at ffffffff816012b8
[exception RIP: my_driver_write+42]
RIP: ffffffffa0001234 RSP: ffff88003fc03fe8 RFLAGS: 00010246
RAX: 0000000000000000 RBX: ffff88003e8a0000 RCX: 0000000000000000
RDX: 0000000000000100 RSI: ffff88003e8a1000 RDI: 0000000000000000
RBP: ffff88003fc03ff0
#8 [ffff88003fc03ff8] sys_write at ffffffff811df3a2
# 调用栈解读:
# 1. sys_write 被调用(用户态 write 系统调用)
# 2. 进入 my_driver_write(自定义驱动模块的 write 函数)
# 3. 发生 page_fault(缺页异常)
# 4. bad_area → no_context → oops_end → crash_kexec → machine_kexec
# 说明:my_driver_write 函数访问了一个无效的内存地址,触发缺页异常
# 2. 反汇编崩溃位置的代码
crash> dis ffffffffa0001234
0xffffffffa0001234 <my_driver_write+42>: mov %rax,(%rdi)
# RDI = 0(从上面的寄存器信息),mov %rax,(0) 就是对空指针解引用
# 3. 查看是哪个模块
crash> mod -s my_driver
MODULE NAME SIZE OBJECT FILE
ffff88003e8a0000 my_driver 16384 /lib/modules/.../my_driver.ko
# 4. 查看模块里的函数源码(需要 debuginfo)
crash> sym ffffffffa0001234
ffffffffa0001230 (t) my_driver_write+38 /usr/src/my_driver/write.c: 42
# 定位到 write.c 第 42 行,发现是对一个未初始化的指针做写操作
这个示例展示了从 vmcore 定位到具体代码行的完整流程。实际工作中,大部分内核崩溃都能通过 bt → dis → sym 三步定位到问题函数。
分析 D 状态进程
有时候崩溃不是因为空指针,而是因为死锁——进程卡在 D 状态(不可中断睡眠),最终触发 hung task 检测导致 panic。这种情况下要换个思路:
# 查找 D 状态进程
crash> ps | grep "UN"
PID PPID CPU TASK ST %MEM VSZ RSS COMM
12345 1 0 ffff88003e5b8000 UN 0.2 262144 8192 mysql
# UN = Uninterruptible Sleep
# 查看这个进程的调用栈
crash> bt ffff88003e5b8000
PID: 12345 TASK: ffff88003e5b8000 CPU: 0 COMMAND: "mysql"
#0 [ffff88003e8a3d80] __schedule at ffffffff8109a2a3
#1 [ffff88003e8a3dd0] schedule at ffffffff8109a3a5
#2 [ffff88003e8a3e00] schedule_timeout at ffffffff8109a6b0
#3 [ffff88003e8a3e50] wait_for_completion at ffffffff8109a8c0
#4 [ffff88003e8a3ea0] flush_work at ffffffff81098123
#5 [ffff88003e8a3ef0] __cancel_work_timer at ffffffff81098456
#6 [ffff88003e8a3f50] cancel_work_sync at ffffffff81098501
# 这个进程卡在 cancel_work_sync → wait_for_completion
# 说明有一个 work_struct 永远没有被执行完成
# 查看这个进程在等什么
crash> struct task_struct ffff88003e5b8000
struct task_struct {
...
state = 2, // TASK_UNINTERRUPTIBLE
...
}
与 kdump 相关的内核参数
除了 crashkernel,还有几个内核参数会影响 kdump 的行为,建议在生产环境中配置:
# /etc/sysctl.d/99-kdump.conf
# panic 时触发 kdump(而不是直接重启)
kernel.panic_on_oops = 1
# panic 后多少秒自动重启(给 kdump 时间完成转储)
# 设太短可能导致转储没完成就被强制重启
kernel.panic = 10
# SysRq 功能(允许通过 /proc/sysrq-trigger 手动触发崩溃测试)
kernel.sysrq = 1
# hung task 检测:进程 D 状态超过指定秒数触发告警
# 默认 120 秒,可根据业务调整
kernel.hung_task_timeout_secs = 120
# hung task 检测触发 panic(默认只告警不 panic)
# 如果你想让 hung task 也触发 kdump 捕获,设为 1
kernel.hung_task_panic = 1
# 硬件 NMI(不可屏蔽中断)触发 panic
# 对于硬件故障导致的挂死,NMI 是最后的手段
kernel.unknown_nmi_panic = 1
# 软锁定检测:CPU 长时间不让出触发告警
kernel.softlockup_panic = 1
一个容易踩的坑:kernel.panic 设为 0 的话,panic 后系统不会自动重启,会一直挂着。这在物理服务器上意味着你需要去机房按电源键。但如果你设太短(比如 1 秒),kdump 可能还没来得及完成转储就被强制重启了。我一般设 10 秒,这个值在大多数环境下都够用。
不同场景下的配置策略
物理服务器
物理服务器是 kdump 最有价值的场景。硬件故障、驱动 bug、固件问题都可能导致内核崩溃,而物理服务器上崩溃后的现场保存最难——你没有 hypervisor 层面的内存快照功能。
配置要点:
# 物理服务器内存通常较大,预留可以多一点
# GRUB 参数:
crashkernel=512M
# 配置 IPMI 查看 crash 时的控制台输出
# 即使 kdump 没能保存 vmcore,IPMI 的 System Event Log
# 也可能记录了崩溃前的硬件信息
# 考虑配置串口控制台,把 panic 信息输出到串口
# GRUB 参数追加:
console=tty0 console=ttyS0,115200
虚拟机(KVM/QEMU)
虚拟机环境配置 kdump 有几个注意点:
# 虚拟机内存通常不大,预留可以少一点
crashkernel=128M
# 确保虚拟机配置了足够预留内存
# 在 libvirt XML 中检查 memory 和 currentMemory 设置
# 虚拟机可以借助 hypervisor 的内存快照作为补充手段
# virsh dump <domain> <file> --memory-only
# 这个命令在虚拟机完全无响应时特别有用
容器宿主机
容器宿主机(Docker host / Kubernetes node)上配置 kdump 尤其重要,因为一个节点的内核崩溃会影响上面运行的所有容器:
# 宿主机配置 kdump 与普通服务器一致
crashkernel=256M
# 关键:确保 kdump 的 initramfs 包含存储驱动模块
# 如果使用 overlay2,需要确认相关模块在 initramfs 中
# 在 /etc/dracut.conf.d/kdump.conf 中添加:
add_drivers+=" overlay br_netfilter nf_conntrack "
# 转储完成后通知 Kubernetes
# 在 /etc/kdump.conf 中配置 kdump_post 脚本:
# kdump_post /usr/local/bin/kdump-notify.sh
kdump 通知脚本示例:
#!/bin/bash
# /usr/local/bin/kdump-notify.sh
# 在 kdump 转储完成后执行,$1 为状态(0=成功,1=失败)
if [ "$1" -eq 0 ]; then
# 转储成功,发送通知
logger -t kdump "vmcore captured successfully at $(date)"
# 可以在这里调用企业微信/飞书/钉钉 webhook
curl -s -X POST "https://your-webhook.example.com/notify" \
-H "Content-Type: application/json" \
-d "{\"event\":\"kernel_panic\",\"host\":\"$(hostname)\",\"vmcore\":\"$(ls -t /var/crash/ | head -1)\"}"
else
# 转储失败
logger -t kdump "vmcore capture FAILED at $(date)"
fi
vmcore 的存储管理
vmcore 文件动辄几个 GB,如果不管它,很快就会把磁盘撑爆。需要设置自动清理策略:
#!/bin/bash
# /usr/local/bin/cleanup-vmcore.sh
# 保留最近 5 份 vmcore,更老的自动删除
CRASH_DIR="/var/crash"
KEEP_COUNT=5
# 按修改时间排序,删除最老的
cd "$CRASH_DIR" || exit 1
ls -dt */ 2>/dev/null | tail -n +$((KEEP_COUNT + 1)) | while read dir; do
echo "Removing old vmcore: $dir"
rm -rf "$dir"
done
# 检查磁盘使用率,超过 80% 删除最老的
USAGE=$(df "$CRASH_DIR" | awk 'NR==2{print $5}' | tr -d '%')
while [ "$USAGE" -gt 80 ]; do
OLDEST=$(ls -dt */ 2>/dev/null | tail -1)
if [ -z "$OLDEST" ]; then
break
fi
echo "Disk usage ${USAGE}%, removing: $OLDEST"
rm -rf "$OLDEST"
USAGE=$(df "$CRASH_DIR" | awk 'NR==2{print $5}' | tr -d '%')
done
加到 crontab:
# 每天凌晨 3 点清理
0 3 * * * /usr/local/bin/cleanup-vmcore.sh >> /var/log/vmcore-cleanup.log 2>&1
kdump 与 systemd-coredump 的区别
经常有人混淆 kdump 和 systemd-coredump,这里做个对比说明:
| 维度 | kdump | systemd-coredump |
|---|---|---|
| 捕获对象 | 内核崩溃(kernel panic) | 用户态进程崩溃(segfault 等) |
| 工作层级 | 内核空间 | 用户空间 |
| 转储内容 | 整个系统内存映像 | 单个进程的内存映像 |
| 触发条件 | 内核 panic / Oops | 进程收到 SIGSEGV/SIGABRT 等 |
| 分析工具 | crash | gdb / coredumpctl |
| 文件大小 | 几百 MB ~ 几 GB | 几 MB ~ 几百 MB |
| 是否需要重启 | 是,转储后系统重启 | 否,只影响崩溃的进程 |
简单说:用户态程序崩溃用 coredump,内核崩溃用 kdump。两者不冲突,可以同时配置。
生产环境完整配置清单
把前面讲的汇总成一份可直接使用的配置清单:
#!/bin/bash
# kdump 生产环境配置脚本
# 适用于 RHEL/CentOS 8+,其他发行版需调整包名
set -euo pipefail
echo "=== 1. 安装组件 ==="
dnf install -y kexec-tools crash
dnf install -y "kernel-debuginfo-$(uname -r)" 2>/dev/null || \
echo "Warning: kernel-debuginfo not found, crash analysis may be limited"
echo "=== 2. 配置 crashkernel 参数 ==="
GRUB_FILE="/etc/default/grub"
if ! grep -q "crashkernel=" "$GRUB_FILE"; then
sed -i 's/GRUB_CMDLINE_LINUX="/GRUB_CMDLINE_LINUX="crashkernel=256M /' "$GRUB_FILE"
echo "Added crashkernel=256M to GRUB"
else
echo "crashkernel already configured"
fi
echo "=== 3. 配置 sysctl 参数 ==="
cat > /etc/sysctl.d/99-kdump.conf << 'EOF'
kernel.panic_on_oops = 1
kernel.panic = 10
kernel.sysrq = 1
kernel.hung_task_timeout_secs = 120
kernel.hung_task_panic = 1
kernel.unknown_nmi_panic = 1
kernel.softlockup_panic = 1
EOF
sysctl --system
echo "=== 4. 配置 kdump.conf ==="
cat > /etc/kdump.conf << 'EOF'
path /var/crash
core_collector makedumpfile -l --message-level 1 -d 31
default reboot
failure_action shell
EOF
echo "=== 5. 启用并启动 kdump ==="
systemctl enable kdump
systemctl restart kdump
echo "=== 6. 创建 vmcore 自动清理 cron ==="
cat > /usr/local/bin/cleanup-vmcore.sh << 'SCRIPT'
#!/bin/bash
CRASH_DIR="/var/crash"
KEEP_COUNT=5
cd "$CRASH_DIR" || exit 1
ls -dt */ 2>/dev/null | tail -n +$((KEEP_COUNT + 1)) | while read dir; do
rm -rf "$dir"
done
USAGE=$(df "$CRASH_DIR" | awk 'NR==2{print $5}' | tr -d '%')
while [ "$USAGE" -gt 80 ]; do
OLDEST=$(ls -dt */ 2>/dev/null | tail -1)
[ -z "$OLDEST" ] && break
rm -rf "$OLDEST"
USAGE=$(df "$CRASH_DIR" | awk 'NR==2{print $5}' | tr -d '%')
done
SCRIPT
chmod +x /usr/local/bin/cleanup-vmcore.sh
echo "0 3 * * * root /usr/local/bin/cleanup-vmcore.sh >> /var/log/vmcore-cleanup.log 2>&1" > /etc/cron.d/vmcore-cleanup
echo "=== 7. 更新 GRUB ==="
grub2-mkconfig -o /boot/grub2/grub.cfg
echo ""
echo "=== 配置完成 ==="
echo "请重启系统使 crashkernel 参数生效:sudo reboot"
echo "重启后验证:"
echo " 1. cat /proc/cmdline | grep crashkernel"
echo " 2. systemctl status kdump"
echo " 3. cat /sys/kernel/kexec_crash_loaded (应为 1)"
echo ""
echo "测试 kdump(会触发崩溃重启):"
echo " echo c > /proc/sysrq-trigger"
总结
kdump 是 Linux 运维中"平时用不上,用时后悔没配"的典型工具。配置它花不了半小时,但内核崩溃时有没有 vmcore,可能决定了你排查问题需要 2 小时还是 2 天。
几个实战经验总结:
预留内存宁可多不可少。捕获内核启动需要内存,预留不够直接导致转储失败。256M 是大多数场景的安全值,大内存机器加到 512M。
makedumpfile 的过滤级别用
-d 31。不压缩的 vmcore 可能比物理内存还大,-d 31 -l能压缩到原始大小的 5-15%。必须实际触发测试。配置完不测,等于在崩溃时赌运气。
echo c > /proc/sysrq-trigger这条命令,我在每台新服务器上线时都会跑一次。kernel.panic 不能设为 0。panic 后不重启,物理服务器就只能去机房了。但也不能设太短,10 秒是个合理值——给 kdump 足够时间完成转储。
vmcore 要有清理策略。一个 vmcore 几个 GB,攒几次磁盘就满了,kdump 就成了新的故障源。
远程转储值得配。当本地文件系统出问题导致崩溃时,本地转储也会失败。SSH 远程转储是兜底方案。
hung_task_panic 和 softlockup_panic 建议开启。很多内核问题不是 panic 而是死锁,开了这两个参数才能让 kdump 捕获到死锁现场。
最后一句话:kdump 不是可选项,是生产服务器的标配。别等炸了才想起来配。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Red Hat Enterprise Linux 内核崩溃转储指南 — Red Hat, kdump 官方文档,工作原理与配置参数说明
- Linux服务器宕机别慌!手把手教你用Kdump抓取内核崩溃现场 — CSDN,CentOS 7/8 环境下 kdump 配置实战与 crashkernel 内存预留指南
- RHEL 8系统kdump.service启动失败排查与修复 — CSDN,crashkernel 参数配置不当导致 kdump 服务启动失败的排查流程
- Linux内核崩溃现场取证指南:手把手教你用kdump和crash分析panic日志 — CSDN,crash 工具分析 vmcore 的实战操作指南
- Linux宕机救星:3种捕获完整Oops日志的方法对比 — CSDN,Oops 日志捕获方案对比与 kdump 配置指南