Docker磁盘告急:从日志膨胀到Overlay2占满的根治与预防
1. 当Docker突然罢工:磁盘爆满的紧急诊断
那天凌晨3点,我的手机突然疯狂震动——监控系统报警显示生产环境服务器磁盘使用率100%。连SSH都卡成幻灯片,好不容易登上去用df -h一看,/var/lib/docker/overlay2这个目录吃掉了95%的空间。这种场景就像你家的水管突然爆裂,必须立即找到漏水点。
第一步要像外科手术般精准定位问题源。进入/var/lib/docker目录后,我用这个命令快速扫描各子目录占用情况:
du -h --max-depth=1 | sort -rh
发现三个"罪魁祸首":
containers目录下堆积了23GB的*-json.log日志文件volumes目录里某个Java应用的日志卷占了18GBoverlay2存储驱动层积压了40GB废弃数据
这里有个实用技巧:用ncdu工具可以交互式查看目录大小(需提前安装)。相比du命令,它能实时导航目录树,特别适合在复杂的目录结构中快速定位大文件:
ncdu /var/lib/docker
2. 救火队员的紧急清理手册
2.1 清理容器日志的黄金三连
面对还在运行的容器,直接删除日志文件可能导致程序异常。我推荐这个安全清理脚本:
#!/bin/bash
echo "==== 开始清理容器日志 ===="
find /var/lib/docker/containers -name "*.log" -exec sh -c 'echo "清理 {}"; > {}' \;
echo "==== 清理完成 ===="
保存为clean_logs.sh后,记得给执行权限:
chmod +x clean_logs.sh
重要警告:如果遇到"设备空间不足"错误,说明磁盘真的被塞满了。这时需要先手动删除部分文件腾出空间:
# 找出最大的5个日志文件并清空
find /var/lib/docker -type f -name "*.log" -exec ls -lh {} + | sort -k5 -rh | head -5 | awk '{print $9}' | xargs -I {} sh -c '> {}'
2.2 彻底重置Docker存储
当需要核弹级清理时(慎用!会删除所有容器和镜像):
docker system prune -af --volumes
这个命令会:
- 停止所有运行中的容器
- 删除所有停止的容器
- 删除所有未被使用的网络
- 删除所有未被任何容器引用的卷
- 删除所有悬空和未使用的镜像
3. 为什么Overlay2会成为磁盘杀手?
3.1 存储驱动的层叠魔法
Overlay2就像千层蛋糕:
- lowerdir:只读的基础镜像层(相当于蛋糕胚)
- upperdir:容器的可写层(相当于奶油装饰)
- merged:最终呈现的统一视图(成品蛋糕)
每次docker commit都会产生新层。我曾遇到过一个反复提交的镜像,竟然堆积了120+层,占用空间是基础镜像的10倍!
3.2 镜像体积的隐形膨胀
通过docker history查看镜像构建历史时,经常会发现这种情况:
IMAGE CREATED SIZE COMMENT
a1b2c3d4e5f6 2 weeks ago 1.2GB apt-get install unnecessary-package
很多开发者喜欢在Dockerfile里用apt-get upgrade,这会导致镜像包含所有旧版本软件包。正确的做法是:
RUN apt-get update && \
apt-get install -y --no-install-recommends necessary-package && \
rm -rf /var/lib/apt/lists/*
4. 构建防爆仓的Docker环境
4.1 日志管理的三重防护
第一重:全局日志限制
编辑/etc/docker/daemon.json(没有就新建):
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3",
"compress": "true"
}
}
然后重启Docker服务:
systemctl restart docker
第二重:Compose文件级控制
在docker-compose.yml中为每个服务单独设置:
services:
my_app:
logging:
driver: json-file
options:
max-size: 50m
max-file: 2
第三重:应用层日志轮转 对于Java应用,在logback.xml中配置:
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/var/log/myapp.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/var/log/myapp.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>7</maxHistory>
</rollingPolicy>
</appender>
4.2 存储空间的智能监控
我习惯用这个脚本做每日检查,保存为/usr/local/bin/check_docker_storage:
#!/bin/bash
THRESHOLD=80
CURRENT=$(df /var/lib/docker | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$CURRENT" -gt "$THRESHOLD" ]; then
echo "警告:Docker存储使用率 ${CURRENT}%"
echo "Top 5大容器:"
docker ps --format "{{.Names}}" | xargs -I {} sh -c \
'echo {}: $(du -sh $(docker inspect {} --format="{{.GraphDriver.Data.MergedDir}}") | cut -f1)'
else
echo "当前使用率: ${CURRENT}%"
fi
然后添加到crontab:
0 9 * * * /usr/local/bin/check_docker_storage >> /var/log/docker_storage.log
5. 高级玩家的存储优化技巧
5.1 使用外挂存储设备
如果服务器支持,最好把Docker数据目录迁移到独立磁盘:
# 假设新磁盘挂载在/mnt/data
systemctl stop docker
rsync -a /var/lib/docker /mnt/data/
mv /var/lib/docker /var/lib/docker.bak
ln -s /mnt/data/docker /var/lib/docker
systemctl start docker
5.2 选择更适合的存储驱动
对于高IOPS需求的场景,可以考虑devicemapper或zfs驱动。比如在CentOS上切换为devicemapper:
# 编辑/etc/docker/daemon.json
{
"storage-driver": "devicemapper",
"storage-opts": [
"dm.basesize=20G",
"dm.fs=ext4"
]
}
不过要注意,不同驱动有各自的适用场景,变更前务必做好测试。
那次事故后,我给团队制定了新的Docker使用规范:所有生产环境容器必须配置日志轮转,CI/CD流水线中加入镜像层数检查,每周例行执行存储健康检查。现在两年过去了,再没出现过半夜被磁盘告警吵醒的情况。
更多推荐



所有评论(0)