Docker存储清理与防膨胀实践

Docker 存储清理与防膨胀实践

背景:线上 40G 系统盘某天突然 100% 写满,导致 MariaDB 写入抛 error 1114 The table 'article' is full,周报落库失败。经排查,根因是日志/构建缓存堆积 + 已删除文件仍被进程持有句柄。本文记录排查思路、清理手段以及长效防御措施。

一、问题表象与根因定位

1.1 表象

  • 应用日志:java.sql.SQLException: The table 'article' is full(MySQL error 1114)
  • 服务器:df -h / 显示 40G 盘已用 39G,Avail 0

1.2 易错点:InnoDB 表空间没有硬上限

MySQL/MariaDB 的 error 1114 直觉上像是"表大小受限",但 InnoDB 默认自扩展表空间(innodb_data_file_path 未指定 max)没有硬上限。出现此错误基本等价于磁盘或 inode 耗尽 ,导致表空间文件无法继续扩展。先查磁盘,再查数据库。

1.3 定位思路:df 与 du 不一致的经典陷阱

bash 复制代码
df -h /          # 文件系统视角:看挂载点总容量
du -sh /var/*    # 目录项视角:累加可见文件

如果 df 显示已用 35G,但 du 只统计到 20G,中间差的 15G 就是已删除但仍被进程持有句柄的文件。常见来源:

  • 进程(如 Java、Gradle Daemon、Nginx)打开着的日志被 rm 掉
  • Docker 容器内进程持有的文件

定位方法:

bash 复制代码
lsof +L1 | grep deleted        # 列出所有"已删除但仍有进程打开"的文件

释放方法:杀掉持有句柄的进程,或重启对应容器:

bash 复制代码
pkill -9 -f GradleDaemon       # 例:释放被删的 .gradle 缓存
docker compose restart nginx nacos redis service

二、一次性清理操作(按安全度从高到低)

2.1 应用日志(bind-mount 目录)

项目中 nginx / nacos / redis 的日志通过 ./logs/* bind mount 到宿主机,不受 Docker json-file 日志驱动的 max-size 限制,会无限增长。

bash 复制代码
cd /data/jiahetng
du -sh ./logs/*                        # 先看各服务占用
find ./logs/nginx -name "*.log" -mtime +7 -delete
find ./logs/nacos -name "*.log" -mtime +7 -delete
find ./logs/redis -name "*.log" -mtime +7 -delete

2.2 Docker 自身资源

bash 复制代码
docker system df                     # 查看镜像/容器/卷/构建缓存占用
docker image prune -a -f             # 删除未被使用的镜像
docker builder prune -a -f           # 清理构建缓存(buildkit 多阶段构建的中间层)
docker volume prune -f               # 删除未被容器使用的 volume(⚠️ 确认无重要数据)

docker system prune -a -f 是上面几条的组合拳,但会同时删未使用的镜像和停止的容器,确认后再用。

2.3 宿主机残留

  • /root/.gradle:CI/CD 或本地构建残留的依赖缓存(本次 15G)
  • /var/log/.osbak:云厂商 OS 备份残留(本次 6.4G),确认内容后 rm -rf
  • journalctl --vacuum-size=500M:systemd 日志(若在 /var/log/journal 而非 tmpfs)

三、长效防御:让存储不再膨胀

3.1 容器日志轮转(json-file 驱动)

在 docker-compose.yaml 每个服务下统一配置:

yaml 复制代码
logging:
  driver: "json-file"
  options:
    max-size: "20m"    # 单个日志文件上限
    max-file: "10"     # 最多保留 10 个轮转文件

⚠️ 重要限制 :json-file 的 max-size 只对 Docker 管理的容器 stdout/stderr 日志生效。bind-mount 到宿主机的应用日志(如 ./logs/nginx:/var/log/nginx)走的是容器内应用自己写文件的路径,不受此约束,必须用 logrotate 或应用侧配置。

3.2 bind-mount 日志的 logrotate

在宿主机 /etc/logrotate.d/jiahetng 中配置:

conf 复制代码
/data/jiahetng/logs/nginx/*.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
    copytruncate
}
/data/jiahetng/logs/nacos/*.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
    copytruncate
}
/data/jiahetng/logs/redis/*.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
    copytruncate
}

copytruncate 是关键:先复制再截断原文件,避免 Nginx/Nacos 仍持有原句柄导致"截断后空间不释放"。

3.3 镜像层瘦身

  • 多阶段构建(如 node:20-alpine AS builder → nginx:alpine),运行镜像不含编译依赖
  • .dockerignore 排除 node_modules、dist、.git 等
  • 基础镜像优先 *-alpine

3.4 定期巡检脚本(cron)

bash 复制代码
#!/bin/bash
# /etc/cron.daily/jiahetng-disk-check
THRESHOLD=85
USAGE=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$USAGE" -ge "$THRESHOLD" ]; then
  docker system prune -f
  find /data/jiahetng/logs -name "*.log" -mtime +7 -delete
fi

四、本次事故复盘

占用项 大小 处理
./logs/* bind-mount 日志 5.5G 删 7 天前日志 + 加 logrotate
/root/.gradle 构建缓存 15G rm -rf + pkill GradleDaemon 释放句柄
/var/log/.osbak 厂商备份 6.4G rm -rf
Docker overlay2(活动镜像) 2.9G 不动
MariaDB 数据 227M 不动

核心教训:

  1. MySQL table is full ≠ 表真满了,先看磁盘。
  2. df 与 du 对不上时,一定有"幽灵文件"被进程持有,lsof +L1 找出来,杀进程或重启容器。
  3. json-file 的 max-size 只管 Docker 自己的 stdout,bind-mount 的应用日志必须单独轮转。
相关推荐
2301_808414381 小时前
Linux中动静态库的理解
linux·运维·服务器
誰能久伴不乏1 小时前
保姆级 Docker 入门教程
docker·容器·eureka
分布式存储与RustFS2 小时前
模型 checkpoint 放对象存储:分片上传、断点续传与残留清理
运维·云原生·开源·对象存储·分布式存储·s3·性能基准
一条破秋裤3 小时前
Linux 线程创建:pthread_create 与基本回收
java·linux·运维
FED_AF5 小时前
Linux运维“邪修”功法之通配符
linux·运维
YOLO数据集集合5 小时前
风机叶片表面损伤检测数据集 | 风机叶片 表面损伤 污渍检测 无人机巡检 风电运维9119期
运维·人工智能·计算机视觉·目标跟踪·无人机·智慧城市·电力巡检
j7~6 小时前
【Linux网络编程】四十七.《Linux IO 模型详解:阻塞 IO、非阻塞 IO 与 IO 多路转接(select)》
linux·运维·网络·select·非阻塞io·阻塞io·i/o多路连接
分布式存储与RustFS6 小时前
用RustFS给Harbor当S3存储后端
运维·云原生·开源·对象存储·分布式存储·s3·性能基准
泡海椒6 小时前
趋势分析报表:jquick-pdf折线图PDF生成实战
java·大数据·运维·服务器·前端·pdf
vipxieliang6 小时前
Docker vs Podman vs containerd:容器运行时全景
docker·容器