摘要:一台预发布环境的 ClickHouse 服务器触发磁盘告警,40G 系统盘使用率飙到 81%。排查后发现,是 ClickHouse 的“日志预留机制”与系统日志刷屏互相放大,形成了恶性循环。本文记录完整的排查思路、根因分析和三步修复方案,最终使用率从 81% 降到 27%。
一、告警背景
某天收到监控告警:预发布环境一台 ClickHouse 服务器磁盘使用率超过 80%。这台机器是 40G 系统盘,运行着 clickhouse-server(systemd 服务,数据盘与系统盘分离)。
第一反应是“又该清日志了”——但这次远不止清理日志这么简单。
二、排查过程
1. 定位空间去向
40G 的盘,光 /var/log 就占了 20G。继续往下看:
构成很清晰:syslog 5.2G + syslog.1 11G + journal 3.9G ≈ 20G。
2. 查看 syslog 里到底在写什么
结果令人意外:100% 的内容都是 clickhouse-server 的异常堆栈,以极高的频率刷屏。其中反复出现两条关键错误:
堆栈路径:
3. 几个反常现象
进一步排查发现几个互相矛盾的线索:
- 服务并没有崩溃:
systemctl status clickhouse-server显示服务已连续运行 45 天,NRestarts=0,内存和 CPU 都正常。 - CK 自身的日志文件是 0 字节:
/var/log/clickhouse-server/clickhouse-server.log和err.log从几天前某时刻起一直是 0 字节——也就是说,CK 早就不再写自己的日志了。 - 操作系统层写入正常:以 clickhouse 用户身份做 shell 写入测试完全正常,说明不是文件系统权限或磁盘真的写满的问题。
4. 根因:ClickHouse 的“日志预留机制”
把这些线索串起来,真相浮出水面:
ClickHouse 有一个内置的磁盘预留机制(logger reservation):为了避免日志写满磁盘影响数据写入,CK 在写日志前会检查磁盘的“未预留空间”。当磁盘空闲空间低于某个水位时,CK 会拒绝写入自己的日志文件,抛出 Cannot add message to the log: Reserved 1.00 MiB ... 异常。
问题在于,这个异常本身也写不进日志文件,于是它被打到了 stderr → 被 systemd 捕获 → 进入 journald → rsyslog 转写到 syslog。而 syslog 和 journal 都在同一个 40G 系统盘上。
于是形成了一个恶性循环:
一天能增长 1.5G 的 syslog,就是这么来的。
5. 为什么现有防护没拦住
排查中还发现三个配置缺陷,让这个循环得以持续放大:
- 没有为 clickhouse 配置 logrotate:系统里根本没有
/etc/logrotate.d/clickhouse-server,CK 的日志文件没有大小上限和轮转策略。 - rsyslog 轮转频率不够:默认 weekly 轮转,扛不住每天 1.5G 的增长,syslog.1 攒到了 11G。
- journald 没有上限:未配置
SystemMaxUse,journal 目录涨到了 3.9G。
三、修复方案
分三步:先止血释放空间,再配置防护防止复发,最后重启验证。
第一步:释放磁盘空间
小技巧:用
truncate而不是rm清日志,可以避免 rsyslog 持有的文件句柄指向已删除文件(那样空间不会真正释放,直到进程重启)。truncate 后补一个 HUP,让 rsyslog 重新定位写位置即可。
第二步:配置日志轮转防护
给 CK 补上 logrotate:
把 rsyslog 轮转改成按天 + 按大小双触发:
journald 上限已经在第一步配置了 500M,与 logrotate 一起构成三道防线。
第三步:重启 clickhouse-server 并验证
验证四项:
| 验证项 | 结果 |
|---|---|
| 服务状态 | active (running) |
| CK 日志恢复写入 | clickhouse-server.log 从 0 字节恢复增长 |
| syslog 刷屏停止 | 10 秒增长 0 字节 |
| 客户端正常响应 | clickhouse-client --query "SELECT version()" 正常返回 |
最终磁盘状态:
81% → 27%,释放约 20G,告警解除。
四、经验总结
排查思路回顾
三条教训
- 日志刷屏的源头往往是“日志写不进日志”。当 syslog 里全是某个进程的异常堆栈时,优先怀疑该进程自身的日志通道出了问题(权限、磁盘预留、文件被删),而不是先去查业务逻辑错误。
- 日志系统本身必须有硬限额。logrotate(按大小)、journald SystemMaxUse、rsyslog maxsize 三道防线缺一不可,否则任何一次刷屏都可能滚雪球。
- 预发布环境同样要配置完整。这类机器的日志策略经常被忽略,但预发布流量并不小,出问题的概率和线上是同一量级。
预防清单
- 所有数据库组件(ClickHouse、MySQL 等)都配置 logrotate,按 size 触发
- journald 配置
SystemMaxUse(建议 500M 以内) - rsyslog 轮转使用 daily + maxsize 双触发
- 磁盘告警阈值设置在水位 80% 以下,留出排查时间
- 收到告警先查“日志占了多少”,/var/log 通常是第一嫌疑