结论
1.容器镜像版本的管理,生产环境大量使用tag是不合适的。
2.容器镜像版本管理使用digest管理,其准确性高于tag。
3.一个容器镜像的digest,在一个固定的环境中、固定的registry中,其sha256是恒定不变的。本质上,RepoDigest值并不是image的不变属性,它们是image存储方式/位置的属性(例如,取决于压缩实现的特定版本:podman还是docker的pull可能导致使用不同的RepoDigest值,而RepoDigest 显然取决于目标registry主机和存储库名称)。例如,docker load会直接消除镜像的RepoDigest。
4.如果想在registry中的副本之间保留完全相同的image digest,那么就不要使用docker/poman {push,pull,save,load}。在多个不同的镜像仓库之间,使用skopeo copy有助于保证容器镜像digest的一致性。
容器镜像拉取
容器镜像,从使用人员的角度,使用tag的方式拉取肯定是最方便和直观的。红帽官网的镜像,提供docker和podman的拉取方式,如:JWS3.1:
在网站上,我们可以看到所有镜像tag的列表:
但是,tag所对应的真正容器镜像并不是固定的。比如latest tag对应的镜像,就会随着时间的推移发生变化。
我们举个例子,使用tag为1.0的docker部署,先pull再run:
现在假设经过了任意时间,让我们再次运行它。
容器镜像run起来后,版本变为2?很显然,有人错误地标记了image。
现在,在部署相同的情况下,如果我们按image的digest值提取数据,则可以100%保证无论是今天还是几年后,都可以提取完全相同的image,而不管采用常规标记。SHA值实际上成为image的标签。
我们拉取最新的红帽JWS3.1镜像(不写tag默认就拉latest)先以podman方式拉取:
[root@repo ~]# podman pull registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift
Trying to pull registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift...Getting image source signatures
Copying blob f897b9608c98 done
Copying blob faa04077df6f done
Copying blob 99a353860a26 done
Copying blob c9ff3e9281bc done
Copying config 44cb270a74 done
Writing manifest to image destination
Storing signatures
44cb270a74c90da1fe27a69fbcdcbd06dd97cf383d45ef0860dff82bd67159b1
[root@repo ~]# podman images |grep -i webserver31-tomcat8-openshift
registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift latest 44cb270a74c9 5 weeks ago 579 MB
我们通过命令行确认digest:
[root@repo ~]# podman inspect registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift | jq '.[]["Digest"]'
"sha256:b22c3fea4374e776366d2363184a854b8ba47539df0d02b6907f1e7816d4587f"
我们再以docker的方式进行拉取:
[root@repo ~]# docker pull registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift
Using default tag: latest
Trying to pull repository registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift ...
latest: Pulling from registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift
c9ff3e9281bc: Pull complete
f897b9608c98: Pull complete
99a353860a26: Pull complete
faa04077df6f: Pull complete
Digest: sha256:a4c2a8a314c2d3ade2d7c2b5565bc463ce89eb86d646931a6a9a46b4cb4d6549
Status: Downloaded newer image for registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift:latest
查看digest:
[root@repo ~]# docker images --digests
REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift latest sha256:a4c2a8a314c2d3ade2d7c2b5565bc463ce89eb86d646931a6a9a46b4cb4d6549 44cb270a74c9 5 weeks ago 548 MB
结论:针对相同的镜像:registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift:latest:
通过podman拉取到本地,查看到的digest是:b22c3fea4374e776366d2363184a854b8ba47539df0d02b6907f1e7816d4587f
通过docker拉取到本地,查看到的digest是:a4c2a8a314c2d3ade2d7c2b5565bc463ce89eb86d646931a6a9a46b4cb4d6549
那么,为什么同一个镜像,用podman和docker拉取下来,digest的哈希数值不同呢?
RepoDigest值是否是恒定不变
RepoDigest值不是image的不变属性,它们是image存储方式/位置的属性(例如,取决于压缩实现的特定版本:podman还是docker的pull可能导致使用不同的RepoDigest值,而RepoDigest 显然取决于目标注册表主机和存储库名称)。在docker pull中,在拉取image以记录拉取操作的源时设置RepoDigest。
但是,如果想在registry中的副本之间保留完全相同的image,那么就不要使用docker/poman {push,pull,save,load},所有这些操作本质上都涉及某种格式转换/压缩或类似的操作。
我们举例说明,对docker进行进行docker save操作,再查看Digest,变成了e5279c49f93a212cbba3f6640430ab5be982f3afda8eacd45e9f9085f0eaa950
[root@repo ~]# docker save registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift > docker-export.tar
[root@repo ~]# skopeo inspect docker-archive:docker-export.tar | jq ".Digest"
"sha256:e5279c49f93a212cbba3f6640430ab5be982f3afda8eacd45e9f9085f0eaa950"
将系统中此前的镜像删除,
[root@repo /]# docker image rm registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshiftUntagged: registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift:latestUntagged: registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift@sha256:a4c2a8a314c2d3ade2d7c2b5565bc463ce89eb86d646931a6a9a46b4cb4d6549Deleted: sha256:44cb270a74c90da1fe27a69fbcdcbd06dd97cf383d45ef0860dff82bd67159b1Deleted: sha256:2f7286327cfe29abd7851d89d84caead89c2f5cd2983bdd144644e38660ea58dDeleted: sha256:bc028cb10e3d2902b70f62404b61651e987a359d0a924b0cba45eeade7e9c7efDeleted: sha256:940311032c0be435b65a79251b988c620f32a49e4a9a4415d53cde0ac45b3e9bDeleted: sha256:49577de6730168df9aef3494209d7618ebdbbc631977f4779fa3d4d079d91dd5
然后加载tar镜像后,再查看digest,是无法直接显示的:
[root@repo /]# docker load -i docker-export.tar49577de67301: Loading layer [==================================================>] 215.1 MB/215.1 MBd02565babdb9: Loading layer [==================================================>] 10.24 kB/10.24 kB59edfeaca1c1: Loading layer [==================================================>] 273.6 MB/273.6 MBcca5cf884bc1: Loading layer [==================================================>] 90.15 MB/90.15 MBLoaded image: registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift:latest[root@repo /]# docker images --digestsREPOSITORY TAG DIGEST IMAGE ID CREATED SIZEregistry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift latest <none> 44cb270a74c9 5 weeks ago 548 MB
原因是:docker load不涉及来自registry的pull,因此无法记录任何RepoDigests;最多只能有一个路径名。
如何避免RepoDigest值变化的问题
在上文中,我们通过podman pull下来了镜像,放到了本地(b22c3fea4374e776366d2363184a854b8ba47539df0d02b6907f1e7816d4587f)。接下来,我们推动到以podman方式运行的docker registry中。先打tag,打完tag后,我们查新tag的镜像,它的digest没有发生变化。
[root@repo webserver31-tomcat8-openshift]# podman inspect registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift | jq '.[]["Digest"]'
"sha256:b22c3fea4374e776366d2363184a854b8ba47539df0d02b6907f1e7816d4587f
[root@repo webserver31-tomcat8-openshift]# podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
53b6aa7b5023 docker.io/library/registry:2 /entrypoint.sh /e... 5 weeks ago Up 4 days ago 0.0.0.0:5000->5000/tcp mirror-registry
[root@repo webserver31-tomcat8-openshift]# podman tag registry.access.redhat.com/jboss-webserver-3/webserver31-tomcat8-openshift:latest repo.ocp4.example.com:5000/jboss-webserver-3/webserver31-tomcat8-openshift:latest
[root@repo webserver31-tomcat8-openshift]# podman inspect repo.ocp4.example.com:5000/jboss-webserver-3/webserver31-tomcat8-openshift:latest | jq '.[]["Digest"]'
"sha256:b22c3fea4374e776366d2363184a854b8ba47539df0d02b6907f1e7816d4587f"
[root@repo webserver31-tomcat8-openshift]# podman push repo.ocp4.example.com:5000/jboss-webserver-3/webserver31-tomcat8-openshift:latest
Getting image source signatures
Copying blob d02565babdb9 done
Copying blob 59edfeaca1c1 done
Copying blob 49577de67301 done
Copying blob cca5cf884bc1 done
Copying config 44cb270a74 done
Writing manifest to image destination
此时,我们将本地缓存的镜像删除,然后从podman运行的docker registry中拉取镜像,再次查看,镜像的digest发生了变化:
[root@repo webserver31-tomcat8-openshift]# podman pull repo.ocp4.example.com:5000/jboss-webserver-3/webserver31-tomcat8-openshift:latest
Trying to pull repo.ocp4.example.com:5000/jboss-webserver-3/webserver31-tomcat8-openshift:latest...Getting image source signatures
Copying blob a660e1760a3a skipped: already exists
Copying blob 246226c3ec30 skipped: already exists
Copying blob 01caf8c9797a skipped: already exists
Copying blob 5bd0fbc2f2ce skipped: already exists
Copying config 44cb270a74 done
Writing manifest to image destination
Storing signatures
44cb270a74c90da1fe27a69fbcdcbd06dd97cf383d45ef0860dff82bd67159b1
[root@repo webserver31-tomcat8-openshift]# podman inspect repo.ocp4.example.com:5000/jboss-webserver-3/webserver31-tomcat8-openshift:latest | jq '.[]["Digest"]'
"sha256:e5279c49f93a212cbba3f6640430ab5be982f3afda8eacd45e9f9085f0eaa950"
镜像的digest从从b22c3fea4374e776366d2363184a854b8ba47539df0d02b6907f1e7816d4587f变化成为了:e5279c49f93a212cbba3f6640430ab5be982f3afda8eacd45e9f9085f0eaa950。
也就是多说,podman tag没有使镜像的digest发生变化,但pull/push的操作会使digest发生变化。
要规避这种问题,在不同的镜像仓库之间同步镜像时,尽量使用skopeo copy。而不是docker/podman save load push pull。
例如,我们有两个ocp集群,第一个负责DEV/TEST/SIT/UAT,第二个负责生产。那么在pipeline中,就需要书写镜像拷贝的步骤,如下图所示: