结论

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的那些事儿_java

在网站上,我们可以看到所有镜像tag的列表:

关于容器镜像tag的那些事儿_java_02


但是,tag所对应的真正容器镜像并不是固定的。比如latest tag对应的镜像,就会随着时间的推移发生变化。


我们举个例子,使用tag为1.0的docker部署,先pull再run:

关于容器镜像tag的那些事儿_java_03

关于容器镜像tag的那些事儿_java_04

现在假设经过了任意时间,让我们再次运行它。

关于容器镜像tag的那些事儿_java_05

容器镜像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 psCONTAINER ID  IMAGE                         COMMAND               CREATED      STATUS         PORTS                   NAMES53b6aa7b5023  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:latestGetting image source signaturesCopying blob d02565babdb9 doneCopying blob 59edfeaca1c1 doneCopying blob 49577de67301 doneCopying blob cca5cf884bc1 doneCopying config 44cb270a74 doneWriting 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:latestTrying to pull repo.ocp4.example.com:5000/jboss-webserver-3/webserver31-tomcat8-openshift:latest...Getting image source signaturesCopying blob a660e1760a3a skipped: already existsCopying blob 246226c3ec30 skipped: already existsCopying blob 01caf8c9797a skipped: already existsCopying blob 5bd0fbc2f2ce skipped: already existsCopying config 44cb270a74 doneWriting manifest to image destinationStoring signatures44cb270a74c90da1fe27a69fbcdcbd06dd97cf383d45ef0860dff82bd67159b1
[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中,就需要书写镜像拷贝的步骤,如下图所示:


关于容器镜像tag的那些事儿_java_06