日志定位:系统日志(dmesg、journalctl)的采集与关键字段解读

镜像加载失败,说白了就是内核在启动阶段或者运行时,没能把镜像文件正确挂载到内存里。这时候你第一反应是什么?我个人的习惯是——先别急着重启,先把系统日志捞出来看看。

系统日志是内核和关键服务留下的“案发现场”。你想想看,镜像加载失败这种问题,十有八九会在 dmesg 或者 journalctl 里留下痕迹。今天我们就来聊聊怎么采集这些日志,以及怎么看懂里面的关键字段。

1. dmesg:内核的“实时监控”

dmesg 是内核环缓冲区(ring buffer)的导出工具。说白了,内核每干一件事,都会往这个缓冲区里写一条记录。镜像加载、设备识别、驱动初始化……全在这里。

1.1 什么时候用 dmesg?

  • 系统刚启动,镜像加载失败,连登录界面都进不去
  • 热插拔设备后,镜像挂载报错
  • 内核模块加载失败,怀疑是镜像路径或权限问题

1.2 采集命令

# 查看所有内核日志
dmesg

# 实时跟踪新日志(类似 tail -f)
dmesg -w

# 只看最后 50 条
dmesg | tail -50

# 保存到文件,方便后续分析
dmesg > /tmp/kernel_log_$(date +%Y%m%d_%H%M%S).txt
我的小技巧: 如果系统已经挂了,连终端都进不去,可以在 GRUB 启动时按 e 编辑内核参数,加上 earlyprintk=vga,这样启动日志会直接输出到屏幕上。我曾经靠这个在无网络、无串口的机器上抓到了镜像加载失败的根因。

1.3 关键字段解读

dmesg 的输出格式大致是:

[    5.123456] <日志级别> <模块名>: <消息内容>
字段 含义 实战关注点
时间戳 系统启动后的秒数(精确到微秒) 看事件发生的先后顺序,定位第一个报错
日志级别 内核定义的严重等级(0-7) 重点关注 err(3)、crit(2)、alert(1)
模块名 产生日志的内核模块或驱动 比如 loop、overlay、ext4 等
消息内容 具体的错误描述 包含文件路径、错误码、设备节点等关键信息

镜像加载失败的典型 dmesg 报错:

[   12.345678] loop0: detected capacity change from 0 to 1048576
[   12.345679] EXT4-fs (loop0): VFS: Can't find ext4 filesystem
[   12.345680] overlayfs: failed to resolve 'lowerdir': -2

看到 -2 了吗?这是内核错误码,对应 ENOENT,意思是“文件或目录不存在”。说白了,就是镜像文件路径写错了,或者文件被删了。

2. journalctl:systemd 的“日志管家”

如果你的系统用的是 systemd(现在绝大多数 Linux 发行版都是),那 journalctl 就是你的首选。它不光收集内核日志,还收集所有 systemd 管理的服务日志。

2.1 什么时候用 journalctl?

  • 镜像加载失败,但系统还能部分运行(比如 SSH 还能连)
  • 需要查看某个特定服务(如 docker、containerd)的日志
  • 需要按时间范围、优先级、单元名等条件过滤

2.2 采集命令

# 查看所有日志(从当前启动开始)
journalctl

# 只看内核日志(相当于 dmesg 的增强版)
journalctl -k

# 查看本次启动的日志
journalctl -b

# 查看上一次启动的日志(系统崩溃后重启,看上次的日志)
journalctl -b -1

# 实时跟踪新日志
journalctl -f

# 按优先级过滤(只显示错误及以上)
journalctl -p err

# 按服务名过滤
journalctl -u docker.service

# 按时间范围过滤
journalctl --since "2024-01-01 10:00:00" --until "2024-01-01 11:00:00"

# 保存到文件
journalctl -b > /tmp/system_log_$(date +%Y%m%d_%H%M%S).txt
注意: journalctl 默认只保留最近几次启动的日志。如果系统频繁重启,旧日志会被自动清理。我建议在生产环境上配置 SystemMaxUse=500M 和 MaxRetentionSec=7day,确保关键日志不会丢失。

2.3 关键字段解读

journalctl 的输出格式更丰富:

Jan 15 10:30:45 hostname kernel: loop0: detected capacity change from 0 to 1048576
Jan 15 10:30:45 hostname dockerd[1234]: failed to mount overlay: no such file or directory
字段 含义 实战关注点
时间戳 系统本地时间(精确到秒) 与 dmesg 的时间戳对应,交叉验证
主机名 产生日志的主机 多节点环境时区分来源
服务名 产生日志的进程或服务 比如 kernel、dockerd、containerd
PID 进程 ID 配合 ps 或 systemctl status 进一步排查
消息内容 具体的日志信息 包含错误码、文件路径、调用栈等

3. 实战:镜像加载失败的日志定位流程

嗯,这里我画了一张流程图,帮你理清思路。遇到镜像加载失败,按这个顺序查,基本不会漏。

镜像加载失败日志定位流程 镜像加载失败 步骤1:dmesg 查看内核日志 是否找到镜像相关报错? 是 分析错误码和路径 否 步骤2:journalctl 查服务日志 找到报错? 是 定位根因,修复后验证 结束 避坑提示 • 先查 dmesg,再查 journalctl • 关注第一个报错,而非最后一个 • 保存原始日志,不要直接清空

4. 避坑指南:我踩过的那些坑

做运维这些年,我在日志采集上栽过不少跟头。分享几个典型的,你遇到了可以少走弯路。

  • 坑一:dmesg 日志被覆盖 —— 我曾经在一台高负载的机器上查问题,dmesg 里只看到最后几百条记录,前面的全被新日志冲掉了。后来我养成了习惯:出问题第一时间 dmesg > /tmp/dmesg_save.txt,先保存再说。
  • 坑二:journalctl 默认不持久化 —— 有一次系统崩溃后重启,我想看上次的日志,结果 journalctl -b -1 告诉我没有日志。为什么?因为 /var/log/journal 目录不存在,日志只存在内存里。解决方案:mkdir -p /var/log/journal && systemd-tmpfiles --create --prefix /var/log/journal。
  • 坑三:只看错误级别,忽略上下文 —— 镜像加载失败,你看到 overlayfs: failed to resolve 'lowerdir': -2,以为只是路径问题。但如果你往前翻几条日志,可能会发现 loop0: detected capacity change from 0 to 1048576,说明镜像文件本身已经被识别了,只是挂载参数不对。上下文很重要。

5. 总结:日志采集的黄金法则

我的三条原则:

  1. 先保存,再分析 —— 日志是易失的,尤其是 dmesg 的环形缓冲区。第一时间保存到文件。
  2. 从内核到服务,逐层排查 —— 先看 dmesg 有没有内核报错,再看 journalctl 里相关服务的日志。不要跳过内核层直接查应用层。
  3. 关注第一个报错 —— 很多时候后面的报错都是连锁反应。找到第一个错误,就找到了根因。