journalctl —— systemd 日志查询
一句话定义
journalctl 是 systemd-journald(systemd 的日志收集器)的查询工具。所有用 systemd unit 跑的服务,它们的 stdout/stderr 都被 journald 收集起来,结构化存储、统一查询。
典型场景
- 服务
status显示 failed:journalctl -u <service> -n 100 - 看 kubelet / containerd / sshd 实时日志:
journalctl -u kubelet -f - 排查"昨晚某个时刻发生了什么":
journalctl --since "2026-05-27 02:00" --until "2026-05-27 03:00" - 启动失败排查:
journalctl -b -p err(看本次启动的所有 err 级日志) - 节点磁盘满了:
journalctl --vacuum-size=500M(限制日志总大小)
和老式 syslog/rsyslog 的区别
| 维度 | 传统 /var/log/*.log | journald |
|---|---|---|
| 存储 | 纯文本,一文件一服务 | 二进制索引文件(/var/log/journal/...) |
| 查询 | grep、tail -f、less | journalctl 各种过滤 |
| 结构化 | 没有(一行一行 grep) | 每条日志带 unit、PID、UID、boot ID、timestamp |
| 跨服务关联 | 难(多个文件) | 容易(单一查询接口) |
| 持久化 | 默认持久 | 默认可能只在内存 / /run(重启丢) |
K8s 节点上几乎所有系统服务(kubelet / containerd / kube-proxy)都跑在 systemd 下,日志全在 journald。学会 journalctl 是 K8s 运维基本功。
核心 6 个用法(90% 够用)
journalctl -u sshd # 看某个服务的日志
journalctl -u kubelet -f # 跟随实时日志(类似 tail -f)
journalctl -u kubelet -n 100 # 最近 100 行
journalctl -u kubelet --since "10 min ago" # 时间过滤
journalctl -u kubelet -p err # 按级别过滤(err 及以上)
journalctl -b # 只看本次启动(reboot 后的)
时间过滤(最常用)
journalctl --since "10 min ago"
journalctl --since today
journalctl --since yesterday --until today
journalctl --since "2026-05-27 10:00:00" --until "2026-05-27 12:00:00"
journalctl --since "1 hour ago" --until "30 min ago"
时间格式宽容:
- 自然语言:
yesterday、today、tomorrow、now - 相对:
"10 min ago"、"1 hour ago"、"2 days ago" - 绝对:
"2026-05-27 10:00:00"或"2026-05-27"
按 unit 过滤
journalctl -u sshd # 单个 unit
journalctl -u sshd -u kubelet # 多个 unit
journalctl --user-unit foo # 用户级 unit
-u 可以多次出现。
通配:
journalctl -u 'kube*' # 所有 kube 开头的
加引号保护 shell 不展开。
按级别过滤
journald 用 syslog 级别(数字越小越紧急):
| 级别 | 数字 | 含义 |
|---|---|---|
| emerg | 0 | 系统不可用 |
| alert | 1 | 必须立即处理 |
| crit | 2 | 严重错误 |
| err | 3 | 错误 |
| warning | 4 | 警告 |
| notice | 5 | 提示 |
| info | 6 | 信息(默认显示这个及以下) |
| debug | 7 | 调试 |
journalctl -p err # err 及更紧急(err, crit, alert, emerg)
journalctl -p warning # warning 及更紧急
journalctl -p 3 # 数字写法等价 -p err
journalctl -p err..crit # 范围(err 到 crit)
排查节点故障常用:
journalctl -b -p err # 本次启动的所有 err
journalctl --since today -p err # 今天的所有 err
跟随实时日志
journalctl -u kubelet -f # 类似 tail -f
journalctl -u kubelet -f -n 50 # 先显示最近 50 行,再跟随
-f 持续输出新日志,Ctrl-C 退出。
启动 / boot 视角
journalctl -b # 本次启动的全部日志
journalctl -b -1 # 上一次启动(-1 = 倒数第一次)
journalctl -b -2 # 倒数第二次启动
journalctl --list-boots # 列出所有可查的 boot ID
排查"机器昨晚重启过吗、重启前/后日志":
journalctl --list-boots
# IDX BOOT ID FIRST ENTRY LAST ENTRY
# -2 abc123... 2026-05-25 09:00:00 UTC 2026-05-26 23:59:30 UTC
# -1 def456... 2026-05-27 00:00:10 UTC 2026-05-27 14:00:00 UTC ← 重启
# 0 ghi789... 2026-05-27 14:00:15 UTC (now)
journalctl -b -1 -p err --since "23:00" # 重启前最后那段时间的 err
journalctl -b 0 -p err -n 50 # 重启后的 err
结构化字段过滤
journald 把每条日志拆成多个字段(_SYSTEMD_UNIT、_PID、_HOSTNAME 等):
journalctl _PID=1234 # 某个 PID 的日志
journalctl _UID=1000 # 某用户的
journalctl _COMM=sshd # 进程名(命令)
journalctl _SYSTEMD_UNIT=kubelet.service # 等价于 -u kubelet
journalctl _TRANSPORT=kernel # 内核日志
看可用字段:
journalctl -F _SYSTEMD_UNIT # 列出所有 unit
journalctl -F _PID # 列出所有 PID
输出格式
journalctl -u sshd -o short # 默认,人类可读
journalctl -u sshd -o short-iso # ISO 8601 时间戳
journalctl -u sshd -o json # JSON(脚本用)
journalctl -u sshd -o json-pretty # 人类可读 JSON
journalctl -u sshd -o cat # 只输出消息,不带 timestamp / unit 前缀
-o cat 在 grep / 二次处理时很有用:
journalctl -u kubelet --since today -o cat | grep -c "out of memory"
-o json 配 jq 用:
journalctl -u kubelet --since "1 hour ago" -o json \
| jq -r 'select(.PRIORITY|tonumber < 4) | .MESSAGE'
kernel 日志(dmesg 的替代)
journalctl -k # 等价 dmesg
journalctl -k -b # 本次启动的内核日志
journalctl -k -b -p err # 本次启动的内核错误
内核 OOM kill / 磁盘错误 / 网卡 link 状态变化等都在这里。K8s 节点 OOM 杀 pod 常见:
journalctl -k -b | grep -i 'oom\|killed process'
journald 持久化
默认行为根据发行版不同:
- 有
/var/log/journal/目录 → 持久化(重启后日志保留) - 没这个目录 → 只在
/run/log/journal/(重启后丢)
排查重启前的问题时如果发现 journalctl -b -1 空,多半是没持久化。
启用持久化:
mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
systemctl restart systemd-journald
或者在 /etc/systemd/journald.conf 里:
[Journal]
Storage=persistent
限制 journal 大小(防磁盘爆)
journald 默认占根分区的 10%(最多 4G)。但有时还是会满:
journalctl --disk-usage # 看当前用了多少
# Archived and active journals take up 3.4G in the file system.
journalctl --vacuum-size=500M # 限制总大小 500M(删旧的直到达标)
journalctl --vacuum-time=7d # 只保留 7 天
journalctl --vacuum-files=10 # 最多保留 10 个文件
永久限制在 /etc/systemd/journald.conf:
[Journal]
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxRetentionSec=1week
改完 systemctl restart systemd-journald。
实战:服务挂了,怎么排查
# 1. 看状态(systemctl status 自带最近 10 行日志)
systemctl status myapp -l
# 2. 看完整日志
journalctl -u myapp -n 200 --no-pager
# 3. 看错误
journalctl -u myapp -p err
# 4. 看一段时间
journalctl -u myapp --since "30 min ago"
# 5. 看重启前后
journalctl -u myapp -b 0 --since "10 min ago" # 重启后
journalctl -u myapp -b -1 --since "10 min ago" # 重启前 10 分钟
实战:K8s 节点排错
# kubelet 状态
journalctl -u kubelet -f # 跟随
journalctl -u kubelet --since today -p err
# 容器运行时
journalctl -u containerd --since "1 hour ago"
journalctl -u docker --since "1 hour ago"
# 网络组件(CNI)
journalctl -u kube-proxy --since today
# 内核(OOM、磁盘、丢包等)
journalctl -k -b | grep -iE 'oom|drop|fail|error' | head
常见踩坑
坑 1:分页器卡死
journalctl -u kubelet
# 卡在 less 界面,按 q 退出,但很烦
加 --no-pager 直接输出到终端:
journalctl -u kubelet --no-pager
journalctl -u kubelet -n 100 --no-pager # 推荐套路
或者通过管道(自动 no-pager):
journalctl -u kubelet | head -100
或者一劳永逸:
export SYSTEMD_PAGER=cat # 全局禁用 pager
坑 2:找不到 -u 指定的 unit
journalctl -u ssh
# -- No entries --
unit 名拼错。可能是:
sshvssshd:Ubuntu 22 是ssh.service- 加
.service后缀更稳:journalctl -u ssh.service
可用 unit 列表:
journalctl -F _SYSTEMD_UNIT | sort -u | head
坑 3:重启后 -b -1 啥也没有
journald 没持久化。看 /var/log/journal/ 目录在不在。详见上面"journald 持久化"段。
坑 4:日志显示有截断([truncated])
journald 默认每条日志最大 64K。容器日志一行特别长会被截断。修改:
# /etc/systemd/journald.conf
[Journal]
LineMax=1M
但通常更好的是改应用 —— 单行日志超过 64K 通常是 JSON 一坨 stack trace,应该拆成多行或者结构化。
坑 5:journalctl 拒绝服务
hint: You are currently not seeing messages from other users.
普通用户默认只看自己的日志。要看全部:
sudo journalctl -u kubelet
或者把用户加入 systemd-journal 组:
sudo usermod -aG systemd-journal $USER
退出登录重进。
坑 6:磁盘满才发现 journal 撑爆
容器化负载长期跑、节点上没人清 → journal 爬到几个 G。直到 df -h 报满。
预防:
# /etc/systemd/journald.conf
[Journal]
SystemMaxUse=1G
MaxRetentionSec=2week
事后清理:
journalctl --vacuum-size=200M