堵住 journald 日志风暴:一次根分区写满的定位与根治

journald 因服务崩溃循环 + RateLimit 误配导致根分区 100% 写满,systemctl journalctl 全部卡死,排查出 StartLimit + RateLimitInterval 冲突与日志轮转缺失。

问题背景

2026 年 8 月底,公司核心堡垒机(跳板机)上线后,运维账号收口进入收尾阶段。所有生产服务器的直连 MySQL/SSH 端口已通过跳板白名单收敛,现场 DBA 与应用运维只能通过堡垒机操作。

9 月 1 日凌晨 03:17,监控告警突然涌入:「堡垒机根分区使用率 100%」「journalctl 命令卡死」「systemctl status 全部超时」。此时正值双节前变更窗口,任何服务中断都可能影响次日业务恢复演练。

堡垒机采用 Rocky Linux 9.4 + systemd 252,journald 默认配置,历史从未出现过日志写满问题。为什么突然在凌晨暴发?

故障现象

  1. 根分区写满df -h / 显示 /dev/mapper/rl-root 已用 48G/48G(100%),可用 0。
  2. journalctl 完全失效:执行 journalctl -u sshd 直接卡死,无任何输出,Ctrl+C 也无响应。
  3. systemctl 全部超时systemctl statussystemctl list-units 全部卡在「systemd-journald」等待。
  4. 堡垒机 Web 界面无法登录:堡垒机自身也通过 systemd 管理,journald 卡死导致认证日志无法写入,Web 登录接口直接 502。
  5. 仅影响堡垒机:其他生产服务器(MySQL/Redis/K8s 节点)均正常,确认是单机问题。

此时已无 SSH 登录堡垒机,只能通过 IPMI/iLO 进入紧急模式(单用户模式)排查。

排查过程

步骤 1:紧急模式下确认磁盘占用

通过 IPMI 进入单用户模式后,先检查大文件:

1
2
du -sh /var/log/journal/* 2>/dev/null | sort -h | tail -5
# 输出:48G    /var/log/journal/xxxxxxxx

确认罪魁祸首是 systemd journal 日志目录,占用整个根分区。

步骤 2:检查 journald 配置与 RateLimit

1
cat /etc/systemd/journald.conf

关键配置:

1
2
3
4
5
6
7
8
[Journal]
Storage=auto
Compress=yes
# RateLimitBurst=1000
# RateLimitInterval=30s
SystemMaxUse=4G
SystemKeepFree=1G
MaxRetentionSec=30day

RateLimit 已被注释!默认值 RateLimitBurst=10000RateLimitInterval=30s 形同虚设。

步骤 3:检查近期崩溃的服务

1
journalctl --list-boots | tail -10

发现 8 月 31 日 23:50 开始,堡垒机上的「堡垒机 Agent」(自研 Python 服务,负责审计日志转发与命令录像)出现连续崩溃:

1
2
3
4
systemctl status fortress-agent
# Active: failed (Result: start-limit-hit)
#   Process: 12345 ExecStart=/opt/fortress/agent.py (code=exited, status=1/FAILURE)
# Main PID: 12345 (code=exited, status=1/FAILURE)

该服务配置:

1
2
3
4
5
[Service]
Restart=always
RestartSec=1
StartLimitBurst=100
StartLimitIntervalSec=60

致命组合Restart=always + RestartSec=1 + StartLimitBurst=100 导致 60 秒内 100 次崩溃循环,每秒写 100+ 条 journal 日志,瞬间打满磁盘。

步骤 4:验证日志轮转缺失

1
2
ls -lh /var/log/journal/*/system.journal
# -rw-r----- 1 root root 48G Sep  1 03:15 system.journal

journald 默认只保留一个 system.journal,没有配置 SystemMaxFilesMaxFileSec,导致单文件无限增长。

解决方案

紧急止血(单用户模式)

  1. 停止崩溃服务,防止继续写日志:
1
2
systemctl stop fortress-agent
systemctl reset-failed fortress-agent
  1. 清理 journal 日志(保留最近 2 天):
1
2
journalctl --vacuum-time=2d
# 删除 46G 日志,根分区恢复可用空间 46G
  1. 重启 journald,使配置生效:
1
systemctl restart systemd-journald
  1. 验证 journalctl 恢复:
1
2
journalctl -u sshd --since "1 hour ago" | tail -5
# 正常输出日志

根治配置(持久化)

编辑 /etc/systemd/journald.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[Journal]
Storage=persistent
Compress=yes
# 速率限制(防日志风暴)
RateLimitBurst=1000
RateLimitInterval=30s
# 磁盘配额(单文件 + 总量)
SystemMaxUse=2G
SystemMaxFileSize=256M
SystemKeepFree=2G
MaxFileSec=1day
MaxRetentionSec=14day
# 防止单服务刷屏
ForwardToSyslog=no
ForwardToKMsg=no

编辑 /etc/systemd/system/fortress-agent.service(关键):

1
2
3
4
5
6
7
[Service]
Type=simple
ExecStart=/opt/fortress/agent.py
Restart=on-failure          # 改为 on-failure,exit 0/1 不自动重启
RestartSec=10               # 间隔拉长到 10 秒
StartLimitBurst=5
StartLimitIntervalSec=300   # 5 分钟内最多 5 次重启

重载 systemd 并重启服务:

1
2
3
systemctl daemon-reload
systemctl restart fortress-agent
systemctl enable fortress-agent

验证与监控

  1. 确认 journald 不再无限增长:
1
2
du -sh /var/log/journal
# 应稳定在 1.8G 左右(2G 配额)
  1. 添加 Prometheus 监控(堡垒机已有 node_exporter):
1
2
3
4
5
6
7
8
# /etc/prometheus/rules/fortress.rules
- alert: JournalDiskUsageHigh
  expr: node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} < 0.2
  for: 5m
  labels:
    severity: critical
  annotations:
    summary: "堡垒机根分区可用空间低于 20%"
  1. 堡垒机 Agent 增加健康检查与退出码规范:非致命错误(网络抖动)返回 0,致命错误(配置错误)返回 1,避免 Restart=always 无限循环。

根因分析

直接原因:堡垒机 Agent 在 8 月 31 日 23:50 因堡垒机白名单收口后「审计日志转发接口 403」导致持续崩溃,Restart=always + RestartSec=1 触发 100 次/分钟的 journal 写入风暴,单文件无上限增长打满根分区。

深层原因

  1. journald 默认配置「宽松」:RateLimit 被注释,SystemMaxUse 未限制,日志轮转缺失。
  2. 服务配置「激进」:Restart=always + RestartSec=1 在生产环境极度危险,一旦崩溃即日志雪崩。
  3. 缺少「变更门禁」:堡垒机 Agent 上线后,未在白名单收口场景下做兼容性测试,直接暴露在生产环境。

预防措施

  1. journald 硬配额:所有生产服务器统一配置 SystemMaxUse=2GMaxFileSec=1day,防止单服务刷屏。
  2. 服务重启策略收紧Restart=on-failure + RestartSec≥10 + StartLimitBurst=5,关键服务必须加 StartLimitAction=reboot(最后手段)。
  3. 日志监控前置:Prometheus + Alertmanager 监控 node_filesystem_avail_bytesjournalctl --disk-usage,阈值 80% 即告警。
  4. 变更演练门禁:堡垒机/Agent 类「基础设施变更」必须在「白名单收口」场景下做 48 小时灰度演练,确认无日志风暴后再全量。
  5. 应急手册:更新「根分区写满应急手册」,明确「单用户模式 → journalctl –vacuum-time → journald.conf 硬配额」三步走流程,演练频次每季度一次。

总结

一次「堡垒机 Agent 403 错误」引发的服务崩溃,本应是「小事」,却因 journald 配置缺失 + 服务重启策略激进,演变为「根分区写满 + 堡垒机全站瘫痪」的生产事故。

教训:基础设施服务(journald/systemd)必须「有上限、有节流、有监控」,任何「无限循环 + 无限制写入」都是定时炸弹。堡垒机作为「最后一道门」,更应在变更前做「最坏场景演练」。

下次堡垒机白名单收口变更前,将先执行「journald 配额 + Agent 重启策略收紧 + 48 小时灰度」三道门禁,确保不再重演。

使用 Hugo 构建
主题 StackJimmy 设计