成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何快速刪除 Harbor 鏡像

開發 項目管理
細問之下得知,該項目目前處于調試階段,每天調試很多次。既然存儲空間不多了,那就去harbor上刪除掉之前的鏡像標簽,保留最近的幾個就好了。在手動刪除的過程中,發現幾百個,每頁才展示十個。我得先按照推送時間排序,然后一頁一頁的刪除。心想著這種情況經歷一次就好了,不要再有下一次。

[[429946]]

本文轉載自微信公眾號「運維開發故事」,作者華仔。轉載本文請聯系運維開發故事公眾號。

背景

最近在巡檢過程中,發現harbor存儲空間使用率已經達到了80%。于是,去看了一下各項目下的鏡像標簽數。發現有個別項目下的鏡像標簽數竟然有好幾百個。細問之下得知,該項目目前處于調試階段,每天調試很多次。既然存儲空間不多了,那就去harbor上刪除掉之前的鏡像標簽,保留最近的幾個就好了。在手動刪除的過程中,發現幾百個,每頁才展示十個。我得先按照推送時間排序,然后一頁一頁的刪除。心想著這種情況經歷一次就好了,不要再有下一次。后來,仔細想想,這個也是不好控制的,每次巡檢發現了就得手動刪除太麻煩。所以就打算寫一個腳本,每次通過腳本去刪除鏡像的標簽,保留最近的幾個就好了。剛好最近在學習golang,就用它來寫就好了。比較尷尬的是,我腳本寫完了,測試沒問題后,發現新版本harbor已經可以在UI上設置保留策略了。自我安慰一下,就當作是一種練習、嘗試好了!

目標

  1. 通過命令行能夠查詢當前所有的項目、無論是否公開、倉庫數量
  2. 通過命令行能夠查詢項目下的倉庫名和鏡像名、拉取次數
  3. 在命令行能夠指定標簽和保留個數進行刪除鏡像標簽
  4. 能夠獲取鏡像的標簽數
  5. 刪除后,不支持立刻垃圾清理,請手動進行垃圾清理(考慮清理過程中無法推拉鏡像)

聲明

該腳本純屬個人練習所寫,不構成任何建議

初學golang,僅僅是為了實現目標,代碼質量極差,請諒解

本次使用的harbor是v2.3.1

全部代碼請移步至github

實現

獲取harbor中所有的項目,API可通過harbor的 swagger獲取

  1. //根據harbor swagger測試出來的結果定義要獲取的數據結構 
  2. type MetaData struct { 
  3.  Public string `json:"public"
  4. type ProjectData struct { 
  5.  MetaData MetaData `json:"metadata"
  6.  ProjectId int `json:"project_id"
  7.  Name string `json:"name"
  8.  RepoCount int `json:"repo_count"
  9.  
  10. type PData []ProjectData 
  11. // 提供harbor地址獲取project 
  12. func GetProject(url string) []map[string]string { 
  13.  //定義url 
  14.  url = url + "/api/v2.0/projects" 
  15.  //url = url + "/api/projects" 
  16.     // 構造請求 
  17.  request, _ := http.NewRequest(http.MethodGet, url,nil) 
  18.     //取消驗證 
  19.  tr := &http.Transport{ 
  20.   TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 
  21.  } 
  22.     //定義客戶端 
  23.  client := &http.Client{Timeout: 10 * time.Second, Transport: tr} 
  24.  //client := &http.Client{Timeout: 10 * time.Second
  25.  request.Header.Set("accept""application/json"
  26.     //設置用戶和密碼 
  27.  request.SetBasicAuth("admin""Harbor12345"
  28.  response, err := client.Do(request) 
  29.  
  30.  if err != nil { 
  31.   fmt.Println("excute failed"
  32.   fmt.Println(err) 
  33.  } 
  34.  // 獲取body 
  35.  body, _ := ioutil.ReadAll(response.Body) 
  36.  defer response.Body.Close() 
  37.  ret := PData{} 
  38.  json.Unmarshal([]byte(string(body)), &ret) 
  39.  var ps = []map[string]string{} 
  40.     // 獲取返回的數據 
  41.  for i := 0; i < len(ret); i++ { 
  42.   RData := make(map[string]string) 
  43.   RData["name"] = (ret[i].Name
  44.   RData["project_id"] = strconv.Itoa(ret[i].ProjectId) 
  45.   RData["repo_count"] =strconv.Itoa(ret[i].RepoCount) 
  46.   RData["public"] = ret[i].MetaData.Public 
  47.  
  48.   ps = append(ps, RData) 
  49.  } 
  50.  return ps 

獲取項目下的repo

  1. // 定義要獲取的數據結構 
  2. type ReposiData struct { 
  3.  Id int `json:"id"
  4.  Name string `json:"name"
  5.  ProjectId int `json:"project_id"
  6.  PullCount int `json:"pull_count"
  7.  
  8. type RepoData []ReposiData 
  9. //通過提供harbor地址和對應的項目來獲取項目下的repo 
  10. func GetRepoData(url string, proj string)  []map[string]string { 
  11.  // /api/v2.0/projects/goharbor/repositories 
  12.  url = url + "/api/v2.0/projects/" + proj +  "/repositories" 
  13.     //構造請求 
  14.  request, _ := http.NewRequest(http.MethodGet, url,nil) 
  15.     //忽略認證 
  16.  tr := &http.Transport{ 
  17.   TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 
  18.  } 
  19.  client := &http.Client{Timeout: 10 * time.Second, Transport: tr} 
  20.  request.Header.Set("accept""application/json"
  21.     //設置用戶名和密碼 
  22.  request.SetBasicAuth("admin""Harbor12345"
  23.  response, err := client.Do(request) 
  24.  
  25.  if err != nil { 
  26.   fmt.Println("excute failed"
  27.   fmt.Println(err) 
  28.  } 
  29.  // 獲取body 
  30.  body, _ := ioutil.ReadAll(response.Body) 
  31.  defer response.Body.Close() 
  32.  ret := RepoData{} 
  33.  json.Unmarshal([]byte(string(body)), &ret) 
  34.  var ps = []map[string]string{} 
  35.     // 獲取返回的數據 
  36.  for i := 0; i < len(ret); i++ { 
  37.   RData := make(map[string]string) 
  38.   RData["name"] = (ret[i].Name
  39.   pId := strconv.Itoa(ret[i].ProjectId) 
  40.   RData["project_id"] = pId 
  41.   RData["id"] =(strconv.Itoa(ret[i].Id)) 
  42.   RData["pullCount"] = (strconv.Itoa(ret[i].PullCount)) 
  43.   ps = append(ps, RData) 
  44.  } 
  45.  return ps 

鏡像tag操作

  1. //定義要獲取的tag數據結構 
  2. type Tag struct { 
  3.  ArtifactId int  `json:"artifact_id"
  4.  Id int `json:"id"
  5.  Name string `json:"name"
  6.  RepositoryId int `json:"repository_id"
  7.  PushTimte string `json:"push_time"
  8.  
  9. type Tag2 struct { 
  10.  ArtifactId string  `json:"artifact_id"
  11.  Id string `json:"id"
  12.  Name string `json:"name"
  13.  RepositoryId string `json:"repository_id"
  14.  PushTimte string `json:"push_time"
  15.  
  16. type Tag2s []Tag2 
  17. // delete tag by specified count,這里通過count先獲取要刪除的tag列表 
  18. func DeleTagsByCount(tags []map[string]string ,count int) []string { 
  19.  var re []string 
  20.   
  21.  tt := tags[0]["tags"
  22.  ss := Tag2s{} 
  23.  json.Unmarshal([]byte(tt), &ss) 
  24.  
  25.  // have a sort 
  26.  for i := 0; i < len(ss); i++ { 
  27.   for j := i + 1; j < len(ss); j++ { 
  28.             //根據pushtime進行排序 
  29.    if ss[i].PushTimte > ss[j].PushTimte { 
  30.     ss[i], ss[j] = ss[j], ss[i] 
  31.    } 
  32.   } 
  33.  } 
  34.  // get all tags 
  35.  for i := 0; i < len(ss); i++ { 
  36.   re = append(re, ss[i].Name
  37.  } 
  38.  // 返回count個會被刪除的tag, 
  39.  return re[0:count
  40.  
  41. // delete tag by specified tag 刪除指定的tag 
  42. func DelTags(url string, project string, repo string, tag string) (int, map[string]interface{})  { 
  43.  url = url + "/api/v2.0/projects/" + project + "/repositories/" + repo + "/artifacts/" + tag + "/tags/" + tag 
  44.  request, _ := http.NewRequest(http.MethodDelete, url,nil) 
  45.  tr := &http.Transport{ 
  46.   TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 
  47.  } 
  48.  client := &http.Client{Timeout: 10 * time.Second, Transport: tr} 
  49.  request.Header.Set("accept""application/json"
  50.  request.SetBasicAuth("admin""Pwd123456"
  51.     // 執行刪除tag 
  52.  response,_ := client.Do(request) 
  53.  defer response.Body.Close() 
  54.  
  55.  var result map[string]interface{} 
  56.  bd, err := ioutil.ReadAll(response.Body) 
  57.  if err == nil { 
  58.   err = json.Unmarshal(bd, &result) 
  59.  } 
  60.  return response.StatusCode,result 
  61.  
  62.  
  63.  
  64.  
  65.  
  66. //定義要獲取的tag數據結構 
  67. type ArtiData struct { 
  68.  Id int `json:"id"
  69.  ProjectId int `json:"project_id"
  70.  RepositoryId int `json:"repository_id"
  71.  //Digest string `json:"digest"
  72.  Tags []Tag `json:"tags"
  73.  
  74. type AData []ArtiData 
  75. // 根據harbor地址、project和repo獲取tag數據 
  76. func GetTags(url string, project string, repo string) []map[string]string { 
  77.  url = url + "/api/v2.0/projects/" + project + "/repositories/" + repo + "/artifacts" 
  78.  request, _ := http.NewRequest(http.MethodGet, url,nil) 
  79.  tr := &http.Transport{ 
  80.   TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 
  81.  } 
  82.  client := &http.Client{Timeout: 10 * time.Second, Transport: tr} 
  83.  request.Header.Set("accept""application/json"
  84.  request.Header.Set("X-Accept-Vulnerabilities""application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0"
  85.  request.SetBasicAuth("admin""Harbor12345"
  86.     // 獲取tag 
  87.  response, err := client.Do(request) 
  88.  
  89.  if err != nil { 
  90.   fmt.Println("excute failed"
  91.   fmt.Println(err) 
  92.  } 
  93.  
  94.  body, _ := ioutil.ReadAll(response.Body) 
  95.  defer response.Body.Close() 
  96.  ret := AData{} 
  97.  json.Unmarshal([]byte(string(body)),&ret) 
  98.  var ps = []map[string]string{} 
  99.  sum := 0 
  100.  RData := make(map[string]string) 
  101.  RData["name"] = repo 
  102.     // 獲取返回的數據 
  103.  for i := 0; i < len(ret); i++ { 
  104.  
  105.   RData["id"] = (strconv.Itoa(ret[i].Id)) 
  106.   RData["project_id"] = (strconv.Itoa(ret[i].ProjectId)) 
  107.   RData["repository_id"] =(strconv.Itoa(ret[i].RepositoryId)) 
  108.   //RData["digest"] = ret[i].Digest 
  109.   var tdata = []map[string]string{} 
  110.   sum = len((ret[i].Tags)) 
  111.         // 獲取tag 
  112.   for j := 0; j < len((ret[i].Tags)); j++ { 
  113.    TagData := make(map[string]string) 
  114.    TagData["artifact_id"] = strconv.Itoa((ret[i].Tags)[j].ArtifactId) 
  115.    TagData["id"] = strconv.Itoa((ret[i].Tags)[j].Id) 
  116.    TagData["name"] = (ret[i].Tags)[j].Name 
  117.    TagData["repository_id"] = strconv.Itoa((ret[i].Tags)[j].RepositoryId) 
  118.    TagData["push_time"] = (ret[i].Tags)[j].PushTimte 
  119.    tdata = append(tdata, TagData) 
  120.   } 
  121.   RData["count"] = strconv.Itoa(sum
  122.   ss, err := json.Marshal(tdata) 
  123.   if err != nil { 
  124.    fmt.Println("failed"
  125.    os.Exit(2) 
  126.   } 
  127.   RData["tags"] = string(ss) 
  128.   ps = append(ps, RData) 
  129.  
  130.  } 
  131.  return ps 

獲取用戶命令行輸入,列出harbor中所有的項目

  1. // 定義獲取harbor中project的相關命令操作 
  2. var projectCmd = &cobra.Command{ 
  3.  Use: "project"
  4.  Short: "to operator project"
  5.  Run: func(cmd *cobra.Command, args []string) { 
  6.  
  7.   output, err := ExecuteCommand("harbor","project", args...) 
  8.   if err != nil { 
  9.    Error(cmd,args, err) 
  10.   } 
  11.   fmt.Fprint(os.Stdout, output
  12.  }, 
  13. // project list 
  14. var projectLsCmd = &cobra.Command{ 
  15.  Use: "ls"
  16.  Short: "list  all project"
  17.  Run: func(cmd *cobra.Command, args []string) { 
  18.   url, _ := cmd.Flags().GetString("url"
  19.   if len(url) == 0 { 
  20.    fmt.Println("url is null,please specified the harbor url first !!!!"
  21.    os.Exit(2) 
  22.   } 
  23.         // 獲取所有的project 
  24.   output := harbor.GetProject(url) 
  25.   fmt.Println("項目名 訪問級別 倉庫數量"
  26.   for i := 0; i < len(output); i++ { 
  27.  
  28.    fmt.Println(output[i]["name"], output[i]["public"], output[i]["repo_count"]) 
  29.   } 
  30.  }, 
  31. // init 
  32. func init() { 
  33.     // ./harbor project ls -u https:// 
  34.  rootCmd.AddCommand(projectCmd) 
  35.  projectCmd.AddCommand(projectLsCmd) 
  36.  projectLsCmd.Flags().StringP("url""u""","defaults: [https://127.0.0.1]"

獲取repo列表

  1. // repo command 
  2. var repoCmd = &cobra.Command{ 
  3.  Use: "repo"
  4.  Short: "to operator repository"
  5.  Run: func(cmd *cobra.Command, args []string) { 
  6.  
  7.   output, err := ExecuteCommand("harbor","repo", args...) 
  8.   if err != nil { 
  9.    Error(cmd,args, err) 
  10.   } 
  11.   fmt.Fprint(os.Stdout, output
  12.  }, 
  13. // repo list 
  14. var repoLsCmd = &cobra.Command{ 
  15.  Use: "ls"
  16.  Short: "list  project's repository"
  17.  Run: func(cmd *cobra.Command, args []string) { 
  18.   url, _ := cmd.Flags().GetString("url"
  19.   project, _ := cmd.Flags().GetString("project"
  20.   if len(project) == 0 { 
  21.    fmt.Println("sorry, you must specified the project which you want to show repository !!!"
  22.    os.Exit(2) 
  23.   } 
  24.         // get all repo  
  25.   output := harbor.GetRepoData(url, project) 
  26.         // 展示數據 
  27.   fmt.Println("倉庫名----------拉取次數"
  28.   for i := 0; i < len(output); i++ { 
  29.    fmt.Println(output[i]["name"],output[i]["pullCount"]) 
  30.   } 
  31.  }, 
  32.  
  33. func init() { 
  34.     // ./harbor repo ls -u https:// -p xxx 
  35.  rootCmd.AddCommand(repoCmd) 
  36.  repoCmd.AddCommand(repoLsCmd) 
  37.  repoLsCmd.Flags().StringP("url""u""","defaults: [https://127.0.0.1]"
  38.  repoLsCmd.Flags().StringP("project""p","""the project"

tag操作

  1. // tag command 
  2. var tagCmd = &cobra.Command{ 
  3.  Use: "tag"
  4.  Short: "to operator image"
  5.  Run: func(cmd *cobra.Command, args []string) { 
  6.  
  7.   output, err := ExecuteCommand("harbor","tag", args...) 
  8.   if err != nil { 
  9.    Error(cmd,args, err) 
  10.   } 
  11.   fmt.Fprint(os.Stdout, output
  12.  }, 
  13. // tag ls 
  14. var tagLsCmd = &cobra.Command{ 
  15.  Use: "ls"
  16.  Short: "list  all tags of the repository you have specified which you should specified project at the same time"
  17.  Run: func(cmd *cobra.Command, args []string) { 
  18.   url, _ := cmd.Flags().GetString("url"
  19.   project, _ := cmd.Flags().GetString("project"
  20.   repo, _ := cmd.Flags().GetString("repo"
  21.         // get all tags  
  22.   ss := harbor.GetTags(url, project, repo) 
  23.   for i := 0; i < len(ss); i++ { 
  24.    count, _ := strconv.Atoi((ss[i])["count"]) 
  25.    fmt.Printf("the repo %s has %d images\n", repo, count
  26.   } 
  27.  
  28.  }, 
  29.  
  30. // tag del by tag or the number of image you want to save 
  31. var tagDelCmd = &cobra.Command{ 
  32.  Use: "del"
  33.  Short: "delete the tags of the repository you have specified which you should specified project at the same time"
  34.  Run: func(cmd *cobra.Command, args []string) { 
  35.         // 獲取用戶輸入并轉格式 
  36.   url, _ := cmd.Flags().GetString("url"
  37.   project, _ := cmd.Flags().GetString("project"
  38.   repo, _ := cmd.Flags().GetString("repo"
  39.   tag,_ := cmd.Flags().GetString("tag"
  40.   count,_ := cmd.Flags().GetString("count"
  41.   ret,_ := strconv.Atoi(count
  42.   if len(tag) != 0 && ret != 0 { 
  43.     fmt.Println("You can't choose both between count and tag"
  44.     os.Exit(2) 
  45.   } else if len(tag) == 0 && ret != 0 { 
  46.             // get all tags 
  47.    retu := harbor.GetTags(url, project, repo) 
  48.             //delete tag by you hsve specied the number of the images you want to save 
  49.    rTagCount, _ := strconv.Atoi((retu[0])["count"]) 
  50.             // 根據用戶輸入的count和實際tag數進行對比,決定是否去執行刪除tag 
  51.    if ret == rTagCount { 
  52.     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) 
  53.    } else if ret > rTagCount { 
  54.     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) 
  55.    } else { 
  56.                 // 可以執行刪除tag 
  57.     fmt.Printf("we will save the latest %d tags  and delete other %d tags !!!\n", ret, (rTagCount - ret)) 
  58.     tags := harbor.GetTags(url, project, repo) 
  59.     retu := harbor.DeleTagsByCount(tags, (rTagCount - ret)) 
  60.     for i := 0 ; i < len(retu); i++ { 
  61.                     // to delete tag 
  62.      code, msg := harbor.DelTags(url, project, repo, retu[i]) 
  63.      fmt.Printf("the tag %s is deleted,status code is %d, msg is %s\n", retu[i], code, msg) 
  64.     } 
  65.    } 
  66.   } else { 
  67.             // delete tag by you specied tag 
  68.    code, msg := harbor.DelTags(url, project, repo, tag) 
  69.    fmt.Println(code, msg["errors"]) 
  70.   } 
  71.  }, 
  72.  
  73.  
  74.  
  75. func init() { 
  76.     // ./harbor tag ls -u -p -r 
  77.  rootCmd.AddCommand(tagCmd) 
  78.  tagCmd.AddCommand(tagLsCmd) 
  79.  tagLsCmd.Flags().StringP("url""u""","defaults: [https://127.0.0.1]"
  80.  tagLsCmd.Flags().StringP("project""p""","the project"
  81.  tagLsCmd.Flags().StringP("repo""r""","the repository"
  82.  // ./harbor tag del -u -p -r [-t | -c] 
  83.  tagCmd.AddCommand(tagDelCmd) 
  84.  tagDelCmd.Flags().StringP("url""u""","defaults: [https://127.0.0.1]"
  85.  tagDelCmd.Flags().StringP("project""p""","the project which you should specified if you want to delete the tag of any repository "
  86.  tagDelCmd.Flags().StringP("repo""r""","the repository which you should specified if you want to delete the tag"
  87.  tagDelCmd.Flags().StringP("tag""t""","the tag, You can't choose  it with tag together"
  88.  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"

測試

  1. //獲取幫助 
  2. harbor % ./harbor -h https://harbor.zaizai.com 
  3. Usage: 
  4.   harbor [flags] 
  5.   harbor [command] 
  6.  
  7. Available Commands: 
  8.   completion  generate the autocompletion script for the specified shell 
  9.   help        Help about any command 
  10.   project     to operator project 
  11.   repo        to operator repository 
  12.   tag         to operator image 
  13.  
  14. Flags: 
  15.   -h, --help   help for harbor 
  16.  
  17. Use "harbor [command] --help" for more information about a command. 
  18.  
  19. //列出所有project 
  20.  harbor % ./harbor  project ls -u https://harbor.zaizai.com 
  21. 項目名 訪問級別 倉庫數量 
  22. goharbor false 3 
  23. library true 0 
  24. public true 1 
  25.  
  26. //列出所有repo 
  27. harbor % ./harbor  repo  ls -u https://harbor.zaizai.com -p goharbor 
  28. 倉庫名----------拉取次數 
  29. goharbor/harbor-portal 0 
  30. goharbor/harbor-db 1 
  31. goharbor/prepare 0 
  32.  
  33. //列出tags harbor % ./harbor  tag   ls -u https://harbor.zaizai.com -p goharbor -r harbor-db 
  34. the repo harbor-db has 9 images 
  35.  
  36. // 通過保留最近20個鏡像去刪除tag 
  37. harbor % ./harbor  tag   del  -u https://harbor.zaizai.com -p goharbor -r harbor-db -c 20     
  38. 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!! 
  39.  
  40. // 通過保留最近10個鏡像去刪除tag 
  41. harbor % ./harbor  tag   del  -u https://harbor.zaizai.com -p goharbor -r harbor-db -c 10 
  42. 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!! 
  43.  
  44. // 通過保留最近5個鏡像去刪除tag 
  45. harbor % ./harbor  tag   del  -u https://harbor.zaizai.com -p goharbor -r harbor-db -c 5  
  46. we will save the latest 5 tags  and delete other 4 tags !!! 
  47. the tag v2.3.9 is deleted,status code is 200, msg is map[] 
  48. the tag v2.3.10 is deleted,status code is 200, msg is map[] 
  49. the tag v2.3.8 is deleted,status code is 200, msg is map[] 
  50. the tag v2.3.7 is deleted,status code is 200, msg is map[] 
  51.  
  52. //指定tag進行刪除 
  53. caicloud@MacBook-Pro-2 harbor % ./harbor tag   del  -u https://harbor.zaizai.com -p goharbor -r harbor-db  -t v2.3.6 
  54. 200 <nil> 
  55.  
  56. !!!! 最后需要手動去harbor UI上進行垃圾回收!!! 

參考

https://github.com/spf13/cobra

 

harbor swagger

 

責任編輯:武曉燕 來源: 運維開發故事
相關推薦

2022-07-11 07:37:55

HarborContainerd

2022-09-16 10:19:36

HarborContainerd

2017-03-24 09:24:21

HarborDocker鏡像倉庫

2024-01-22 09:12:51

2021-12-22 10:45:56

Docker RegiHarborLinux

2021-02-23 15:05:55

Docker鏡像開發

2020-11-25 08:57:29

Redis Docke

2021-02-03 10:43:54

Linux系統磁盤

2023-04-06 10:08:58

2010-09-01 17:19:49

SQL刪除TABLE

2018-08-29 12:30:45

數據庫MySQLInnodb

2010-09-02 11:38:44

SQL刪除

2023-04-06 09:53:21

Harbor企業級服務

2011-05-13 11:05:59

oracle刪除

2022-08-21 16:50:36

Kubeadm?Kubernetes

2010-07-02 13:50:11

SQL Server數

2023-03-03 00:07:24

2019-07-30 16:16:11

Windows 10刪除快速訪問

2021-09-23 10:30:21

Docker RegiHarborLinux

2010-09-16 16:17:03

TRUNCATE TA
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99热在线播放 | 日本免费一区二区三区四区 | 成人日韩av| 国产在线视频一区二区董小宛性色 | 81精品国产乱码久久久久久 | 黄色三级毛片 | 天天干天天干 | 情侣酒店偷拍一区二区在线播放 | 日韩成人一区 | 成人免费视频网站 | 欧美一级片 | www.亚洲一区 | 国产精品久久久久久福利一牛影视 | 国产精品无码久久久久 | 久久国产婷婷国产香蕉 | 精品国产青草久久久久福利 | 极品在线 | 中文字幕日韩专区 | 久久久久亚洲视频 | 在线免费观看黄色 | 久久久久久久久国产精品 | 97伦理最新伦理 | 97国产成人| 亚洲狠狠爱| 亚洲精品一区在线 | 国产精品欧美一区二区 | 亚洲国产精品一区二区www | 久久精品欧美一区二区三区不卡 | 亚洲视频在线一区 | 日韩快播电影网 | 男女网站免费观看 | 亚洲欧美视频一区二区 | 成人免费大片黄在线播放 | 日韩在线免费视频 | 97超碰免费| 国产探花在线精品一区二区 | 综合中文字幕 | 亚洲日日夜夜 | jlzzjlzz国产精品久久 | 在线观看精品 | 成人免费淫片aa视频免费 |