docker system prune 不够用,因其无法按业务维度清理镜像(如保留每服务最新3个或剔除7天未用测试镜像);需用Go调用Docker API获取镜像元数据,按语义化版本排序、安全校验引用关系后条件删除。

为什么 docker system prune 不够用
本地开发或 CI 环境中,docker build 产生的悬空镜像()、未打标签的中间层、以及被覆盖但未删除的旧版本镜像,会持续占用磁盘空间。单纯依赖 docker system prune -f --filter "until=24h" 无法按业务维度清理——比如只保留每个服务最新 3 个镜像,或自动剔除超过 7 天未使用的测试镜像。Golang 能帮你写一个有状态、可审计、带条件过滤的清理工具。
用 github.com/docker/docker/api/types 获取镜像列表
Go 客户端需通过 Docker daemon 的 HTTP API 拉取镜像元数据,关键字段包括 RepoTags、Id、Created 和 Size_。注意:Created 是 Unix 时间戳(秒级),不是字符串;RepoTags 可能为 nil(悬空镜像);Size_ 是字节数,需自行格式化。
- 必须启用 Docker daemon 的
-H unix:///var/run/docker.sock(默认已开) - Go 程序需有读取
/var/run/docker.sock的权限(通常加到docker用户组) - 不要直接解析
docker images命令输出——列宽、排序、截断不可靠
client, _ := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
images, _ := client.ImageList(context.Background(), types.ImageListOptions{All: true})
for _, img := range images {
fmt.Printf("ID: %s, Tags: %v, Created: %d, Size: %d\n",
img.ID, img.RepoTags, img.Created, img.Size_)
}
按标签前缀 + 版本号排序保留 N 个镜像
对形如 myapp:v1.2.0、myapp:v1.2.1-rc、myapp:latest 的镜像,不能简单按字典序删旧版——v1.10.0 会排在 v1.2.0 前面。需提取语义化版本(semver)并解析。
- 用
strings.HasPrefix(tag, "myapp:")过滤目标镜像 - 跳过
:latest或含-dev/-test的 tag(按需配置) - 用
github.com/blang/semver/v4解析有效版本,失败则归入“杂项”单独处理 - 对同一 repo 名下的镜像,按版本降序取前 N 个,其余标记为待删
安全删除:先 docker image inspect 再 client.ImageRemove
直接调 client.ImageRemove 可能因镜像被容器引用而失败,报错 conflict: unable to delete ... (must be forced)。生产环境应避免强制删除(Force: true),而是先检查引用关系。
立即学习“go语言免费学习笔记(深入)”;
- 调用
client.ContainerList并遍历container.Image字段,确认该镜像是否正被运行/已停止的容器使用 - 对悬空镜像(
RepoTags == nil),可直接删——它们本就不该被容器引用 - 删除前打印将删的镜像 ID 和大小,支持
--dry-run模式 - 错误时记录具体原因(如
image is being used by running container),而非忽略
真正难的不是删镜像,是判断“这个镜像还能不能删”。比如 CI 流水线刚推了一个新镜像,但下游部署任务还没拉取,此时删掉会导致部署失败。这类依赖关系必须靠外部标记(如镜像 label)、时间窗口(如保留最近 2 小时内构建的)或服务注册中心状态来协同决策——Go 工具只能提供基础能力,边界得自己划清楚。










