Leo Zhang
菩提本无树,明镜亦非台!

前言

在jenkins CI/CD流水线中以自动打包并push镜像的方式运行了一段时间之后,

docker registry中堆积的历史镜像数量极多,磁盘空间告急,为此,有必要定期做镜像的清理,并释放镜像占用的存储空间。

 

清除原理

Docker registry提供有restful api进行镜像管理,参考官方文档:
https://docs.docker.com/registry/spec/api/

 

Docker存储使用的aufs文件系统分层存储结构,将容器文件以读写分层的形式存储在宿主机中.在registry容器中,

存放镜像的分层数据在宿主机上的挂载路径为:

/var/lib/docker/volumes/{container_id}/_data/docker/registry/v2/blobs

关于docker aufs的存储模式,这篇文章写得非常通俗易懂,可以参考:
https://www.cnblogs.com/sammyliu/p/5931383.html

 

一图简介上层镜像生成及删除过程中的分层文件处理原理:

如图中文字解释,仅仅是调用api删除镜像是不够的,在删除了镜像之后,镜像的非共享分层文件还是会存放在磁盘中继续占用存储空间,

因此,需要在删除镜像之后,使用docker registry自带的GC工具来进行垃圾分层(即无绑定镜像的分层)数据清除.

 

 

镜像分析

在了解以上前提后,开始排查哪些registry repo的历史镜像较多(分层数量多)
1.从宿主机进入docker registry容器内部,使用registry GC分析命令查看分层情况:

registry garbage-collect --dry-run /etc/docker/registry/config.yml  # --dry-run选项为layer层级分析,并不实际进行GC


2.可以便捷使用以下命令对分层数较多的镜像做一个排序:

registry garbage-collect --dry-run /etc/docker/registry/config.yml >> res.txt
6ac03183e197:~# cat res.txt | awk -F : '{print $1}' | sort | uniq -c | sort -rn -k1 | head -10
134161 zdtest
56101 ordertest
42691 bjdev
35881 zhqtest
13801 systemtest
9601 zddev
9361 bjtest
7411 dsystemtest
505 tooltest

可以看到,如上10个repo历史镜像数量大,需要清理。

 

 

删除镜像

注意:

无论是delete方法调用restful接口,还是registry 自带工具的GC清理,都需要registry的配置文件中开启允许删除功能:

vim /etc/docker/registry/config.yml

storage:
  delete:     enabled:
true

 

由于数量较多,因此使用python多线程来调用registry restful api进行删除操作,脚本内容如下,可根据自己的场景修改registry url:

import requests
from concurrent.futures import ThreadPoolExecutor


class Docker(object):
    def __init__(self, hub, repos):
        self.hub = hub
        self.repos = repos

    @staticmethod
    def get_tag_list(hub, repo):
        # 获取这个repo的所有tags
        tag_list_url = '%s/v2/%s/tags/list' % (hub, repo)
        r1 = requests.get(url=tag_list_url)
        tag_list = r1.json().get('tags')
        return tag_list

    def main(self):
        thpool = ThreadPoolExecutor(10)
        for repo in self.repos:
            thpool.submit(self.delete_images, repo)

        thpool.shutdown(wait=True)

    def delete_images(self, repo):
        hub = self.hub
        tag_list = self.get_tag_list(hub=hub, repo=repo)
        num = 0
        try:
            # 保留最后两个版本的镜像
            for tag in tag_list[:-2]:
                # 获取image digest摘要信息
                get_info_url = '{}/v2/{}/manifests/{}'.format(hub, repo, tag)
                header = {"Accept": "application/vnd.docker.distribution.manifest.v2+json"}
                r2 = requests.get(url=get_info_url, headers=header, timeout=10)
                digest = r2.headers.get('Docker-Content-Digest')

                # 删除镜像
                delete_url = '%s/v2/%s/manifests/%s' % (hub, repo, digest)
                r3 = requests.delete(url=delete_url)
                if r3.status_code == 202:
                    num += 1

        except Exception as e:
            print(str(e))

        print('仓库%s 共删除了%i个历史镜像' % (repo, num))


if __name__ == '__main__':
    hub = 'http://registry.xxx.com:5000'
    repos = ['zdtest', 'ordertest', 'bjdev', 'zhqtest', 'systemtest', 'zddev', 'bjtest', 'dsystemtest', 'tooltest']
    d = Docker(hub=hub, repos=repos)
    d.main()

 

运行结果:

仓库tooltest 共删除了17个历史镜像
仓库dsystemtest 共删除了245个历史镜像
仓库bjtest 共删除了310个历史镜像
仓库zddev 共删除了318个历史镜像
仓库systemtest 共删除了463个历史镜像
仓库zdtest 共删除了1574个历史镜像
仓库zhqtest 共删除了300个历史镜像
仓库bjdev 共删除了1421个历史镜像
仓库ordertest 共删除了1868个历史镜像

 

 

空间清理

回到docker registry容器内,直接运行GC命令,这次不再加 --dry-run选项

registry garbage-collect /etc/docker/registry/config.yml

查看磁盘,可以发现磁盘容量已经空闲出许多了,镜像清理及存储空间释放完成!

 

 

———————————————————————————————————

原文链接:https://blog.csdn.net/ywq935/article/details/83828888

 

 

posted on 2019-11-29 17:05  LeoZhanggg  阅读(3222)  评论(0编辑  收藏  举报