KernelRanch
返回文章列表

systemd 与 journalctl 速查:unit 状态、失败重启计数、按时间与优先级过滤

2026-11-10 #运维

服务起不来或反复重启时,systemctl status 只给结论,细节全在 journal 里。我的固定动作是三步:systemctl status 看当前状态与最近几行,systemctl show 看重启计数与退出码,journalctl -u ... --since 把时间窗打开看完整上下文。三步之外还有两个隐藏开关会影响结论:journal 是否持久化、以及速率限制是否已经把日志丢掉了。

unit 状态与重启

目的命令读什么
当前状态systemctl status app.serviceActive: 一行(active/failed/inactive)、Main PID、末尾 10 行日志
是否失败systemctl is-failed app.service退出码 0 表示 failed,适合脚本判断
全机失败单元systemctl list-units --state=failed一次列出所有 failed,排查连带故障
重启次数与退出码systemctl show -p NRestarts -p ExecMainStatus -p Result app.serviceNRestarts 持续增长说明在重启循环
依赖关系systemctl list-dependencies app.service上游 target 未起来时服务不会启动
开机自启systemctl is-enabled app.serviceenabled/disabled/static
配置是否生效systemctl cat app.service显示所有 drop-in 合并前的原文
改了配置之后systemctl daemon-reload && systemctl restart app.service只 restart 不 reload 会用旧单元定义

NRestarts 是判断"重启循环"的直接证据。如果它每分钟涨几次,配合 journalctl -u app.service -n 50 就能看到每次退出前的最后一行。ExecMainStatus 给的是主进程退出码,被信号杀掉时 Result 会显示 signal,此时对照 dmesg 里的 OOM 记录能快速区分"自己退出"和"被杀"。

journalctl 过滤

目的命令
指定单元journalctl -u app.service
本次启动journalctl -b / 上次启动 journalctl -b -1
时间窗journalctl --since "2026-11-10 09:00:00" --until "2026-11-10 10:00:00"
相对时间journalctl --since "-30min" / --since today / --since yesterday
优先级journalctl -p err / 区间 -p warning..crit
正则搜`journalctl -g "timeoutrefused"`
内核消息journalctl -k -b
只看消息体journalctl -o cat -u app.service
结构化输出journalctl -o json-pretty -n 5 -u app.service
磁盘占用journalctl --disk-usage
清理到指定大小journalctl --vacuum-size=500M

-p 的语义容易记错:给单个级别时,显示的是该级别及更严重(数值更小)的所有消息,-p err 等价于 err、crit、alert、emerg 四档,不是只有 err。要看区间就写 -p err..debug 这种 FROM..TO 形式。

三个容易踩的开关

第一,journal 可能根本没落盘。Storage= 默认由编译期决定,auto 的行为是"/var/log/journal 存在就持久化,否则只写内存"。所以在 /run/log/journal 里翻历史,重启后就没了。判断方法很直接:ls -d /var/log/journal 存在才算持久化,journalctl --disk-usage 的数字也才有长期意义。

第二,速率限制会静默丢日志。RateLimitIntervalSec= 与 RateLimitBurst= 默认是 30 秒内 10000 条,超出的消息被丢弃,只在日志里留一条"suppressed"提示。刷屏的服务在故障时恰好是最需要日志的时候,这时候要把这两个值调大或置 0,并接受由此带来的磁盘增长。

第三,时间过滤要用 short-full 的格式核对。--since/--until 接受 2012-10-30 18:17:16 这种写法,省略时间部分会补 00:00:00,省略日期会补今天。输出默认是 short,它不带年份,肉眼对照容易错位;加 -o short-full 就能看到与过滤参数完全一致的时间戳。

unit 里最常改的几行

[Service]
Restart=on-failure          # 只在非零退出/被杀时重启
RestartSec=5s               # 重启间隔,太短会打满 journal
StartLimitIntervalSec=60    # 与 StartLimitBurst 配合,限制重启风暴
StartLimitBurst=5
ExecStart=/usr/local/bin/app --config /etc/app.conf

Restart=always 与 on-failure 的区别在"正常退出要不要拉起"。一次性任务用 Type=oneshot 且不要配 Restart,否则每次成功退出都会被当成需要重启。StartLimitBurst 是防止无限重启的最后一道闸:超过次数后 systemd 会把 unit 标记为 failed 并停止尝试,此时 NRestarts 停止增长,status 里会出现 "start request repeated too quickly" 这样的提示。

启动即退出的三步判断

第一步看 status 的 Active: 行与末尾日志,判断是"从没起来"还是"起来后退了"。第二步看 show -p ExecMainStatus -p Result,退出码 0 但状态 failed 通常意味着 Type= 配错(比如把常驻进程写成 oneshot);Result=signal 要去 dmesg 里找 OOM 或段错误。第三步才去看应用自己的日志文件——如果 ExecStart 的输出没有进 journal,说明进程自己重定向了 stdout,这时 journal 里只有 systemd 的启停记录,看不到应用输出,需要去应用配置的路径找。

排查顺序反过来做也能成立,但会多花时间:先翻应用日志往往看到的是"启动成功"然后戛然而止,根因(端口被占、配置文件权限、依赖服务未就绪)反而在 systemd 侧的状态里。

日志量与保留策略

journal 落盘后会一直增长,直到碰到上限。上限由 SystemMaxUse= 控制,默认是所在文件系统的 10%、且封顶 4G;单个文件的上限由 SystemMaxFileSize= 控制,默认是 SystemMaxUse= 的八分之一、封顶 128M。这两个默认值意味着:一个 500G 的数据盘上,journal 最多也只用到 4G 左右,不会出现"日志把盘写满"的情况——前提是持久化真的开着。

调整只需要一个 drop-in,不要直接改 /usr/lib 下的厂商文件:

mkdir -p /etc/systemd/journald.conf.d
printf '[Journal]\nSystemMaxUse=2G\nSystemMaxFileSize=256M\n' > /etc/systemd/journald.conf.d/99-size.conf
systemctl restart systemd-journald
journalctl --disk-usage

--vacuum-size= 只对已归档的文件生效,所以清理前先 --rotate 把当前活跃文件归档,回收量才对得上 --disk-usage 显示的差值。这一点手册里有明确提示,实际执行时很容易忽略,然后误以为"清了没用"。

小结

排查服务类故障的顺序建议固定为:先 list-units --state=failed 确认范围,再 status + show 拿到状态与重启计数,最后用 journalctl -u --since 拉时间窗。这套顺序的好处是每一步都有可复核的输出,写故障报告时直接把命令和输出贴进去即可。

需要留意的边界是容器。debian:12-slim 这类镜像里既没有 systemctl 也没有 journalctl,容器内 command -v systemctl 返回空,日志由容器运行时收集到别处(docker logs 或宿主 journal)。在容器里按文中命令操作会直接报 command not found,这属于环境边界,不是配置错误。

数据来源与检索时间

来源URL对应数据
man7.org · journalctl(1)https://man7.org/linux/man-pages/man1/journalctl.1.html--since/--until 时间语法、-p 单值与区间语义、-b、-u、-g、-o 输出格式、--disk-usage、--vacuum-size
man7.org · journald.conf(5)https://man7.org/linux/man-pages/man5/journald.conf.5.htmlStorage= 的 volatile/persistent/auto 行为、RateLimitIntervalSec/RateLimitBurst 默认 10000 条/30 秒、SystemMaxUse= 上限

检索时间:2026-11-10(Asia/Shanghai)。

核验说明

  • 真跑(环境边界确认):在 debian:12-slim 容器内执行 command -v systemctl 与 command -v journalctl 均无输出,确认该镜像不含 systemd,因此文中所有 systemctl/journalctl 命令未在本机执行。
  • 仅依据文档,未在本机运行:NRestarts/ExecMainStatus 的实际取值、-p err..crit 的区间过滤效果、--vacuum-size 的回收量、速率限制触发后的 suppressed 提示,均按上面两个手册页描述记录,未构造服务验证。
  • 未验证:Storage=auto 在 /var/log/journal 存在与不存在两种布局下的落盘差异,属文档明文但本机无法运行 systemd 验证。