问题背景
某生产服务器(CentOS 7.9,systemd 219)在凌晨 03:17 触发 Zabbix 告警:「根分区使用率 92%(阈值 85%)」。运维值班人员通过 SSH 登录后发现 /var/log/journal 目录已占用 47GB,且仍在持续增长。正常情况下该服务器日志量每日约 200-300MB,根分区(/)总容量 100GB,理论上不应在 4 小时内被日志占满。
此前一周该服务器刚完成「journald 配置优化」变更(工单 #OPS-2026-0918),变更内容包括将 Storage 从 volatile 调整为 auto,并设置 SystemMaxUse=2G、RuntimeMaxUse=500M。变更后监控显示日志目录大小稳定在 1.8GB 左右,变更验收通过。但 5 天后再次出现日志风暴,根因指向配置被 override 机制覆盖。
故障现象
-
磁盘空间告警:03:17 Zabbix 触发根分区使用率告警,SSH 登录后
df -h /显示:1 2Filesystem Size Used Avail Use% Mounted on /dev/sda2 100G 92G 8.0G 92% //var/log/journal目录实际占用 47GB(du -sh /var/log/journal)。 -
journald 进程 CPU 飙升:
top显示systemd-journal进程 CPU 使用率 180%(双核),且journalctl --disk-usage命令卡死 30 秒以上才返回结果。 -
日志写入异常:
journalctl -n 100能正常输出最近 100 条日志,但-f跟进模式下日志条目每秒约 1200 条,且大量重复的audit: type=1400SELinux AVC denied 消息(源头是某容器化应用误配 SELinux 策略)。 -
配置检查异常:
journalctl --verify报错「File corruption detected at …」,且systemctl status systemd-journald显示服务处于active (running)但有 3 次Watchdog timeout重启记录。
排查过程
步骤 1:确认日志增长源头
首先定位日志增长最快的 unit:
|
|
发现 audit 相关的日志条目占比超过 78%,且时间戳集中在 02:58-03:15 之间每秒约 800-1200 条。
步骤 2:检查 journald 配置与 override 机制
按变更记录检查 /etc/systemd/journald.conf:
|
|
但 systemctl cat systemd-journald 显示实际生效配置与预期不符:
|
|
该 99-custom.conf 文件创建时间为 2026-09-20 14:22,恰好是「journald 配置优化」变更的第 3 天。经核查,该文件由另一运维同事在处理「日志归档需求」时手动创建,未走变更流程。
步骤 3:验证 override 优先级与生效顺序
systemd 配置加载顺序为:
/etc/systemd/journald.conf(主配置)/etc/systemd/journald.conf.d/*.conf(按文件名 ASCII 排序)/run/systemd/journald.conf.d/*.conf(运行时配置,优先级最高)/usr/lib/systemd/journald.conf.d/*.conf(包自带配置,优先级最低)
99-custom.conf 的 99- 前缀使其在 ASCII 排序中排在最后,因此覆盖了主配置中的 SystemMaxUse=2G。同时 Storage=persistent 强制 journald 将日志写入 /var/log/journal 而非 /run/log/journal(tmpfs),导致根分区持续被写入。
步骤 4:定位日志风暴的业务源头
确认配置问题后,进一步排查日志来源:
|
|
输出显示 SELinux AVC denied 消息来自容器 app-frontend(Kubernetes Pod),该容器在 02:58 因滚动更新触发大量文件访问,SELinux 策略误配导致每秒产生约 800 条 audit 日志。
步骤 5:验证 journald Watchdog 与文件损坏
journalctl --verify 报错的根因是 journal 文件在磁盘空间耗尽前被 journald 强制截断,导致索引损坏:
|
|
解决方案
立即止血(不重启服务)
-
临时限制 journald 写入:
1 2 3# 通过 systemd-run 临时覆盖配置 systemctl kill --signal=SIGUSR1 systemd-journald # 触发 journald 立即执行日志轮转与裁剪 -
手动清理历史日志(保留最近 7 天):
1 2journalctl --vacuum-time=7d # 输出:Vacuuming done, freed 41.3G of archived journals -
删除异常 override 配置:
1 2 3rm -f /etc/systemd/journald.conf.d/99-custom.conf systemctl daemon-reload systemctl restart systemd-journald
根治措施
-
规范化 journald 配置(删除所有 override,统一主配置):
1 2 3 4 5 6 7 8 9# /etc/systemd/journald.conf [Journal] Storage=auto SystemMaxUse=2G RuntimeMaxUse=500M SystemMaxFileSec=1week MaxRetentionSec=4weeks ForwardToSyslog=no Audit=yes -
建立配置变更门禁:
- 所有
/etc/systemd/*.conf.d/目录变更必须走变更流程 - 变更单需包含「配置加载优先级验证」环节(
systemctl cat截图) - 新增自动化检查脚本(每日 06:00 执行):
1 2 3 4 5 6#!/bin/bash # check_journald_override.sh if ls /etc/systemd/journald.conf.d/*.conf 2>/dev/null | grep -q .; then echo "[WARN] journald override 配置存在,请检查" ls -l /etc/systemd/journald.conf.d/ fi
- 所有
-
修复 SELinux 策略(业务侧):
- 为
app-frontend容器生成自定义 SELinux 策略:1 2ausearch -m avc -ts recent | audit2allow -M myapp semodule -i myapp.pp - 或在 Deployment 中添加
securityContext.seLinuxOptions放行必要权限。
- 为
-
监控增强:
- 新增 Prometheus 指标
node_filesystem_avail_bytes阈值告警(<15% 触发 P2) - 新增
journalctl --disk-usage每日巡检脚本,输出到运维群
- 新增 Prometheus 指标
根因分析
直接原因:/etc/systemd/journald.conf.d/99-custom.conf 的 SystemMaxUse=50G 覆盖了主配置的 2G 限制,且 Storage=persistent 强制日志写入根分区而非 tmpfs,导致 SELinux AVC 日志风暴直接耗尽根分区。
间接原因:
- 变更流程缺失「配置 override 检查」环节,允许运维人员在
/etc/systemd/*.conf.d/目录创建高优先级配置文件。 - journald 的
Storage=auto行为依赖/var/log/journal目录是否存在:若目录不存在则使用volatile(tmpfs),若存在则使用persistent。变更时未清理旧目录,导致配置切换后日志持续写入根分区。 - SELinux 策略误配导致 audit 日志量激增 40 倍,叠加 journald 配置错误,4 小时内产生 47GB 日志。
配置加载优先级陷阱:systemd 的 *.conf.d/ 目录机制设计初衷是「局部覆盖」,但文件名 ASCII 排序导致 99-*.conf 总是最后加载,极易产生「看似主配置生效,实际被 override」的隐蔽故障。
预防措施
- 配置即代码:将
/etc/systemd/journald.conf纳入 Ansible/ SaltStack 管理,禁止手工修改*.conf.d/目录。 - 变更 checklist 增加「override 扫描」:
- 变更前执行
systemctl cat <unit>并截图存档 - 变更后执行
systemd-analyze cat-config <unit>验证最终生效配置
- 变更前执行
- 监控前置:
- 新增告警规则:
journalctl --disk-usage | awk '{print $7}' > 5G触发 P2 告警 - 新增日志速率监控:
journalctl --since "1 minute ago" | wc -l > 1000触发 P3 告警(防日志风暴)
- 新增告警规则:
- 定期演练:每季度执行一次「journald 日志轮转与裁剪演练」,验证
journalctl --vacuum-time与SystemMaxUse的实际效果。 - SELinux 策略治理:建立「容器 SELinux 策略清单」,所有新上线容器必须通过
audit2allow生成策略并 review,避免 AVC denied 日志泛滥。
总结
本次故障的根因是「配置 override 机制被忽视 + 变更流程缺失 override 检查」,导致看似「已优化」的 journald 配置实际被高优先级文件覆盖。journald 的 Storage=auto 与 SystemMaxUse 在 override 场景下表现隐蔽,极易产生「配置已生效但实际未生效」的错觉。
教训:
- systemd 的
*.conf.d/目录是「双刃剑」:方便局部定制,但优先级陷阱极易导致配置漂移。 - 任何涉及「日志/监控/告警」的配置变更,必须在变更单中明确「配置加载验证」环节。
- 监控指标应覆盖「配置实际生效值」(如
journalctl --disk-usage),而非仅依赖「配置已下发」。
延伸阅读: