一次 ClickHouse 服务器磁盘告警的排查与修复实录

摘要:一台预发布环境的 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. 几个反常现象

进一步排查发现几个互相矛盾的线索:

  1. 服务并没有崩溃 :systemctl status clickhouse-server 显示服务已连续运行 45 天,NRestarts=0,内存和 CPU 都正常。
  2. CK 自身的日志文件是 0 字节 :/var/log/clickhouse-server/clickhouse-server.log 和 err.log 从几天前某时刻起一直是 0 字节------也就是说,CK 早就不再写自己的日志了。
  3. 操作系统层写入正常:以 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. 为什么现有防护没拦住

排查中还发现三个配置缺陷,让这个循环得以持续放大:

  1. 没有为 clickhouse 配置 logrotate :系统里根本没有 /etc/logrotate.d/clickhouse-server,CK 的日志文件没有大小上限和轮转策略。
  2. rsyslog 轮转频率不够:默认 weekly 轮转,扛不住每天 1.5G 的增长,syslog.1 攒到了 11G。
  3. 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 上限)

三条教训

  1. 日志刷屏的源头往往是"日志写不进日志"。当 syslog 里全是某个进程的异常堆栈时,优先怀疑该进程自身的日志通道出了问题(权限、磁盘预留、文件被删),而不是先去查业务逻辑错误。
  2. 日志系统本身必须有硬限额。logrotate(按大小)、journald SystemMaxUse、rsyslog maxsize 三道防线缺一不可,否则任何一次刷屏都可能滚雪球。
  3. 预发布环境同样要配置完整。这类机器的日志策略经常被忽略,但预发布流量并不小,出问题的概率和线上是同一量级。

预防清单

  • 所有数据库组件(ClickHouse、MySQL 等)都配置 logrotate,按 size 触发
  • journald 配置 SystemMaxUse(建议 500M 以内)
  • rsyslog 轮转使用 daily + maxsize 双触发
  • 磁盘告警阈值设置在水位 80% 以下,留出排查时间
  • 收到告警先查"日志占了多少",/var/log 通常是第一嫌疑

Webfunny全链路监控埋点平台是一站式前端监控 + 用户行为埋点 + 大数据分析平台,天然适配点位细查、用户行为回溯、批量导出等场景:

一体化架构:监控 + 埋点同一套 SDK,数据互通无壁垒

私有化部署:数据完全本地化,满足企业合规要求

高吞吐支撑:基于 ClickHouse 构建,亿级日志秒级查询

全端覆盖:H5 / 小程序 / APP / 鸿蒙全覆盖,统一导出口径

可定制强:支持接口扩展、分布式锁、限流降级等企业级能力

参考文档:Docker部署ClickHouse clickhouse-backup完整备份&恢复完整方案 - Webfunny

相关推荐
自由彩虹1 小时前
靶场通关16--victim1
linux·服务器·网络·安全·web安全·网络安全
Thomas.Sir1 小时前
第11课:文件上传下载、跨域CORS配置,SpringBoot4.x跨域新方案
java·spring boot
wuminyu1 小时前
JVM规范学习总结:class执行性能差异
java·linux·c语言·jvm·c++
java_nnnn1 小时前
spring web mvc图书管理小案例
前端·spring·mvc
java1234_小锋1 小时前
【技术专题】CSS3 网页技术 - CSS3 基础样式
android·前端·css3
路由侠内网穿透.1 小时前
本地部署开源 WSGI HTTP 服务器 Gunicorn 并实现外部访问
运维·服务器·网络·网络协议·http·开源
一条泥憨鱼1 小时前
【RabbitMQ】高级篇
java·分布式·rabbitmq·php·ruby
畅想视界ThinkView1 小时前
病区护士站触控终端选型指南:分诊叫号与护理交互终端怎么挑
前端·microsoft·交互