用友云开发者中心是基于 Docker 容器进行微服务架构应用的落地与管理. 相信各位同学在使用的过程中, 会发现随着 Docker 镜像的增多, 占用磁盘空间也约来越多. 这时我们需要清理私有镜像仓库中不需要的镜像. 但在实际操作时, 才会发现这本以为很简单的任务中却暗藏玄机, 遇到了不少的麻烦. 在这里我们分享一下清理镜像仓库时遇到的坑点. 想要直接寻求解决方案的同学可以直接看第二部分.
一, 那些年, 我们在清理镜像仓库时走过的坑
坑点 1: 官方提供的接口并不能真正的删除镜像
这着实是最大的坑点. 很多同学查资料发现, 官方已经提供了删除镜像仓库的 API, 所以可能相当然的以为直接使用就好, 殊不知掉入了官方埋下的最大的坑点, 也是本文要着手解决的核心问题: 官方提供的删除镜像仓库中镜像的接口, 仅仅是把 manifest 删除了, 真正的镜像文件还存在! 官方并没有提供删除镜像层的接口! 这也就是说, 当我们调用删除镜像的接口之后, 仅仅是查看镜像的列表时看不到原镜像了, 然而原有镜像仍然在磁盘中, 占用着宝贵的文件存储空间.
坑点 2: 直接调用官方的删除镜像 API, 会返回 405 的错误码
直接调用删除镜像的接口, 可能会遇到以下错误提示:
405, 意味着方法不被允许. 实际上, 官方可能是处于安全性的考虑, 在默认的情况下禁止了直接删除镜像的功能. 若要开启删除镜像功能, 需要修改镜像仓库的配置文件. 具体操作为修改 / etc/docker/registry/config.YAML 文件, 在 storage 下添加 delete 的许可之后, 重启镜像仓库服务.
坑点 3: 使用官方提供的 garbage-collect 工具, 会有无用的文件残留
官方为 registry 提供了 garbage-collect(gc) 工具清理镜像的物理存储, 将没有引用的 layer 删除.
gc 的清理过程分为两部分:
1)mark: 扫描所有的 manifest, 列出引用的 layer;
2)sweep: 扫描所有的 layer, 不在 mark 里的 layer 将被清理删除.
gc 可以在 dry-run 的模式下运行 (添加参数 - d), 只输出 gc 信息, 不进行实际操作. 我们可以通过这种方式来确认哪些镜像会被清除.
使用 gc 工具清理镜像的一个问题就是文件清理得不够干净, 无法清理已经没有 tag 的镜像目录, 并且还残存少部分文件, 从十 KB 到几十 KB 不等. 久而久之, 垃圾文件和目录的数量会越来越多.
坑点 4:garbage-collect 不是事务操作, 清理镜像时可能会产生误操作
gc 不是事务操作, 当 gc 过程中刚好有 push 操作时, 则可能会误删数据. 一个可行的解决办法是手动更改镜像仓库的配置, 暂时禁止镜像的 push 操作.
在镜像仓库的配置文件中可以配置 read-only 模式. 当启用 read-only 之后, 再 push 镜像时会得到 405 的错误. gc 完成后取消 read-only 模式, 再 push 镜像即可.
坑点 5: 使用 garbage-collect 工具后, 必须重启镜像仓库才能正常使用
如果不重启镜像仓库, 则再次 push 该镜像时可能会得到 layer already exists 错误:
其可能的原因是镜像被删除后, 仓库的缓存中还存有已经删除的镜像信息, 所以再次 push 会报层存在的错误.
二, 两种清理镜像仓库的方案
方案一: 使用官方 API + GC
使用官方提供的方法可以较为简便的清理镜像仓库. 整个清理过程可能需要几百毫秒到几秒的时间. 此操作有一定的危险性, 因此清理镜像不宜过于频繁. 官方在 Git 上也有类似描述. 点击查看:
具体操作过程如下:
1, 准备工作
在配置中许可删除操作. 修改镜像仓库的配置文件, 一般在如下路径:
/etc/docker/registry/config.YAML
在 storage 下添加 delete 的许可之后, 重启镜像仓库.
用 docker 方式启动的镜像仓库也可以添加环境变量:
REGISTRY_STORAGE_DELETE_ENABLED=true
2, 获取待删镜像的 digest
获取镜像 digest 的 API 为:
GET /v2/<name>/manifests/<reference>
其中, name 是仓库名, reference 是标签, 此时需要注意, 调用时需要加上 header 内容:
Accept: application/vnd.docker.distribution.manifest.v2+JSON
其中 Docker-Content-Digest 的值就是镜像的 digest
3, 调用官方的 HTTP API V2 删除镜像
删除镜像的 API 为:
DELETE /v2/<name>/manifests/<reference>
其中, name 是仓库名称, reference 是包含 "sha256:" 的 digest.
4, 调用 GC 清理镜像文件
使用 gc 工具的方式为:
bin/registry garbage-collect /etc/docker/registry/config.YAML
gc 清理需要时间, 如果在 gc 过程中刚好有 push 操作, 可能会产生未知的问题, 建议设置 read-only 模式之后再进行 gc, 然后再改回来.
5, 重启 docker registry
注意, 如果不重启会导致 push 相同镜像时产生 layer already exists 错误.
方案二: 使用第三方脚本
在清理镜像仓库这件事上, 业内已经有很多人进行过各种各样的尝试. 本文挑选一种比较好的方式推荐使用.
1, 宿主机安装 delete-docker-registry-image
可参考此命令的安装和使用方式. 参考链接:
2, 执行 delete-docker-registry-image 命令可以删除某个仓库 (sb)
或者某个具体的镜像 (如 alpine:3.2)
如果删除某镜像后该仓库为空, 可以用删除仓库的方式删除此空仓.
该工具也提供了 dry-run 的方式, 只输出待删除的信息不执行删除操作. 在命令后加上 --dry-run 即可.
3, 重启 docker registry
跟 gc 方式一样, 删除镜像之后要重启 docker registry, 不然还是会出现相同镜像 push 不成功的问题.
以上就是本文推荐的两种清理镜像仓库的两种方案. 第一种方案更多的使用了官方提供的工具, 使用时相对更加安全, 且无需额外安装其他内容. 第二种方案使用了第三方工具或脚本, 使用时更加灵活且简便, 且清理的更加彻底. 具体操作时可根据自己的需求选择方案.
来源: http://blog.51cto.com/14084875/2350610