如何防止 json-file 驱动打爆宿主机磁盘
解读
在国内生产环境,容器日志默认走 json-file 驱动,直接写宿主机 /var/lib/docker/containers//-json.log。一旦应用日志量失控(如大促、死循环、DEBUG 全开),单个文件可达数十 GB,把系统盘撑满触发 No space left on device,导致 kubelet/docker 异常、Pod 被驱逐甚至节点 NotReady。面试官想确认你是否具备“事前容量规划 + 事中实时治理 + 事后兜底清理”的全链路闭环能力,而不仅仅是“切到 journald”这种一句话答案。
知识点
- json-file 驱动的写盘机制:每行日志追加到本地 JSON 文件,无自动压缩、无自动清理。
- Docker 日志轮转参数:max-size、max-file、compress、labels 等,只能在容器创建时生效,运行时不可热更新。
- 国内云厂商系统盘普遍 40~100 GiB,日志与镜像、kubelet 复用同一块盘,极易竞争。
- logrotate 与 Docker 轮转并存时,双锁竞争可能丢日志或切出空文件。
- 合规要求:金融、运营商场景要求日志留存 ≥6 个月,不能简单 rm -f,必须转储到 S3/OSS/日志平台后再清理。
- 内核参数:fs.inotify.max_user_watches 不足会导致 filebeat 侧漏采,日志堆积后二次打盘。
答案
我采用“三层防线”策略,已在 20+ 生产集群落地,实现全年零因日志打盘故障:
-
全局默认模板
在 /etc/docker/daemon.json 统一注入日志驱动参数,强制所有新建容器生效:
{ "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "5", "compress": "true", "labels": "app,env,team", "env": "LOG_LEVEL" } }
随后 systemctl reload docker,reload 不中断已运行容器,但新容器立即受控。 -
存量热治理
对未设置限额的旧容器,使用自研脚本 docker-log-rotate.sh:- 通过 docker inspect 拿到日志路径,利用 copytruncate + flock 避免与业务写冲突;
- 按 50 MB 切分,gzip 压缩后上传到内网 MinIO,返回生命周期 7 天自动沉降;
- 上传成功再执行 echo '' > log.log 清空头文件,秒级释放磁盘空间;
- 整体对容器无重启、无感知。
-
兜底监控与告警
- Prometheus + node_exporter 采集 (docker_container_log_size_bytes / 1024 / 1024) > 80 立即告警到飞书;
- 配套 Grafana 面板按 app、node、team 维度聚合,值班同学 5 分钟内可定位 top10 容器;
- 磁盘使用率 >85% 触发自动扩容云盘,阿里云 ESSD 支持在线扩容无需停机;
- 每周巡检脚本扫描大于 30 天未访问的 *.log.gz,自动提交解冻-删除工单,满足审计要求。
通过以上三层,把日志增长从不可控变为可预测,系统盘年增长率 <5%,彻底杜绝“日志打爆”事件。
拓展思考
-
若公司强制使用 journald,如何兼容存量 filebeat 配置?
答:可在 docker-compose 里给业务容器加 logging.driver=journald 并挂载 /run/log/journal,filebeat 通过 journald input 读取,注意字段映射与 multiline 正则保持一致。 -
对短时 Job 容器,json-file 轮转意义不大,更优方案是 gelf 或 fluentd 驱动直接走 UDP 到外置日志栈,容器退出即自动释放空间,但需评估网络抖动导致丢日志的风险。
-
在边缘节点(2C4G 盒子)上,磁盘仅 32 GB,可关闭容器日志落盘:
docker run --log-driver=none ...
业务日志通过 stdout -> memory buffer -> MQTT 上传到中心,需预留 200 MB 内存给 ring-buffer,并做 QoS1 重传,防止日志丢失。