公司 Harbor 仓库一年多没清理,硬盘被堆满了,为此写了个批量清除的 Python 脚本。

源代码见 github-harbor_clean.py

# coding: utf-8

from operator import itemgetter
from urllib import parse
import requests import datetime as dt
# import maya import logging
logging.basicConfig(filename='harbor_clean.txt', filemode="w", level=logging.INFO) logger = logging.getLogger(__name__) """
清理 Harbor 仓库的老镜像
""" class HarborCleaner(object):
delete_status = {
200: "Delete tag successfully.",
400: "Invalid repo_name.",
401: "Unauthorized.",
403: "Forbidden.",
404: "Repository or tag not found.",
} def __init__(self, user: str, password: str, hostname: str, port: int, use_https=True):
scheme = "https" if use_https else "http"
api_base = f"{scheme}://{hostname}:{port}/api"
self.search_api = api_base + "/search?q={key_word}"
self.projects_api = api_base + "/projects"
self.repository_query_api = api_base + "/repositories?project_id={project_id}"
# repo_name 一般为 "project_name/repo_name" 格式,必须做转义处理(因为中间有斜杠)
self.repository_tags_api = api_base + "/repositories/{repo_name}/tags"
self.repository_tag_api = self.repository_tags_api + "/{tag}" self.session = requests.Session()
self.session.verify = False # 如果公司是使用自签名证书,不能通过 SSL 验证,就需要设置这个
self.session.headers = {
"Accept": "application/json"
} self.session.auth = (user, password) def get_all_projects(self):
resp = self.session.get(self.projects_api) success = resp.status_code == 200
return {
"success": success,
"data": resp.json() if success else resp.text
} def get_all_repos(self, project: dict):
url = self.repository_query_api.format(project_id=project['project_id'])
resp = self.session.get(url) success = resp.status_code == 200
return {
"success": success,
"data": resp.json() if success else resp.text
} def get_all_tags(self, repo: dict):
"""repo_name 需要做转义"""
repo_name = parse.quote(repo['name'], safe="")
url = self.repository_tags_api.format(repo_name=repo_name)
resp = self.session.get(url) success = resp.status_code == 200
return {
"success": success,
"data": resp.json() if success else resp.text
} def get_tags_except_lastest_n(self, repo: dict, n: int):
"""获取除了最新的 n 个 tag 之外的所有 tags""" # 如果 tags 数小于 n + 1,说明该镜像不需要做清理。
if repo['tags_count'] <= n+1: # +1 是因为 latest 是重复的 tag
return [] result = self.get_all_tags(repo)
tags: list = result['data']
for tag in tags:
# tag['time'] = maya.MayaDT.from_iso8601(tag['created']) # '2019-04-09T11:33:49.296960745Z'
# # python 自带的解析函数,只能处理 6 位小数,下面截去多余的三位
timestamp = tag['created'][:-4] + 'Z'
tag['time'] = dt.datetime.strptime(timestamp, r'%Y-%m-%dT%H:%M:%S.%fZ') tags.sort(key=itemgetter('time')) # 使用 time 键进行原地排序
return tags[:-n-1] # expect the latest n tags, -1 是因为 latest 是重复的 tag def soft_delete_tag(self, repo: dict, tag: dict):
"""repo_name 需要做转义
这里删除后,还需要进行一次 GC,才能真正地清理出可用空间。
"""
repo_name = parse.quote(repo['name'], safe="")
url = self.repository_tag_api.format(repo_name=repo_name, tag=tag['name'])
resp = self.session.delete(url) return {
"success": resp.status_code == 200,
"message": self.delete_status.get(resp.status_code)
} def soft_delete_all_tags_except_latest_n(self, n):
"""从每个仓库中,删除所有的 tags,只有最新的 n 个 tag 外的所有 tags 除外"""
res_projects = self.get_all_projects()
if not res_projects['success']:
logger.warning("faild to get all projects, message: {}".format(res_projects['data'])) logger.info("we have {} projects".format(len(res_projects['data'])))
for p in res_projects['data']:
res_repos = self.get_all_repos(p)
if not res_projects['success']:
logger.warning("faild to get all repos in project: {}, message: {}".format(p['name'], res_repos['data'])) logger.info("we have {} repos in project:{}".format(len(res_repos['data']), p['name']))
for repo in res_repos['data']:
logger.info("deal with repo: {}".format(repo['name'])) old_tags = self.get_tags_except_lastest_n(repo, n)
logger.info("we have {} tags to delete in repo: {}".format(len(old_tags), repo['name']))
for tag in old_tags:
logger.info("try to delete repo:{}, tag: {}, create_time: {}".format(repo['name'], tag['name'], tag['created']))
result = self.soft_delete_tag(repo, tag)
if result['success']:
logger.info("success delete it.")
else:
logger.warning("delete failed!, message: {}".format(result['message'])) if __name__ == "__main__":
# 1. 通过 harbor 的 restful api 进行软删除
harbor_cleaner = HarborCleaner(
user="admin",
password="Admin123",
hostname="reg.harbor.com",
port=8321
)
harbor_cleaner.soft_delete_all_tags_except_latest_n(10) # 每个镜像只保留最新的十个 tag # 2. 进行一次 GC,清除掉所有已软删除的 images
# 2.1 harbor 1.7 之前的版本,需要停机才能 GC """
cd /volume1/docker/harbor/harbor
docker-compose down # 停机 # 下面的 tag 'v2.6.2-v1.4.0' 需要换成当前使用的 registry-photon 镜像的版本号
# --dry-run 表示尝试进行 GC,输出 log 与正式 gc 一致,可用于提前发现问题
docker run -it --name gc --rm --volumes-from registry vmware/registry-photon:v2.6.2-v1.4.0 garbage-collect --dry-run /etc/registry/config.yml # 正式 gc,这个才会真正的 gc 掉已经软删除的镜像
docker run -it --name gc --rm --volumes-from registry vmware/registry-photon:v2.6.2-v1.4.0 garbage-collect /etc/registry/config.yml
""" # 2.2 harbor 1.7+ 可以通过 restful api 进行在线 GC 或定期自动 GC。

Harbor 批量清理历史镜像的更多相关文章

  1. 【转载】Docker registry仓库历史镜像批量清理

    前言 在jenkins CI/CD流水线中以自动打包并push镜像的方式运行了一段时间之后, docker registry中堆积的历史镜像数量极多,磁盘空间告急,为此,有必要定期做镜像的清理,并释放 ...

  2. harbor批量导出镜像

    工作中遇到一个问题,要把某个项目的harbor镜像库全部迁移到其他环境的harbor上,由于网络不通,只能导出来,传给同事,让同事导入到他们的harbor上: 记录下我的操作方法. 参考博客园的一篇博 ...

  3. harbor自动清理镜像

    harbor定时清理镜像 ​ 分享下最近写harbor仓库镜像自动清理脚本思路,很长时间不写shell脚本,这次的脚本也是匆匆写的,还有很多可优化点,感兴趣的可以参考自己优化下,写的不完善地方也希望指 ...

  4. Harbor作为Docker的镜像中心

    转载于网络 我们采用Harbor作为Docker的镜像中心. 有几个原因: Harbor采用Docker Compose拉起维护,简单方便. 采用Nginx作为入口网关,各种参数配置相对熟悉. 基于N ...

  5. (十七)VMware Harbor 垃圾清理

    1. 在线垃圾清理 注意:从Harbor中删除镜像时不释放空间,垃圾收集是通过从清单中不再引用文件系统中删除blob来释放空间的任务. 注意:在执行垃圾收集时,Harbor将进入只读模式,并且禁止对d ...

  6. 微软在MSDN中更新了Win8.1批量授权版镜像(中文版更新完毕&版本说明)

    微软在MSDN中更新了Win8.1大客户专业版和企业版镜像,零售版镜像(即专业版+核心版二合一镜像)没有更新,依然是9月份发布的版本.已证实,新的批量授权版镜像是集成了GA Rollup A更新,并且 ...

  7. 使用Harbor配置Kubernetes私有镜像仓库

    通常情况下,在私有云环境中使用kubernetes时,我们要从docker registry拉取镜像的时候,都会给docker daemo配置–insecure-registry属性来告诉docker ...

  8. Nginx 负载均衡的Cache缓存批量清理的操作记录

    1)nginx.conf配置 [root@inner-lb01 ~]# cat /data/nginx/conf/nginx.conf user www; worker_processes 8; #e ...

  9. docker 定时清理none镜像

    =============================================== 2019/3/31_第1次修改                       ccb_warlock == ...

随机推荐

  1. ORACLE AUDIT

    Oracle 作者:Davis_itpub 时间:2018-06-27 16:28:39  61  0 审计(Audit)用于监视用户所执行的数据库操作,并且Oracle 会将审计跟踪结果存放到OS ...

  2. 解决无法将java项目部署到tomcat中去

    project facets java转成web项目 用Eclipse开发项目的时候,把一个Web项目导入到Eclipse里会变成了一个Java工程,将无法在Tomcat中进行部署运行. 方法: 1. ...

  3. .gitignore忽略多层文件夹用**

    一.写法 **/bin/Debug/ 前面的两个*号代表任意多层上级文件夹 需要 git 1.8.2 及其以上的版本才支持 如何查看当前版本并且升级(windows) 二.如何升级 git是2.14. ...

  4. 给Oracle字段和表加注释

    给字段加注释 comment on column testtb17.AGE is '年龄';comment on column testtb17.CREATEDTIME is '创建时间';comme ...

  5. [English]常用中英文对照表

    Always have been 一直如此 accordingly:相应地 assumption:假定 brace:大括号 branket:中括号 comma:逗号MISC:Miscellaneous ...

  6. Mosquitto--webServer应用测试结果

    https://blog.csdn.net/u012377333/article/details/71123671 Mosquitto--webServer应用测试结果 一.测试环境介绍 二.测试流程 ...

  7. Ionic4 Cordova 调用原生硬件 Api 实现扫码功能

    QR Scanner 速度快,样式随心所欲,默认只能扫二维码 https://ionicframework.com/docs/native/qr-scanner/ 安装插件 ionic cordova ...

  8. bat实现每天定时执行命令[windows底下每天重启一下Nginx]

    --试验通过--Windows环境脚本名称:restart.bat脚本内容: @echo offtaskkill /f /fi "IMAGENAME eq nginx.exe"cd ...

  9. 软件定义网络基础---OpenFlow协议

    一:OpenFlow协议概述 OpenFlow协议为控制器与交换机之间的通信,提供了一种开放标准的方式.OpenFlow交换机通过安全通道与控制器进行信息交互 二:OpenFlow消息类型 (一)co ...

  10. 【Linux】宝塔上 GitLab数据迁移修改配置后nginx无法启动

    背景: 服务器A 向 服务器B 迁移数据,修改配置重启.发现gitlab的 nginx 无法启动. 查找原因 gitlab-ctl tail 错误信息: 网上查了查,似乎是宝塔的问题, 原因应该是为了 ...