摘要:一台预发布环境的 ClickHouse 服务器触发磁盘告警,40G 系统盘使用率飙到 81%。排查后发现,是 ClickHouse 的"日志预留机制"与系统日志刷屏互相放大,形成了恶性循环。本文记录完整的排查思路、根因分析和三步修复方案,最终使用率从 81% 降到 27%。
一、告警背景
某天收到监控告警:预发布环境一台 ClickHouse 服务器磁盘使用率超过 80%。这台机器是 40G 系统盘,运行着 clickhouse-server(systemd 服务,数据盘与系统盘分离)。
第一反应是"又该清日志了"------但这次远不止清理日志这么简单。
二、排查过程
1. 定位空间去向
bash
df -h /
# /dev/vda3 40G 30G 7.3G 81% /
du -sh /var/log
20G /var/log
40G 的盘,光 /var/log 就占了 20G。继续往下看:
bash
ls -lh /var/log/syslog /var/log/syslog.1
# -rw-r----- 1 syslog adm 5.2G /var/log/syslog
# -rw-r----- 1 syslog adm 11G /var/log/syslog.1
journalctl --disk-usage
Archived and active journals take up 3.9G in the file system.
构成很清晰:syslog 5.2G + syslog.1 11G + journal 3.9G ≈ 20G。
2. 查看 syslog 里到底在写什么
bash
tail -100 /var/log/syslog
结果令人意外:100% 的内容都是 clickhouse-server 的异常堆栈,以极高的频率刷屏。其中反复出现两条关键错误:
Poco::Exception. Code: 1000, e.code() = 0,
File access error: /var/log/clickhouse-server/clickhouse-server.log
Cannot add message to the log: Reserved 1.00 MiB on local disk `default`,
having unreserved 7.48 GiB
堆栈路径:
AsynchronousMetrics::update
→ OwnSplitChannel::log
→ Poco::FileChannel::log
→ RotateBySizeStrategy::mustRotate
→ FileException
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 系统盘上。
于是形成了一个恶性循环:
磁盘空闲低
→ CK 拒绝写自身日志(预留机制)
→ 异常堆栈打到 stderr
→ journald / syslog 疯狂增长
→ 磁盘空闲更低
→ 刷屏更凶
→ ......循环
一天能增长 1.5G 的 syslog,就是这么来的。
5. 为什么现有防护没拦住
排查中还发现三个配置缺陷,让这个循环得以持续放大:
- 没有为 clickhouse 配置 logrotate :系统里根本没有
/etc/logrotate.d/clickhouse-server,CK 的日志文件没有大小上限和轮转策略。 - rsyslog 轮转频率不够:默认 weekly 轮转,扛不住每天 1.5G 的增长,syslog.1 攒到了 11G。
- journald 没有上限 :未配置
SystemMaxUse,journal 目录涨到了 3.9G。
三、修复方案
分三步:先止血释放空间,再配置防护防止复发,最后重启验证。
第一步:释放磁盘空间
bash
# 清空 syslog(truncate 保留 inode,rsyslog 文件句柄不受影响)
truncate -s 0 /var/log/syslog.1
truncate -s 0 /var/log/syslog
systemctl kill -s HUP rsyslog.service # 通知 rsyslog 刷新文件句柄
journald 限制到 500M
echo 'SystemMaxUse=500M' >> /etc/systemd/journald.conf
systemctl restart systemd-journald # 重启后自动清理到上限以下
journalctl --disk-usage # 3.9G → 413.8M
小技巧:用
truncate而不是rm清日志,可以避免 rsyslog 持有的文件句柄指向已删除文件(那样空间不会真正释放,直到进程重启)。truncate 后补一个 HUP,让 rsyslog 重新定位写位置即可。
第二步:配置日志轮转防护
给 CK 补上 logrotate:
bash
cat > /etc/logrotate.d/clickhouse-server <<'EOF'
/var/log/clickhouse-server/*.log {
size 100M
rotate 5
missingok
notifempty
compress
delaycompress
copytruncate
}
EOF
把 rsyslog 轮转改成按天 + 按大小双触发:
bash
# /etc/logrotate.d/rsyslog 中:
# weekly → daily
# 增加 maxsize 1G
sed -i 's/^\([[:space:]]*\)weekly/\1daily/' /etc/logrotate.d/rsyslog
sed -i '/^[[:space:]]*rotate /a\ maxsize 1G' /etc/logrotate.d/rsyslog
journald 上限已经在第一步配置了 500M,与 logrotate 一起构成三道防线。
第三步:重启 clickhouse-server 并验证
bash
systemctl restart clickhouse-server
sleep 20
systemctl status clickhouse-server --no-pager -l
验证四项:
| 验证项 | 结果 |
|---|---|
| 服务状态 | active (running) |
| CK 日志恢复写入 | clickhouse-server.log 从 0 字节恢复增长 |
| syslog 刷屏停止 | 10 秒增长 0 字节 |
| 客户端正常响应 | clickhouse-client --query "SELECT version()" 正常返回 |
最终磁盘状态:
/dev/vda3 40G 9.8G 28G 27% /
479M /var/log
81% → 27%,释放约 20G,告警解除。
四、经验总结
排查思路回顾
磁盘告警
→ df 看整体使用率
→ du 逐层定位大头目录(/var/log 占 20G)
→ 看日志内容(syslog 被什么刷屏)
→ 找刷屏源头(clickhouse-server 异常堆栈)
→ 反查刷屏原因(CK 日志预留机制拒绝写日志)
→ 检查防护配置缺口(logrotate / journald 上限)
三条教训
- 日志刷屏的源头往往是"日志写不进日志"。当 syslog 里全是某个进程的异常堆栈时,优先怀疑该进程自身的日志通道出了问题(权限、磁盘预留、文件被删),而不是先去查业务逻辑错误。
- 日志系统本身必须有硬限额。logrotate(按大小)、journald SystemMaxUse、rsyslog maxsize 三道防线缺一不可,否则任何一次刷屏都可能滚雪球。
- 预发布环境同样要配置完整。这类机器的日志策略经常被忽略,但预发布流量并不小,出问题的概率和线上是同一量级。
预防清单
- 所有数据库组件(ClickHouse、MySQL 等)都配置 logrotate,按 size 触发
- journald 配置
SystemMaxUse(建议 500M 以内) - rsyslog 轮转使用 daily + maxsize 双触发
- 磁盘告警阈值设置在水位 80% 以下,留出排查时间
- 收到告警先查"日志占了多少",/var/log 通常是第一嫌疑
Webfunny全链路监控埋点平台是一站式前端监控 + 用户行为埋点 + 大数据分析平台,天然适配点位细查、用户行为回溯、批量导出等场景:
一体化架构:监控 + 埋点同一套 SDK,数据互通无壁垒
私有化部署:数据完全本地化,满足企业合规要求
高吞吐支撑:基于 ClickHouse 构建,亿级日志秒级查询
全端覆盖:H5 / 小程序 / APP / 鸿蒙全覆盖,统一导出口径
可定制强:支持接口扩展、分布式锁、限流降级等企业级能力
参考文档:Docker部署ClickHouse clickhouse-backup完整备份&恢复完整方案 - Webfunny