一 说明
Docker的镜像仓库有多种选择,即可以使用一些互联网上的镜像仓库,也可以使用自己搭建的镜像仓库。
可以自行搭建的私有镜像仓库有:
名称 | 语言 | 特点 |
---|---|---|
Docker Registry | Go | Docker官方的镜像仓库,简单易用,机器资源消耗低,但是没管理界面 |
Harbor | Go | VMware出品,功能强大,支持权限管理、镜像复制等,但是对机器配置要求较高,所需资源较多,一般要求至少2核8G配置 |
Nexus Repository | Java | Sonatype出品,功能强大,支持多种包类型管理 |
GitLab Container Registry | Ruby/Go | GitLab自带的镜像仓库,与GitLab平台深度集成 |
此文章仅讨论Docker Registry镜像仓库,且主要是如何清理仓库的tag标签。
为何要清理tag标签?
因为自行搭建的私有仓库,空间一般都是有限的,而随着tag越来越多,占用的空间也随之越来越大,这会导致磁盘不足等情况,所以正常情况下,tag的数量保留最新的10个左右(视具体情况而定)就可以了,而Docker Registry特别简单,所以并没有Harbor等仓库自带的自动清理tag的功能,则需要通过脚本自动定时清理。
二 Docker方式安装Docker Registry镜像仓库
- 镜像版本:registry:2.7.1
- 部署位置:/opt/registry
- 数据目录:/opt/registry/data
- 所属用户:root
- 安装脚本位置:/opt/registry/start.sh
脚本 /opt/registry/start.sh
内容如下:
1 | docker run -itd \ |
通过脚本启动之后,可以通过浏览器查看已有镜像仓库(首次访问仓库应该是空数组):http://[IP]:5000/v2/_catalog
三 脚本说明
脚本目录结构如下:1
2
3
4
5.
├── Dockerfile
├── docker-build.sh
├── main.py
└── requirements.txt
Python脚本main.py
的内容:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70import requests
import operator
import re
import os
# 仓库地址
registry_url = os.getenv('REGISTRY_URL', 'http://192.168.64.2:5000')
remain_num_str = os.getenv('REMAIN_TAG_NUM', '5')
# 保留的tag数量
remain_num = int(remain_num_str)
# 匹配仓库的正则,默认所有
regex_pattern = os.getenv('REPO_REGEX_PATTERN', '.*')
# 获取所有的仓库名
def get_repositories():
url = f'{registry_url}/v2/_catalog'
response = requests.get(url)
repositories = response.json().get('repositories')
return repositories
# 获取指定仓库的所有 tag
def get_tags(repository):
url = f'{registry_url}/v2/{repository}/tags/list'
response = requests.get(url)
tags = response.json().get('tags')
return tags
# 获取指定 tag 的 Docker-Content-Digest
def get_digest(repository, tag):
url = f'{registry_url}/v2/{repository}/manifests/{tag}'
headers = {'Accept': 'application/vnd.docker.distribution.manifest.v2+json'}
response = requests.get(url, headers=headers)
digest = response.headers.get('Docker-Content-Digest')
return digest
# 删除指定仓库的指定 tag
def delete_tag(repository, tag):
digest = get_digest(repository, tag)
url = f'{registry_url}/v2/{repository}/manifests/{digest}'
response = requests.delete(url)
return response.status_code == 202
# 对 tag 进行排序
def sort_tags(tags):
sorted_tags = sorted(tags, key=lambda x: tuple(map(int_or_str, re.split('[.-]', x))))
return sorted_tags[::-1]
# 将字符串转化为数字或字符串
def int_or_str(s):
try:
return int(s)
except ValueError:
return s
# 获取所有需要处理的仓库名
repositories = [r for r in get_repositories() if re.match(regex_pattern, r)]
# 遍历所有需要处理的仓库
for repository in repositories:
print(f'Process repository {repository}')
# 获取所有 tag 并排序
tags = get_tags(repository)
sorted_tags = sort_tags(tags)
# 仅保留最新的两个 tag
for tag in sorted_tags[remain_num:]:
# 删除不需要保留的 tag
if delete_tag(repository, tag):
print(f'Deleted tag {tag} from repository {repository}')
else:
print(f'Failed to delete tag {tag} from repository {repository}')
其中使用了三个环境变量如下:
- REGISTRY_URL: 镜像仓库地址
- REMAIN_TAG_NUM:保留的tag数量
- REPO_REGEX_PATTERN:匹配仓库的正则
脚本中Docker registry的api的相关接口和响应如下(看着接口与响应数据更容易理解以上的脚本代码):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28获取所有仓库的get请求示例:192.168.64.2:5000/v2/_catalog
响应:
{
"repositories": [
"server/mafgwo-server",
"web/mafgwo-web"
]
}
获取某个仓库所有tag的get请求示例:192.168.64.2:5000/v2/server/mafgwo-server/tags/list
响应:
{
"name": "server/mafgwo-server",
"tags": [
"1.0.988-dev",
"1.0.1009-dev",
"1.0.1010-dev",
"1.0.1011-dev",
"1.0.1012-dev"
]
}
获取 某个tag的 Docker-Content-Digest 响应头
get请求 http://http://192.168.64.2:5000/v2/server/mafgwo-server/manifests/1.0.1009-dev 的Docker-Content-Digest响应头
返回:Docker-Content-Digest: sha256:30f4cf8547b010438fe79006acab4db4dd7b90e5e3795909fa58e45cb7911d54
删除某个tag的DELETE请求 192.168.64.2:5000/v2/server/mafgwo-server/manifests/sha256:30f4cf8547b010438fe79006acab4db4dd7b90e5e3795909fa58e45cb7911d54
响应码202,没有body响应
Python依赖requirements.txt
的内容:1
requests
Dockerfile
的内容:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16FROM python:3.7.17-slim-buster
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
RUN apt-get update && apt-get -y install cron
RUN echo "* * * * * root /usr/local/bin/python /app/main.py >> /var/log/cron.log 2>&1" >> /etc/crontab
RUN chmod 0644 /etc/crontab
RUN touch /var/log/cron.log
COPY . .
CMD cron && tail -f /var/log/cron.log
构建Docker镜像的脚本命令 docker-build.sh
的内容:1
docker build -t tools/registry-clean:1.0 .
把以上文件都准备好,通过 bash docker-build.sh
就可以构建好Docker镜像。
四 脚本部署
直接通过一下Docker命令启动就等于部署成功了脚本1
2
3
4
5docker run -d \
-e REGISTRY_URL=http://192.168.90.221:5000 \
-e REMAIN_TAG_NUM=10 \
-e REPO_REGEX_PATTERN="^tools.*" \
--name registry-clean tools/registry-clean:1.0
启动之后可以通过以下命令查看脚本执行情况,脚本某人一分钟执行一次,所以执行以下查看日志的命令后等待一会能看到正常的日志输出说明成功了,如果想要看到删除的tag的日志请确保仓库中的tag数量超过了保留的数量。1
docker logs -f registry-clean