问题背景
2026 年 9 月初,公司在 DC 域控服务器(Windows Server 2022 + Hyper-V 虚拟化)上部署一套基于 Debian 12 的监控与日志采集节点,用于集中收集 PVS 服务器与无盘终端的运行日志与性能指标。系统上线后前两周运行平稳,journald 默认配置(RateLimitIntervalSec=30s、RateLimitBurst=10000)被认为「足够宽松」。
9 月 7 日凌晨 03:12 开始,监控大盘突然出现大量「node down」告警,SSH 登录延迟剧增,部分服务开始 OOM。运维值班同学通过 IPMI 远程控制台发现根分区使用率已达 100%,journalctl -f 刷屏输出,系统进入只读模式无法写入任何文件。紧急重启后服务逐步恢复,但根分区使用率在 10 分钟内再次冲顶,呈现典型的「日志风暴」特征。
本次故障波及范围:监控节点本身 + 通过该节点上报的 3 台 PVS 服务器(间接影响 200+ 无盘终端的性能数据采集)。故障持续约 47 分钟,业务影响面为「监控盲区 + 性能数据丢失」。
故障现象
- 根分区瞬间写满:
df -h /显示/dev/mapper/vg-root使用率从 68% → 100%(用时 8 分钟),可用空间从 12 GB → 0。 - journalctl 刷屏:
journalctl -f输出频率超过每秒 200 行,主要为kernel: [UFW BLOCK]与sshd[xxxx]: Failed password for invalid user(疑似外部扫描)。 - RateLimit 告警:
journalctl --since "1 hour ago" | grep -i "rate limit"出现大量journal: Suppressed X messages from Y提示,说明 RateLimit 已触发但仍无法抑制刷盘。 - 服务雪崩:
systemctl list-units --failed显示node-exporter、promtail、sshd相继失败,根因是No space left on device导致 journal 无法写入,进而触发 systemd 服务启动失败。 - 磁盘 I/O 飙升:
iostat -x 1显示sda的%util长期维持在 95% 以上,await从 12 ms → 380 ms,确认是「写盘速度远超磁盘能力」。
排查过程
步骤 1:定位日志堆积源头
|
|
默认情况下 journald 采用 Storage=auto,当根分区可用空间 < 4G 时会自动切换到 volatile(内存),但本次故障中「可用空间被日志本身吃光」,导致切换失败。
步骤 2:分析 RateLimit 配置
|
|
关键发现:
RateLimitBurst=10000看似很大,但在「每秒 200 行」的刷屏场景下,30 秒内实际产生的日志量远超 10000 条。RateLimitIntervalSec=30s意味着每 30 秒才重置一次计数器,攻击/扫描流量在 30 秒内持续产生日志,RateLimit 形同虚设。SystemMaxUse=为空,journald 默认按「根分区 10%」计算上限(约 12 GB),但实际已写满 47 GB,说明「日志轮转/清理策略未生效」。
步骤 3:确认日志来源与内容
|
|
日志内容以 UFW BLOCK(防火墙拦截记录)与 sshd Failed password 为主,属于「外部扫描 + 内部日志未聚合」导致的「日志放大攻击」。
步骤 4:复现日志风暴
在测试环境模拟 journalctl 高频写入:
|
|
复现结果:30 秒内写入 1.2 GB 日志,确认「磁盘 I/O 能力(约 80 MB/s)远低于日志产生速度」。
解决方案
立即止血(不重启)
|
|
执行后 3 分钟内根分区使用率从 100% → 71%,服务逐步恢复。
根治配置(持久化)
|
|
配套监控与告警
在 Prometheus node-exporter 中增加 journal 监控:
|
|
根因分析
- RateLimit 配置过宽:默认
RateLimitBurst=10000 / 30s在「每秒 200 行」的扫描场景下完全失效,日志以 80 MB/s 的速度持续写入磁盘。 - SystemMaxUse 为空:journald 默认按「根分区 10%」计算上限,但当日志本身吃光可用空间时,清理策略无法触发,形成「死循环」。
- 日志来源未收敛:UFW 与 sshd 日志未做聚合/限流,外部扫描流量被完整记录到 journal,导致「日志放大」。
- 磁盘 I/O 能力不足:根分区使用普通 SATA SSD(顺序写 ~120 MB/s),在「日志风暴」场景下
await飙升至 380 ms,服务因无法写入 journal 而 OOM。
预防措施
-
journald 配置基线:
- 所有生产服务器必须显式设置
SystemMaxUse=4G、RateLimitBurst=2000。 - 定期执行
journalctl --vacuum-time=7d作为 cron 任务(防止配置漂移)。
- 所有生产服务器必须显式设置
-
日志聚合前置:
- 将高频日志(UFW、sshd、kernel)通过
rsyslog或fluent-bit聚合到独立日志服务器,journald 仅保留「结构化 + 关键事件」。 - 在防火墙/主机上启用
fail2ban或sshguard,从源头减少无效日志产生。
- 将高频日志(UFW、sshd、kernel)通过
-
监控与告警前置:
- 增加
JournaldDiskUsageHigh与JournaldRateLimitTriggered告警,提前 15 分钟发现异常。 - 在 Grafana 中增加「journal 日志增长速率」面板,设置阈值告警。
- 增加
-
变更验收 Checklist:
- 新增「journald 配置 review」项:RateLimit 是否收紧?SystemMaxUse 是否显式设置?日志是否聚合?
- 变更后执行「日志风暴演练」:模拟每秒 300 行日志,验证 RateLimit 是否生效、根分区是否写满。
-
根分区隔离:
- 未来服务器部署时,将
/var/log/journal单独挂载到独立 LV 或 NFS,防止日志吃光根分区导致系统只读。
- 未来服务器部署时,将
总结
本次故障的本质是「日志产生速度远超磁盘 I/O 能力 + RateLimit 配置过宽 + SystemMaxUse 为空」三重因素叠加,导致 journald 日志风暴吃光根分区,进而引发服务雪崩。排查过程通过 du 定位日志目录、journalctl 分析 RateLimit 配置、iostat 确认 I/O 瓶颈,最终通过「立即止血(vacuum + 重启 journald)」与「根治配置(SystemMaxUse=4G + RateLimitBurst=2000)」双管齐下恢复服务。
关键教训:
- journald 默认配置在「高频日志场景」下并不安全,必须显式收紧 RateLimit 与 SystemMaxUse。
- 监控告警必须覆盖「日志增长速率」而非仅「磁盘使用率」,否则故障发现时已晚。
- 变更验收 Checklist 必须包含「日志配置 review」,防止「默认配置」带来的隐性风险。
后续将把「journald 日志风暴演练」纳入每月变更验收门禁,并更新「服务器暴露面收敛 Checklist」与「服务健康检查 Checklist」,确保同类问题不再复发。