1. 问题背景:为什么Docker会吃光你的磁盘空间?
第一次发现Docker把磁盘占满时,我正在部署一个微服务项目。df -h命令显示/var/lib/docker/overlay2目录突然暴涨到50GB,整个系统分区只剩下可怜的几百MB空间。这种经历对Docker用户来说太常见了——特别是当你频繁构建镜像、运行容器时,那些隐藏的"存储碎片"会像黑洞一样吞噬你的磁盘。
Overlay2作为Docker默认的存储驱动,采用"写时复制"机制。每次创建新容器时,它会在基础镜像上叠加一个可写层,所有修改都记录在这个薄层中。听起来很高效对吧?但问题在于:
- 停止的容器仍然保留可写层
- 构建失败的中间镜像层不会自动清理
- 日志文件可能无限增长
- 卷(volume)数据长期累积
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 诊断磁盘占用:找到真正的空间杀手
2.1 快速定位大文件
bash复制# 查看Docker根目录大小
du -sh /var/lib/docker
# 深入分析overlay2目录
du -h --max-depth=1 /var/lib/docker/overlay2 | sort -h
最近一次我用这个命令发现,某个容器的diff目录竟然有12GB——原来是个MySQL容器,binlog和慢查询日志从未轮转。
2.2 使用docker system命令
bash复制docker system df
输出示例:
code复制TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 15 6 5.2GB 3.1GB (59%)
Containers 8 3 1.1GB 700MB (63%)
Local Volumes 5 2 2.5GB 1.8GB (72%)
Build Cache 32 0 1.4GB 1.4GB
这个宝藏命令能显示四类可回收空间:
- 未使用的镜像(悬空镜像最容易被忽视)
- 停止的容器
- 孤立的卷
- 构建缓存
3. 精准清理策略:外科手术式删除
3.1 基础清理三板斧
bash复制# 1. 删除所有停止的容器
docker container prune
# 2. 删除所有悬空镜像(无标签的中间层)
docker image prune
# 3. 删除未被任何容器引用的卷
docker volume prune
上周我用这三条命令在一个测试环境回收了28GB空间。但注意:prune操作不可逆,生产环境建议先确认列表:
bash复制docker container ls -a --filter status=exited
docker image ls --filter dangling=true
3.2 高级清理技巧
3.2.1 按时间过滤删除
bash复制# 删除创建超过24小时的容器
docker container prune --filter "until=24h"
# 删除一周前构建的镜像
docker image prune --all --filter "until=168h"
3.2.2 选择性删除镜像
bash复制# 删除redis相关镜像
docker image rm $(docker image ls -q "redis*")
# 保留最近3个版本
docker image ls --format '{{.Repository}}:{{.Tag}}' | grep 'myapp' | sort -V | head -n -3 | xargs docker image rm
4. 根治方案:从源头控制磁盘增长
4.1 调整Docker存储配置
编辑/etc/docker/daemon.json:
json复制{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.size=20GB" # 限制单个容器rootfs大小
],
"log-driver": "json-file",
"log-opts": {
"max-size": "10m", # 单个日志文件最大10MB
"max-file": "3" # 保留3个日志文件
}
}
改完后需要重启Docker服务:
bash复制systemctl restart docker
4.2 容器日志管理实战
上周有个Java应用的容器日志居然占了7GB!解决方案:
bash复制# 运行时限制日志大小
docker run --log-opt max-size=10m --log-opt max-file=3 myapp
# 对已存在的容器修改配置
docker inspect --format='{{.HostConfig.LogConfig}}' my-container
# 然后更新容器配置(需要停止容器)
4.3 构建优化减少镜像层
dockerfile复制# 错误示例:每行RUN都会产生新层
RUN apt update
RUN apt install -y python
RUN pip install -r requirements.txt
# 正确做法:合并命令 && 清理缓存
RUN apt update && \
apt install -y python && \
pip install -r requirements.txt && \
apt clean && \
rm -rf /var/lib/apt/lists/*
5. 极端情况处理:手动清理overlay2
当标准命令无法解决问题时(比如Docker服务已无法启动),可以手动操作:
- 停止Docker服务
bash复制systemctl stop docker
- 安全删除(先备份!)
bash复制cd /var/lib/docker/overlay2
# 找出正在使用的层
docker ps -q | xargs docker inspect --format '{{.GraphDriver.Data.MergedDir}}' | xargs -I{} basename {}
# 然后删除未被引用的目录
- 使用专用工具
bash复制# 安装docker-gc工具
curl -L https://github.com/spotify/docker-gc/raw/master/docker-gc > /usr/local/bin/docker-gc
chmod +x /usr/local/bin/docker-gc
docker-gc
6. 预防性维护:建立监控体系
在我的生产环境,会设置定期任务:
bash复制# 每天凌晨清理
0 3 * * * docker system prune -f --filter "until=72h"
# 每周日深度清理
0 4 * * 0 docker system prune -af && docker volume prune -f
配合监控脚本(保存为check_docker_disk.sh):
bash复制#!/bin/bash
THRESHOLD=80
USAGE=$(df /var/lib/docker | awk 'NR==2 {print $5}' | tr -d '%')
if [ $USAGE -gt $THRESHOLD ]; then
docker system prune -f
echo "$(date): 触发Docker自动清理" >> /var/log/docker_clean.log
fi
添加到cron:
bash复制*/30 * * * * /path/to/check_docker_disk.sh
7. 疑难问题排查实录
7.1 删除文件后磁盘空间未释放
这是因为文件可能被已停止的容器进程占用:
bash复制# 查找被删除但仍被占用的文件
lsof +L1 | grep deleted
# 解决方案是彻底删除相关容器
7.2 卷数据意外增长
某次我发现一个postgres容器的卷占了15GB,原因是未配置WAL归档:
bash复制# 查看卷大小
docker system df -v
# 进入卷目录检查
docker volume inspect my-volume
7.3 Build Cache占用过大
特别是多阶段构建时会产生大量缓存:
bash复制# 构建时禁用缓存
docker build --no-cache .
# 或者定期清理
docker builder prune
8. 容器存储进阶管理
对于长期运行的容器,建议:
- 重要数据挂载到宿主机的特定目录
- 使用
tmpfs挂载临时文件
bash复制docker run --tmpfs /tmp:rw,size=1g myapp
- 对数据库类容器,配置自动清理策略(如MySQL的binlog过期)
我在管理Kubernetes集群时,还会使用:
bash复制kubectl --context=prod get pods -o json | jq '.items[].spec.containers[].resources.requests.storage'
来监控所有Pod的存储请求量
