docker清理私有镜像仓registry,python脚本实现

docker清理私有镜像仓registry,python脚本实现

查询和登录私有镜像仓容器

docker ps | grep registry
docker exec -it <container_id> /bin/sh

registry 配置

docker配置详解

# 进入容器
docker exec -it d2cfdce99aeb /bin/sh

# 修改registry配置文件
# vi /etc/docker/registry/config.yml
version: 0.1
log:
  fields:
    service: registry
storage:
  delete:
    enabled: true
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
  maintenance:
    uploadpurging:
      enabled: true
      age: 168h
      interval: 24h
      dryrun: false
    readonly:
      enabled: false
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3

registry API

查询镜像列表

curl --location --request GET 'http://192.168.205.129:5000/v2/_catalog'  -ik

获取响应中的reposities数组
在这里插入图片描述

查询指定repositry的tag

curl --location --request GET 'http://192.168.205.129:5000/v2/redis/tags/list'  -ik

URL中的redis是上一步中查询处理的,需要替换。如果当前repository没有tag,则为null
在这里插入图片描述

获取dgist签名

curl --location --request GET 'http://192.168.205.129:5000/v2/redis/manifests/latest' \
--header 'Accept: application/vnd.docker.distribution.manifest.v2+json' -ik

获取返回头里面的Docker-Content-Digest值
在这里插入图片描述

删除镜像

根据上一步查询处理的digest进行删除

curl --location --request DELETE 'http://192.168.205.129:5000/v2/redis/manifests/sha256:b11fca7b77c198f14a027bfca1981d95c9704fbf97fbf33908cb179c7da3588f' -ik

在这里插入图片描述

registry 垃圾回收

# 垃圾回收(容器内执行命令)
docker exec -it <私有库的容器ID或者容器名> sh -c ' registry garbage-collect /etc/docker/registry/config.yml'
或
registry garbage-collect /etc/docker/registry/config.yml

在这里插入图片描述重启registry后,再次执行垃圾回收,发现已经回收完成
在这里插入图片描述

python脚本实现

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Usage:执行前修改全局变量,如果某些容器不需要处理
"""

from urllib import request
import json
import logging
import os

# 环境变量配置,执行前请修改此处的内容
REGISTRY_HOST = "192.168.205.129"
REGISTRY_PORT = "5000"
VM_USER = "root"
REGISTRY_URL = f"http://{REGISTRY_HOST}:{REGISTRY_PORT}"
# 镜像白名单,对于某些不需要处理的镜像,可以将其repository写在列表中过滤掉
whiteList = []

logger = logging.getLogger(__name__)

# 设置日志级别
logging.basicConfig(level="INFO")

# 第二步:创建控制台日志处理器+文件日志处理器
console_handler = logging.StreamHandler()
file_handler = logging.FileHandler("./registry-clean-image-running.log", mode="a", encoding="utf-8")

# 第三步:设置控制台日志的输出级别,需要日志器也设置日志级别为info;----根据两个地方的等级进行对比,取日志器的级别
console_handler.setLevel(level="WARNING")

# 第四步:设置控制台日志和文件日志的输出格式
console_fmt = "%(name)s--->%(levelname)s--->%(asctime)s--->%(message)s--->%(lineno)d"
file_fmt = "%(lineno)d--->%(name)s--->%(levelname)s--->%(asctime)s--->%(message)s"

fmt1 = logging.Formatter(fmt=console_fmt)
fmt2 = logging.Formatter(fmt=file_fmt)

console_handler.setFormatter(fmt=fmt1)
file_handler.setFormatter(fmt=fmt2)

# 第五步:将控制台日志器、文件日志器,添加进日志器对象中
logger.addHandler(console_handler)
logger.addHandler(file_handler)


class RegistryError(Exception):
    pass


class RegistryAPI:

    def __init__(self, registry_url):
        self.registry_url = registry_url

    def getRepositories(self):
        """获取镜像仓中的所有镜像目录"""
        response = request.urlopen(f'{self.registry_url}/v2/_catalog')
        code = response.status
        content = response.read().decode('utf-8')
        if code == 200:
            logger.info(code)
            logger.info(content)
            return json.loads(content)['repositories']
        else:
            raise RegistryError(content)

    def getImageTags(self, repository):
        """获取镜像的标签tag"""
        response = request.urlopen(f'{self.registry_url}/v2/{repository}/tags/list')
        code = response.status
        content = response.read().decode('utf-8')
        if code == 200:
            logger.info(code)
            logger.info(content)
            return json.loads(content)['tags']
        else:
            raise RegistryError(content)

    def getImageDigest(self, repository, tag):
        """获取指定tag镜像的digest"""
        headers = {"Accept": "application/vnd.docker.distribution.manifest.v2+json"}
        req = request.Request(f'{self.registry_url}/v2/{repository}/manifests/{tag}', headers=headers)
        response = request.urlopen(req)
        code = response.status
        responseHeaders = dict(response.getheaders())
        content = response.read().decode('utf-8')
        if code == 200:
            logger.info(code)
            logger.info(responseHeaders)
            logger.info(content)
            return responseHeaders['Docker-Content-Digest']

        else:
            raise RegistryError(content)

    def deleteImageDigest(self, repository, digest):
        """删除tag镜像文件(不是彻底删除,只是把关联的文件释放了,还需要在容器中执行回收程序才会清理磁盘)"""
        req = request.Request(f'{self.registry_url}/v2/{repository}/manifests/{digest}', method='DELETE')
        response = request.urlopen(req)
        code = response.status
        content = response.read().decode('utf-8')
        if code == 202:
            logger.info(code)
            logger.info(content)
            logger.warning(f"delete success,repository:{repository},digest:{digest}")
        else:
            raise RegistryError(content)


class RegistryClean:

    def __init__(self, whiteList=[]):
        self.registryAPI = RegistryAPI(REGISTRY_URL)
        self.whiteList = whiteList  # 配置过滤的白名单,跳过清理白名单的镜像

    def deleteAllImages(self):
        """执行清理镜像的操作"""
        repositories = self.registryAPI.getRepositories()
        for repository in repositories:
            logger.info(f"current repository is: {repository}")

            if repository in self.whiteList:
                logger.info(f"{repository} in white list")
                continue

            repositoryTags = self.registryAPI.getImageTags(repository)
            if repositoryTags is not None:
                for tag in repositoryTags:
                    logger.info(f"current tag is: {tag}")

                    digest = self.registryAPI.getImageDigest(repository, tag)
                    logger.info(f"current digest is: {digest}")
                    if digest is not None:
                        self.registryAPI.deleteImageDigest(repository, digest)

            else:
                pass

    """
      注意,后续3个可执行命令,根据实际生产环境的情况需要适配一下。如果直接在部署registry的机器上执行,则可以去掉ssh的步骤。
    """

    def getRegistryId(self):
        """获取registry容器ID"""
        cmd = "docker ps | grep registry | awk '{print $1}'"
        r = os.popen(f'ssh -o "StrictHostKeyChecking no" {VM_USER}@{REGISTRY_HOST} "{cmd}"')
        data = r.readlines()
        print(data)
        return data

    def garbageCollect(self):
        """registry 垃圾回收"""
        containerId = self.getRegistryId()
        cmd = f"docker exec -it {containerId} sh -c ' registry garbage-collect /etc/docker/registry/config.yml'"
        r = os.popen(f'ssh -o "StrictHostKeyChecking no" {VM_USER}@{REGISTRY_HOST} "{cmd}"')
        data = r.readlines()
        print(data)

    def restartRegistry(self):
        """重启registry容器"""
        containerId = self.getRegistryId()
        cmd = f"docker restart {containerId}"
        r = os.popen(f'ssh -o "StrictHostKeyChecking no" {VM_USER}@{REGISTRY_HOST} "{cmd}"')
        data = r.readlines()
        print(data)


def main():
    """整体的流程:清理镜像文件,执行垃圾回收,重启镜像容器"""
    registryClean = RegistryClean(whiteList=whiteList)
    registryClean.deleteAllImages()
    registryClean.getRegistryId()
    registryClean.garbageCollect()
    registryClean.restartRegistry()


if __name__ == "__main__":
    main()

参考链接

感谢大佬们的帖子,参考链接如下:
docker配置详解
Docker彻底删除私有库镜像

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值