从python脚本到skopeo 容器搬运篇(2)

  1. 1. 原有方案
    1. 1.1. 问题
  2. 2. 使用私有镜像仓库来进行搬运
    1. 2.1. 问题
  3. 3. 最终方案(?)
    1. 3.1. 搭建私有的harbor仓库
    2. 3.2. 在本地构建镜像后推送到私有harbor
    3. 3.3. 使用skopeo拉取镜像
    4. 3.4. 转换拉取的镜像文件
    5. 3.5. 拉取镜像测试

在生产中有时候会遇上一个比较恶心的问题,因为经常性需要去各个甲方部署,公司的平台基于docker-compose部署,另外还有大大小小各种乱七八糟几百个镜像的业务依赖,每部署一次都需要重新挑出所需的镜像对其进行导出,再将其部署。

原有方案

将docker镜像通过docker save 的方式导出为tar包,然后再到需要部署的机器上导入:

1
2
#导出操作
docker images|grep -v "REPOSITORY"|awk '{print "docker save -o "NR".tar " $1":"$2}' |awk '{system($0)}'
1
2
# 导入操作
ls *.tar|awk '{print $NF}'|sed -r 's#(.*)#sudo docker load -i \1#' |bash

问题

这个方案存在两个问题,一来是docker容器通过docker save 导出后会占用大量的磁盘空间,而且费时费力,另外需要在某台机器先部署好镜像才能进行导出,

为了提高效率,拟采用新的方式进行镜像的搬运。

使用私有镜像仓库来进行搬运

docker 官方镜像中存在docker registry的镜像,该镜像可比较方便的进行导入导出,并且镜像分层,可以较好的解决磁盘占用和导入时间长的问题。这个方式类似上一章中提到的harbor做镜像存储操作,

1
2
3
4
5
6
# 运行容器
docker pull registry
docker run -d -p 80:5000 -v /root/registry:/var/lib/registry registry
#推送镜像
docker images|grep -v "REPOSITORY"|awk '{print "docker tag " $1":"$2 "localhost:5000/" $1":"$2}' |awk '{system($0)}'
docker images|grep "localhost"|awk '{print "docker push " $1":"$2}' |awk '{system($0)}'

此时registry文件夹即保存的镜像,

部署:

1
2
3
# 导入registry镜像
docker load -i registry.tar
docker run -d -p 5000:5000 -v /root/registry:/var/lib/registry registry

通过python脚本简单下载:

1
2
3
4
5
6
7
8
import requests
import json
import os
res = requests.get("http://127.0.0.1:5000/v2/_catalog")
imagelist = res.json()["repositories"]
for i in imagelist:
os.system(f"docker pull localhost:5000/{i}")
os.system(f"docker tag localhost:5000/{i} {i}")

问题

这个方案虽然解决磁盘占用问题,但仍需要通过本地的docker 导出镜像。同样会带来大量的镜像。

最终方案(?)

最近听说了skopeo之后发现它可以比较方便的解决这个问题,但执行起来还是有一些阻碍:

  • 各种依赖镜像原本都是使用Dockerfile进行构建的,此时使用skopeo去获取镜像并不容易
  • skopeo保存的镜像仍然存在分层重复的问题,重复占用磁盘的问题仍旧存在。

解决问题:

搭建私有的harbor仓库

不多做赘述,可以参考此处

在本地构建镜像后推送到私有harbor

先构建全部的可能用到的镜像,构建过程略,在harbor建立一个image仓库,然后推送:

1
2
docker images|grep -v "REPOSITORY"|awk '{print "docker tag " $1":"$2 "harbor.local/images/"  $1":"$2}' |awk '{system($0)}'
docker images|grep "harbor.local"|awk '{print "docker push " $1":"$2}' |awk '{system($0)}'

使用skopeo拉取镜像

skopeo镜像拉取方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
skopeo  sync --src docker harbor.local/image/nginx:latest  --dest  dir image --insecure-policy --tls-verify=false
WARN[0000] '--tls-verify' is deprecated, please set this on the specific subcommand
INFO[0000] Tag presence check imagename="harbor.local/image/nginx:latest" tagged=true
INFO[0000] Copying image ref 1/1 from="docker://harbor.local/image/nginx:latest" to="dir:image/nginx:latest"
Getting image source signatures
Copying blob 7dbe7f8c80d3 done
Copying blob 89a5e550c323 done
Copying blob 2ef7f86534fd done
Copying blob b2109f84b5d3 done
Copying blob f88e263879e1 done
Copying blob e445148a2a87 done
Copying config 4f380adfc1 done
Writing manifest to image destination
Storing signatures
INFO[0006] Synced 1 images from 1 sources

通过脚本获取需要的镜像列表,然后对其分别拉取到image目录。

此时image的目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
tree images/
images/
└── nginx:latest
├── 2ef7f86534fdfcd45609e1f26fae69dff48828665da24023b86ce80a4efdea06
├── 4f380adfc10f4cd34f775ae57a17d2835385efd5251d6dfe0f246b0018fb0399
├── 7dbe7f8c80d3a2e99a0d1e07e4314dc18fecaf591563e596c1080698173d3724
├── 89a5e550c32388274bd4ced3c636fa8a3f21b1fd01e0f3b91bad4769ead317c6
├── b2109f84b5d3876f7fe664c298f91e4de086ce6d892cf6b885820e168fe6d10b
├── e445148a2a87d1e55db8d9e3f0f554481cb2ff8bf70383ad3f34d5379c5ad984
├── f88e263879e1903b1cdf9fe53ace2241fc3995aaa77b6524a1dcf3f9447bfd06
├── manifest.json
└── version

转换拉取的镜像文件

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
import os
import json
import hashlib
# 略去获取镜像列表过程
# imagelist为镜像列表
os.mkdir("registry")
os.makedirs(f"registry/docker/registry/v2/blobs/sha256")
# 创建导出文件夹
imagelist=["nginx:latest"]

for i in imagelist:
os.system(f"skopeo sync --src docker harbor.local/test/{i} --dest dir image --insecure-policy --tls-verify=false")
imagename=i.split(':')[0]
imagetag=i.split(':')[1]
os.makedirs(f"registry/docker/registry/v2/repositories/{imagename}")
with open(f"image/{i}/manifest.json") as f:
manifestdata = f.read()

# 创建对应镜像数据文件夹
with open(f"image/{i}/manifest.json",'r',encoding="utf-8") as f:
man_sha=f.read()
data = json.loads(man_sha)
m = hashlib.sha256()
m.update(bytes(man_sha,encoding='utf-8'))
manifest_sha = m.hexdigest()
os.makedirs(f"registry/docker/registry/v2/repositories/{imagename}/_manifests/revisions/sha256/{manifest_sha}")
with open(f"registry/docker/registry/v2/repositories/{imagename}/_manifests/revisions/sha256/{manifest_sha}/link","w") as f:
f.write(f"sha256:{manifest_sha}")
#构建镜像tag的link文件
os.makedirs(f"registry/docker/registry/v2/repositories/{imagename}/_manifests/tags/{imagetag}/index/sha256/{manifest_sha}")
with open(f"registry/docker/registry/v2/repositories/{imagename}/_manifests/tags/{imagetag}/index/sha256/{manifest_sha}/link","w") as f:
f.write(f"sha256:{manifest_sha}")
os.makedirs(f"registry/docker/registry/v2/repositories/{imagename}/_manifests/tags/{imagetag}/current")
with open(f"registry/docker/registry/v2/repositories/{imagename}/_manifests/tags/{imagetag}/current/link","w") as f:
f.write(f"sha256:{manifest_sha}")
#构建blobs目录
sim_man_sha = manifest_sha[:2]
os.makedirs(f"registry/docker/registry/v2/blobs/sha256/{sim_man_sha}/{manifest_sha}")
with open(f"registry/docker/registry/v2/blobs/sha256/{sim_man_sha}/{manifest_sha}/data","w") as f:
f.write(man_sha)
man_digest=data['config']["digest"].split(":")[1]
os.makedirs(f"registry/docker/registry/v2/blobs/sha256/{man_digest[:2]}/{man_digest}")
os.system(f"ln -f image/{i}/{man_digest} registry/docker/registry/v2/blobs/sha256/{man_digest[:2]}/{man_digest}/data")
os.makedirs(f"registry/docker/registry/v2/repositories/{imagename}/_layers/sha256/{man_digest}")
with open(f"registry/docker/registry/v2/repositories/{imagename}/_layers/sha256/{man_digest}/link","w") as f:
f.write(f"sha256:{man_digest}")
for layer in data['layers']:
print(layer)
# 写入layer的link文件
layerhash = layer["digest"].split(":")[2]
layerpath = f"registry/docker/registry/v2/repositories/{imagename}/_layers/sha256/{layerhash}"
sim_layerhash = layerhash[:2]
os.makedirs(layerpath)
with open(f"{layerpath}/link",'w',encoding="utf-8") as f:
f.write(layer["digest"])
os.makedirs(f"registry/docker/registry/v2/blobs/sha256/{sim_layerhash}/{layerhash}")
os.system(f"ln -f image/{i}/{layerhash} registry/docker/registry/v2/blobs/sha256/{sim_layerhash}/{layerhash}/data")

导出后文件夹格式:

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
registry/
└── docker
└── registry
└── v2
├── blobs
│   └── sha256
│   ├── 2e
│   │   └── 2ef7f86534fdfcd45609e1f26fae69dff48828665da24023b86ce80a4efdea06
│   │   └── data
│   ├── 4f
│   │   └── 4f380adfc10f4cd34f775ae57a17d2835385efd5251d6dfe0f246b0018fb0399
│   │   └── data
│   ├── 7d
│   │   └── 7dbe7f8c80d3a2e99a0d1e07e4314dc18fecaf591563e596c1080698173d3724
│   │   └── data
│   ├── 89
│   │   └── 89a5e550c32388274bd4ced3c636fa8a3f21b1fd01e0f3b91bad4769ead317c6
│   │   └── data
│   ├── b2
│   │   └── b2109f84b5d3876f7fe664c298f91e4de086ce6d892cf6b885820e168fe6d10b
│   │   └── data
│   ├── d1
│   │   └── d1ad002da32d49eed600f7fcff543396a57a7e2c913063ac9bb68ca78219bfd1
│   │   └── data
│   ├── e4
│   │   └── e445148a2a87d1e55db8d9e3f0f554481cb2ff8bf70383ad3f34d5379c5ad984
│   │   └── data
│   └── f8
│   └── f88e263879e1903b1cdf9fe53ace2241fc3995aaa77b6524a1dcf3f9447bfd06
│   └── data
└── repositories
└── nginx
├── _layers
│   └── sha256
│   ├── 2ef7f86534fdfcd45609e1f26fae69dff48828665da24023b86ce80a4efdea06
│   │   └── link
│   ├── 4f380adfc10f4cd34f775ae57a17d2835385efd5251d6dfe0f246b0018fb0399
│   │   └── link
│   ├── 7dbe7f8c80d3a2e99a0d1e07e4314dc18fecaf591563e596c1080698173d3724
│   │   └── link
│   ├── 89a5e550c32388274bd4ced3c636fa8a3f21b1fd01e0f3b91bad4769ead317c6
│   │   └── link
│   ├── b2109f84b5d3876f7fe664c298f91e4de086ce6d892cf6b885820e168fe6d10b
│   │   └── link
│   ├── e445148a2a87d1e55db8d9e3f0f554481cb2ff8bf70383ad3f34d5379c5ad984
│   │   └── link
│   └── f88e263879e1903b1cdf9fe53ace2241fc3995aaa77b6524a1dcf3f9447bfd06
│   └── link
└── _manifests
├── revisions
│   └── sha256
│   └── d1ad002da32d49eed600f7fcff543396a57a7e2c913063ac9bb68ca78219bfd1
│   └── link
└── tags
└── latest
├── current
│   └── link
└── index
└── sha256
└── d1ad002da32d49eed600f7fcff543396a57a7e2c913063ac9bb68ca78219bfd1
└── link

42 directories, 18 files

拉取镜像测试

1
2
3
4
5
6
7
8
9
10
11
12
docker pull localhost:5000/nginx
Using default tag: latest
latest: Pulling from nginx
7dbe7f8c80d3: Pull complete
89a5e550c323: Pull complete
2ef7f86534fd: Pull complete
b2109f84b5d3: Pull complete
f88e263879e1: Pull complete
e445148a2a87: Pull complete
Digest: sha256:d1ad002da32d49eed600f7fcff543396a57a7e2c913063ac9bb68ca78219bfd1
Status: Downloaded newer image for localhost:5000/nginx:latest
localhost:5000/nginx:latest