Docker registry倉庫歷史映象批量清理
前言
Docker registry在以jenkins CI/CD流水線自動打包並push映象的方式運行了一段時間之後,堆積的歷史映象數量極多,磁碟空間告急,為此,有必要定期做映象的清理,並釋放映象佔用的儲存空間
清除原理
Docker儲存使用的aufs檔案系統分層儲存結構,將容器檔案以讀寫分層的形式儲存在宿主機中,在宿主機存放的分層資料路徑為:/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的配置檔案中開啟允許刪除功能: /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.youkeshu.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
檢視磁碟,可以發現磁碟容量已經空閒出許多了,映象清理及儲存空間釋放完成!