1. 项目概述
在Docker的日常使用中,镜像和容器的积累是一个常见问题。作为一名长期使用Docker的开发者,我经常遇到磁盘空间被大量无用镜像和停止的容器占用的情况。手动清理不仅耗时,还容易遗漏。为此,我开发了一个自动化清理脚本,能够智能识别并清理无用的Docker资源。
这个脚本的核心功能包括:
- 自动识别并删除未被任何容器引用的镜像(dangling images)
- 清理指定时间之前创建的旧镜像
- 移除已停止运行的容器
- 保留最近N个版本的镜像(可配置)
2. 核心需求解析
2.1 为什么需要自动化清理
Docker环境在使用过程中会产生大量"垃圾":
- 构建过程中产生的中间层镜像
- 测试后遗留下来的容器
- 旧版本的应用程序镜像
- 未被引用的数据卷
这些资源不仅占用磁盘空间,还会影响Docker的性能。手动清理效率低下且容易出错。
2.2 清理策略设计
经过多次实践,我总结出以下清理策略:
-
镜像清理:
- 优先删除未被任何容器引用的镜像
- 保留最近3个版本的应用程序镜像
- 删除超过30天未使用的旧镜像
-
容器清理:
- 自动清理已停止运行的容器
- 保留最近7天内创建的容器(用于调试)
-
数据卷清理:
- 删除未被任何容器引用的数据卷
- 保留关键数据卷(通过白名单配置)
3. 脚本实现细节
3.1 基础清理功能实现
bash复制#!/bin/bash
# 清理未被引用的镜像
docker image prune -a -f
# 清理已停止的容器
docker container prune -f
# 清理未被使用的数据卷
docker volume prune -f
这个基础版本已经能解决80%的清理需求。但实际使用中发现几个问题:
- 会删除所有未被引用的镜像,包括可能需要的中间层
- 无法保留特定数量的最新版本
- 没有日志记录功能
3.2 增强版清理脚本
bash复制#!/bin/bash
set -e
LOG_FILE="/var/log/docker-clean.log"
KEEP_LAST=3 # 保留最近3个版本
function log {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $LOG_FILE
}
# 1. 清理未被容器引用的镜像
log "开始清理未被引用的镜像..."
docker image prune -a -f | tee -a $LOG_FILE
# 2. 清理已停止的容器(保留最近7天)
log "清理已停止的容器..."
docker container prune -f --filter "until=168h" | tee -a $LOG_FILE
# 3. 清理旧版本镜像(保留最近KEEP_LAST个)
log "清理旧版本镜像..."
for image in $(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "<none>"); do
# 跳过latest标签
if [[ $image == *:latest ]]; then
continue
fi
# 获取同仓库的所有镜像并按创建时间排序
repo=${image%:*}
images_to_delete=$(docker images --format "{{.ID}} {{.CreatedAt}}" --filter reference="$repo:*" | sort -k2 -r | awk -v keep="$KEEP_LAST" 'NR>keep {print $1}')
if [ -n "$images_to_delete" ]; then
log "删除旧镜像: $images_to_delete"
docker rmi $images_to_delete | tee -a $LOG_FILE
fi
done
# 4. 清理未被使用的数据卷(白名单除外)
log "清理未被使用的数据卷..."
docker volume prune -f --filter "label!=keep" | tee -a $LOG_FILE
log "清理完成"
4. 高级功能实现
4.1 镜像保留策略优化
在实际使用中,我们发现简单的"保留最近N个版本"策略有时会误删重要镜像。因此增加了以下规则:
-
永远保留带有以下标签的镜像:
- latest
- stable
- production
-
按镜像大小智能清理:
- 优先清理大尺寸镜像
- 保留常用基础镜像(如alpine、ubuntu等)
bash复制# 在原有脚本中添加
SAFE_TAGS=("latest" "stable" "production")
function should_keep_image {
local tag=${1#*:}
for safe_tag in "${SAFE_TAGS[@]}"; do
if [[ $tag == $safe_tag ]]; then
return 0
fi
done
return 1
}
4.2 安全删除检查
为避免误删正在使用的资源,增加了安全检查:
bash复制function safe_delete_images {
local images=$1
for image in $images; do
# 检查是否有容器在使用该镜像
if [ $(docker ps -q -f ancestor=$image | wc -l) -eq 0 ]; then
docker rmi $image | tee -a $LOG_FILE
else
log "跳过正在使用的镜像: $image"
fi
done
}
5. 自动化部署方案
5.1 定时任务配置
建议通过crontab设置每天凌晨执行清理:
bash复制# 每天凌晨3点执行清理
0 3 * * * /usr/local/bin/docker-clean.sh >> /var/log/docker-clean.log 2>&1
5.2 容器化部署
也可以将脚本打包为Docker镜像,通过容器方式运行:
dockerfile复制FROM alpine:latest
RUN apk add --no-cache docker-cli bash
COPY docker-clean.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-clean.sh
CMD ["/usr/local/bin/docker-clean.sh"]
使用时需要挂载Docker socket:
bash复制docker run -d --name docker-cleaner \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /var/log/docker-clean.log:/var/log/docker-clean.log \
my-docker-cleaner
6. 常见问题与解决方案
6.1 权限问题
如果遇到权限错误,可以:
-
将当前用户加入docker组:
bash复制sudo usermod -aG docker $USER -
或者使用root权限运行脚本:
bash复制sudo /usr/local/bin/docker-clean.sh
6.2 镜像删除失败
当镜像被容器引用时,删除会失败。解决方案:
-
先删除相关容器:
bash复制docker rm $(docker ps -aq -f ancestor=镜像名) -
或者强制删除(不推荐):
bash复制
docker rmi -f 镜像ID
6.3 磁盘空间未释放
有时删除镜像后磁盘空间不会立即释放,需要:
-
重启Docker服务:
bash复制sudo systemctl restart docker -
或者手动清理存储驱动:
bash复制sudo docker system prune -a --volumes
7. 性能优化建议
-
清理频率:
- 生产环境:每周一次
- 开发环境:每天一次
-
资源保留策略:
- 根据项目需求调整KEEP_LAST参数
- 对关键镜像添加保护标签
-
监控与告警:
bash复制# 检查Docker磁盘使用情况 docker system df -v # 设置磁盘空间告警 THRESHOLD=90 if [ $(df /var/lib/docker --output=pcent | tail -1 | tr -d '% ') -gt $THRESHOLD ]; then echo "Docker磁盘使用超过${THRESHOLD}%" | mail -s "Docker磁盘告警" admin@example.com fi
8. 脚本扩展功能
8.1 多节点清理
对于Swarm或Kubernetes集群,可以扩展脚本支持多节点清理:
bash复制#!/bin/bash
NODES=$(docker node ls --format "{{.Hostname}}")
for node in $NODES; do
echo "清理节点: $node"
docker ssh $node /usr/local/bin/docker-clean.sh
done
8.2 清理报告生成
增加HTML报告生成功能:
bash复制function generate_report {
local report_file="/var/log/docker-clean-report-$(date +%Y%m%d).html"
cat > $report_file <<EOF
<html>
<head><title>Docker清理报告 $(date)</title></head>
<body>
<h1>Docker清理报告 - $(date)</h1>
<h2>释放空间</h2>
<pre>$(docker system df)</pre>
<h2>删除的镜像</h2>
<pre>$(grep "删除旧镜像" $LOG_FILE)</pre>
</body>
</html>
EOF
}
9. 最佳实践总结
经过长期使用和优化,总结出以下最佳实践:
-
渐进式清理:
- 先清理未被引用的资源
- 再清理旧版本
- 最后处理数据卷
-
白名单机制:
bash复制# 在数据卷创建时添加保护标签 docker volume create --label keep=true my_important_volume -
日志与审计:
- 记录所有清理操作
- 定期审查日志文件
- 设置操作告警
-
测试环境验证:
- 先在测试环境运行脚本
- 使用--dry-run参数预览清理操作
bash复制
docker system prune --dry-run
这个自动化清理脚本已经在我们的生产环境运行超过一年,平均每月节省40%的Docker存储空间,显著改善了系统性能。根据不同的使用场景,可以灵活调整清理策略参数。
