如何快速删除harbor镜像
2021/10/20 23:11:15
本文主要是介绍如何快速删除harbor镜像,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
微信公众号:运维开发故事,作者:华仔
github地址:https://github.com/sunsharing-note/harbor
背景
最近在巡检过程中,发现harbor存储空间使用率已经达到了80%。于是,去看了一下各项目下的镜像标签数。发现有个别项目下的镜像标签数竟然有好几百个。细问之下得知,该项目目前处于调试阶段,每天调试很多次。既然存储空间不多了,那就去harbor上删除掉之前的镜像标签,保留最近的几个就好了。在手动删除的过程中,发现几百个,每页才展示十个。我得先按照推送时间排序,然后一页一页的删除。心想着这种情况经历一次就好了,不要再有下一次。
后来,仔细想想,这个也是不好控制的,每次巡检发现了就得手动删除太麻烦。所以就打算写一个脚本,每次通过脚本去删除镜像的标签,保留最近的几个就好了。刚好最近在学习golang,就用它来写就好了。
比较尴尬的是,我脚本写完了,测试没问题后,发现新版本harbor已经可以在UI上设置保留策略了。自我安慰一下,就当作是一种练习、尝试好了!
目标
- 通过命令行能够查询当前所有的项目、无论是否公开、仓库数量
- 通过命令行能够查询项目下的仓库名和镜像名、拉取次数
- 在命令行能够指定标签和保留个数进行删除镜像标签
- 能够获取镜像的标签数
- 删除后,不支持立刻垃圾清理,请手动进行垃圾清理(考虑清理过程中无法推拉镜像)
声明
- 该脚本纯属个人练习所写,不构成任何建议
- 初学golang,仅仅是为了实现目标,代码质量极差,请谅解
- 本次使用的harbor是v2.3.1
- 全部代码请移步至github
实现
- 获取harbor中所有的项目,API可通过harbor的 swagger获取
//根据harbor swagger测试出来的结果定义要获取的数据结构 type MetaData struct { Public string `json:"public"` } type ProjectData struct { MetaData MetaData `json:"metadata"` ProjectId int `json:"project_id"` Name string `json:"name"` RepoCount int `json:"repo_count"` } type PData []ProjectData // 提供harbor地址获取project func GetProject(url string) []map[string]string { //定义url url = url + "/api/v2.0/projects" //url = url + "/api/projects" // 构造请求 request, _ := http.NewRequest(http.MethodGet, url,nil) //取消验证 tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } //定义客户端 client := &http.Client{Timeout: 10 * time.Second, Transport: tr} //client := &http.Client{Timeout: 10 * time.Second} request.Header.Set("accept", "application/json") //设置用户和密码 request.SetBasicAuth("admin", "Harbor12345") response, err := client.Do(request) if err != nil { fmt.Println("excute failed") fmt.Println(err) } // 获取body body, _ := ioutil.ReadAll(response.Body) defer response.Body.Close() ret := PData{} json.Unmarshal([]byte(string(body)), &ret) var ps = []map[string]string{} // 获取返回的数据 for i := 0; i < len(ret); i++ { RData := make(map[string]string) RData["name"] = (ret[i].Name) RData["project_id"] = strconv.Itoa(ret[i].ProjectId) RData["repo_count"] =strconv.Itoa(ret[i].RepoCount) RData["public"] = ret[i].MetaData.Public ps = append(ps, RData) } return ps }
- 获取项目下的repo
// 定义要获取的数据结构 type ReposiData struct { Id int `json:"id"` Name string `json:"name"` ProjectId int `json:"project_id"` PullCount int `json:"pull_count"` } type RepoData []ReposiData //通过提供harbor地址和对应的项目来获取项目下的repo func GetRepoData(url string, proj string) []map[string]string { // /api/v2.0/projects/goharbor/repositories url = url + "/api/v2.0/projects/" + proj + "/repositories" //构造请求 request, _ := http.NewRequest(http.MethodGet, url,nil) //忽略认证 tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := &http.Client{Timeout: 10 * time.Second, Transport: tr} request.Header.Set("accept", "application/json") //设置用户名和密码 request.SetBasicAuth("admin", "Harbor12345") response, err := client.Do(request) if err != nil { fmt.Println("excute failed") fmt.Println(err) } // 获取body body, _ := ioutil.ReadAll(response.Body) defer response.Body.Close() ret := RepoData{} json.Unmarshal([]byte(string(body)), &ret) var ps = []map[string]string{} // 获取返回的数据 for i := 0; i < len(ret); i++ { RData := make(map[string]string) RData["name"] = (ret[i].Name) pId := strconv.Itoa(ret[i].ProjectId) RData["project_id"] = pId RData["id"] =(strconv.Itoa(ret[i].Id)) RData["pullCount"] = (strconv.Itoa(ret[i].PullCount)) ps = append(ps, RData) } return ps }
- 镜像tag操作
//定义要获取的tag数据结构 type Tag struct { ArtifactId int `json:"artifact_id"` Id int `json:"id"` Name string `json:"name"` RepositoryId int `json:"repository_id"` PushTimte string `json:"push_time"` } type Tag2 struct { ArtifactId string `json:"artifact_id"` Id string `json:"id"` Name string `json:"name"` RepositoryId string `json:"repository_id"` PushTimte string `json:"push_time"` } type Tag2s []Tag2 // delete tag by specified count,这里通过count先获取要删除的tag列表 func DeleTagsByCount(tags []map[string]string ,count int) []string { var re []string tt := tags[0]["tags"] ss := Tag2s{} json.Unmarshal([]byte(tt), &ss) // have a sort for i := 0; i < len(ss); i++ { for j := i + 1; j < len(ss); j++ { //根据pushtime进行排序 if ss[i].PushTimte > ss[j].PushTimte { ss[i], ss[j] = ss[j], ss[i] } } } // get all tags for i := 0; i < len(ss); i++ { re = append(re, ss[i].Name) } // 返回count个会被删除的tag, return re[0:count] } // delete tag by specified tag 删除指定的tag func DelTags(url string, project string, repo string, tag string) (int, map[string]interface{}) { url = url + "/api/v2.0/projects/" + project + "/repositories/" + repo + "/artifacts/" + tag + "/tags/" + tag request, _ := http.NewRequest(http.MethodDelete, url,nil) tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := &http.Client{Timeout: 10 * time.Second, Transport: tr} request.Header.Set("accept", "application/json") request.SetBasicAuth("admin", "Pwd123456") // 执行删除tag response,_ := client.Do(request) defer response.Body.Close() var result map[string]interface{} bd, err := ioutil.ReadAll(response.Body) if err == nil { err = json.Unmarshal(bd, &result) } return response.StatusCode,result } //定义要获取的tag数据结构 type ArtiData struct { Id int `json:"id"` ProjectId int `json:"project_id"` RepositoryId int `json:"repository_id"` //Digest string `json:"digest"` Tags []Tag `json:"tags"` } type AData []ArtiData // 根据harbor地址、project和repo获取tag数据 func GetTags(url string, project string, repo string) []map[string]string { url = url + "/api/v2.0/projects/" + project + "/repositories/" + repo + "/artifacts" request, _ := http.NewRequest(http.MethodGet, url,nil) tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := &http.Client{Timeout: 10 * time.Second, Transport: tr} request.Header.Set("accept", "application/json") request.Header.Set("X-Accept-Vulnerabilities", "application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0") request.SetBasicAuth("admin", "Harbor12345") // 获取tag response, err := client.Do(request) if err != nil { fmt.Println("excute failed") fmt.Println(err) } body, _ := ioutil.ReadAll(response.Body) defer response.Body.Close() ret := AData{} json.Unmarshal([]byte(string(body)),&ret) var ps = []map[string]string{} sum := 0 RData := make(map[string]string) RData["name"] = repo // 获取返回的数据 for i := 0; i < len(ret); i++ { RData["id"] = (strconv.Itoa(ret[i].Id)) RData["project_id"] = (strconv.Itoa(ret[i].ProjectId)) RData["repository_id"] =(strconv.Itoa(ret[i].RepositoryId)) //RData["digest"] = ret[i].Digest var tdata = []map[string]string{} sum = len((ret[i].Tags)) // 获取tag for j := 0; j < len((ret[i].Tags)); j++ { TagData := make(map[string]string) TagData["artifact_id"] = strconv.Itoa((ret[i].Tags)[j].ArtifactId) TagData["id"] = strconv.Itoa((ret[i].Tags)[j].Id) TagData["name"] = (ret[i].Tags)[j].Name TagData["repository_id"] = strconv.Itoa((ret[i].Tags)[j].RepositoryId) TagData["push_time"] = (ret[i].Tags)[j].PushTimte tdata = append(tdata, TagData) } RData["count"] = strconv.Itoa(sum) ss, err := json.Marshal(tdata) if err != nil { fmt.Println("failed") os.Exit(2) } RData["tags"] = string(ss) ps = append(ps, RData) } return ps }
- 获取用户命令行输入,列出harbor中所有的项目
// 定义获取harbor中project的相关命令操作 var projectCmd = &cobra.Command{ Use: "project", Short: "to operator project", Run: func(cmd *cobra.Command, args []string) { output, err := ExecuteCommand("harbor","project", args...) if err != nil { Error(cmd,args, err) } fmt.Fprint(os.Stdout, output) }, } // project list var projectLsCmd = &cobra.Command{ Use: "ls", Short: "list all project", Run: func(cmd *cobra.Command, args []string) { url, _ := cmd.Flags().GetString("url") if len(url) == 0 { fmt.Println("url is null,please specified the harbor url first !!!!") os.Exit(2) } // 获取所有的project output := harbor.GetProject(url) fmt.Println("项目名 访问级别 仓库数量") for i := 0; i < len(output); i++ { fmt.Println(output[i]["name"], output[i]["public"], output[i]["repo_count"]) } }, } // init func init() { // ./harbor project ls -u https:// rootCmd.AddCommand(projectCmd) projectCmd.AddCommand(projectLsCmd) projectLsCmd.Flags().StringP("url", "u", "","defaults: [https://127.0.0.1]") }
- 获取repo列表
// repo command var repoCmd = &cobra.Command{ Use: "repo", Short: "to operator repository", Run: func(cmd *cobra.Command, args []string) { output, err := ExecuteCommand("harbor","repo", args...) if err != nil { Error(cmd,args, err) } fmt.Fprint(os.Stdout, output) }, } // repo list var repoLsCmd = &cobra.Command{ Use: "ls", Short: "list project's repository", Run: func(cmd *cobra.Command, args []string) { url, _ := cmd.Flags().GetString("url") project, _ := cmd.Flags().GetString("project") if len(project) == 0 { fmt.Println("sorry, you must specified the project which you want to show repository !!!") os.Exit(2) } // get all repo output := harbor.GetRepoData(url, project) // 展示数据 fmt.Println("仓库名----------拉取次数") for i := 0; i < len(output); i++ { fmt.Println(output[i]["name"],output[i]["pullCount"]) } }, } func init() { // ./harbor repo ls -u https:// -p xxx rootCmd.AddCommand(repoCmd) repoCmd.AddCommand(repoLsCmd) repoLsCmd.Flags().StringP("url", "u", "","defaults: [https://127.0.0.1]") repoLsCmd.Flags().StringP("project", "p","", "the project") }
- tag操作
// tag command var tagCmd = &cobra.Command{ Use: "tag", Short: "to operator image", Run: func(cmd *cobra.Command, args []string) { output, err := ExecuteCommand("harbor","tag", args...) if err != nil { Error(cmd,args, err) } fmt.Fprint(os.Stdout, output) }, } // tag ls var tagLsCmd = &cobra.Command{ Use: "ls", Short: "list all tags of the repository you have specified which you should specified project at the same time", Run: func(cmd *cobra.Command, args []string) { url, _ := cmd.Flags().GetString("url") project, _ := cmd.Flags().GetString("project") repo, _ := cmd.Flags().GetString("repo") // get all tags ss := harbor.GetTags(url, project, repo) for i := 0; i < len(ss); i++ { count, _ := strconv.Atoi((ss[i])["count"]) fmt.Printf("the repo %s has %d images\n", repo, count) } }, } // tag del by tag or the number of image you want to save var tagDelCmd = &cobra.Command{ Use: "del", Short: "delete the tags of the repository you have specified which you should specified project at the same time", Run: func(cmd *cobra.Command, args []string) { // 获取用户输入并转格式 url, _ := cmd.Flags().GetString("url") project, _ := cmd.Flags().GetString("project") repo, _ := cmd.Flags().GetString("repo") tag,_ := cmd.Flags().GetString("tag") count,_ := cmd.Flags().GetString("count") ret,_ := strconv.Atoi(count) if len(tag) != 0 && ret != 0 { fmt.Println("You can't choose both between count and tag") os.Exit(2) } else if len(tag) == 0 && ret != 0 { // get all tags retu := harbor.GetTags(url, project, repo) //delete tag by you hsve specied the number of the images you want to save rTagCount, _ := strconv.Atoi((retu[0])["count"]) // 根据用户输入的count和实际tag数进行对比,决定是否去执行删除tag if ret == rTagCount { fmt.Printf("the repository %s of the project %s only have %d tags, so you can't delete tags and we will do nothing!!\n", repo, project,ret) } else if ret > rTagCount { fmt.Printf("the repository %s of the project %s only have %d tags, but you want to delete %d tags, so we suggest you to have a rest and we will do nothing!!\n", repo, project,rTagCount, ret) } else { // 可以执行删除tag fmt.Printf("we will save the latest %d tags and delete other %d tags !!!\n", ret, (rTagCount - ret)) tags := harbor.GetTags(url, project, repo) retu := harbor.DeleTagsByCount(tags, (rTagCount - ret)) for i := 0 ; i < len(retu); i++ { // to delete tag code, msg := harbor.DelTags(url, project, repo, retu[i]) fmt.Printf("the tag %s is deleted,status code is %d, msg is %s\n", retu[i], code, msg) } } } else { // delete tag by you specied tag code, msg := harbor.DelTags(url, project, repo, tag) fmt.Println(code, msg["errors"]) } }, } func init() { // ./harbor tag ls -u -p -r rootCmd.AddCommand(tagCmd) tagCmd.AddCommand(tagLsCmd) tagLsCmd.Flags().StringP("url", "u", "","defaults: [https://127.0.0.1]") tagLsCmd.Flags().StringP("project", "p", "","the project") tagLsCmd.Flags().StringP("repo", "r", "","the repository") // ./harbor tag del -u -p -r [-t | -c] tagCmd.AddCommand(tagDelCmd) tagDelCmd.Flags().StringP("url", "u", "","defaults: [https://127.0.0.1]") tagDelCmd.Flags().StringP("project", "p", "","the project which you should specified if you want to delete the tag of any repository ") tagDelCmd.Flags().StringP("repo", "r", "","the repository which you should specified if you want to delete the tag") tagDelCmd.Flags().StringP("tag", "t", "","the tag, You can't choose it with tag together") tagDelCmd.Flags().StringP("count", "c", "","the total number you want to save.for example: you set --count=10, we will save the 10 latest tags by use push_time to sort,can't choose it with tag together") }
测试
//获取帮助 harbor % ./harbor -h https://harbor.zaizai.com Usage: harbor [flags] harbor [command] Available Commands: completion generate the autocompletion script for the specified shell help Help about any command project to operator project repo to operator repository tag to operator image Flags: -h, --help help for harbor Use "harbor [command] --help" for more information about a command. //列出所有project harbor % ./harbor project ls -u https://harbor.zaizai.com 项目名 访问级别 仓库数量 goharbor false 3 library true 0 public true 1 //列出所有repo harbor % ./harbor repo ls -u https://harbor.zaizai.com -p goharbor 仓库名----------拉取次数 goharbor/harbor-portal 0 goharbor/harbor-db 1 goharbor/prepare 0 //列出tags harbor % ./harbor tag ls -u https://harbor.zaizai.com -p goharbor -r harbor-db the repo harbor-db has 9 images // 通过保留最近20个镜像去删除tag harbor % ./harbor tag del -u https://harbor.zaizai.com -p goharbor -r harbor-db -c 20 the repository harbor-db of the project goharbor only have 9 tags, but you want to delete 20 tags, so we suggest you to have a rest and we will do nothing!! // 通过保留最近10个镜像去删除tag harbor % ./harbor tag del -u https://harbor.zaizai.com -p goharbor -r harbor-db -c 10 the repository harbor-db of the project goharbor only have 9 tags, but you want to delete 10 tags, so we suggest you to have a rest and we will do nothing!! // 通过保留最近5个镜像去删除tag harbor % ./harbor tag del -u https://harbor.zaizai.com -p goharbor -r harbor-db -c 5 we will save the latest 5 tags and delete other 4 tags !!! the tag v2.3.9 is deleted,status code is 200, msg is map[] the tag v2.3.10 is deleted,status code is 200, msg is map[] the tag v2.3.8 is deleted,status code is 200, msg is map[] the tag v2.3.7 is deleted,status code is 200, msg is map[] //指定tag进行删除 caicloud@MacBook-Pro-2 harbor % ./harbor tag del -u https://harbor.zaizai.com -p goharbor -r harbor-db -t v2.3.6 200 <nil> !!!! 最后需要手动去harbor UI上进行垃圾回收!!!
参考
- https://github.com/spf13/cobra
- harbor swagger
这篇关于如何快速删除harbor镜像的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南