go-containerregistry push和docker push的layer_id不一致原因分析

本文探讨了使用go-containerregistry库push Docker镜像时与docker push产生的layer_id不一致的原因。分析发现,差异源于go-containerregistry在推送过程中对layer.tar进行了额外的压缩,导致计算的sha256值变化。通过调整代码,避免这一额外压缩,可以使layer_id与docker push保持一致。同时,文章还简要介绍了docker pull的原理,包括manifest和image config在其中的作用。

问题

使用go-containerregistry库 push image 时和 docker push image 时的layer hash不一致

### 本地docker push推送镜像 ###
# docker push 192.168.1.1:30500/alpine-java:1.0.0
The push refers to repository [192.168.1.1:30500/alpine-java]
372aafbe5d64: Preparing
5b7df235d876: Preparing
### 拉取go-containerregistry推送到registry的镜像 ###
# docker pull 192.168.1.1:30500/alpine-java:1.0.0
1.0.0: Pulling from alpine-java
4d23f0108a5c: Pull complete
20ec70f16685: Pull complete
Digest: sha256:ee64093d46b7c9f6e9676deca8b8e10ac935ab2970f1b25d24c06182872f265a
Status: Downloaded newer image for 192.168.1.1:30500/alpine-java:1.0.0

分析

  1. 两种方式push后,docker inspect 两个image后,发现内容是一致的,说明其实push的是同一个镜像
    在这里插入图片描述(左边是机器上docker inspect的结果,右边是本地docker inspect结果。rootfs:容器只读的文件系统)

  2. docker save -o两个image的tar包后,解压比对manifest.json文件
    在这里插入图片描述
    (左边是docker push manifest.json,右边是go-containerregistry push manifest.json)

  3. 通过分析go-containerregistry源码发现,推送的image layer_id实际上是对layer.tar做了“再压缩”后计算的sha256 commit到manifest.json导致sha256发生变化。从而导致与docker push的layer sha256不一致。
    在这里插入图片描述(go-containerregistry代码计算的layer_id,与本文docker pull的layer_id一致)

在生成sha256同样的位置,对本地原文件计算sha256。可以得到与在终端使用sha256sum命令计算相同的结果。如下:

file, _ := os.Open("~/xxxxxxx/layer.tar")
hasher := sha256.New()
io.Copy(hasher, file) // 将文件copy到hasher中

// -> 5b7df235d876e8cd4a2a329ae786db3fb152eff939f88379c49bcaaabbaf
fmt.Printf("%x\n",hasher.Sum(nil))
// 

分析原因:读取layer.tar文件时,gzip.ReadCloser()函数会在原先layer.tar的基础上做一次“再压缩”。压缩包内容与实际layer.tar不同,导致计算的sha256不同。在这里插入图片描述
修改方案:将gzip.ReadCloser(u) -> u之后,获取到的layer_id与docker push的layer_id保持一致。

原理剖析

那么layer涉及到更深层次有什么原理吗?

  1. 先了解下docker pull的原理
    在这里插入图片描述

  2. 主要需要了解两个配置文件:manifest(对应registry服务端的配置)和 image config(针对本地存储端的)。值得一提的是,这两个不同的配置文件中的layer_id其实是不同的。

​ 简单举例,查看manifest.json关于layer的配置:

......,
"layers": [
   {
    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
    "size": 2929796,
    "digest": "sha256:4d23f0108a5cb4d445395bea6210f5c729774ff4d0a2cd5a720b03ed612b71ee"
  },
  {
    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
    "size": 89480540,
    "digest": "sha256:20ec70f166851563b685a57685d55a0db6b9ecd92bfd19f6824a91d04d8bf892"
  }
]

​ 再来看看image config里面关于layer存的是什么:

......,
"RootFS": {
  "Type": "layers",
  "Layers": [
    "sha256:5b7df235d876e8cd4a2a329ae786db3fb152eff939f88379c49bcaaabbafbd9c",
    "sha256:372aafbe5d64e05dadee8a2e0ae22e4e7e963106208d58f3572c0724b6553c08"
  ]
},
......
  1. 为什么要拉取两个文件?
  • 当我们去registry上拉layer的时候,拉的格式是根据请求中的media type决定的,因为layer存在本地的时候未压缩的,或者是已解压过的。
  • 为了在网络上传输的更快,所有media type一般会指定压缩格式,如.tar。
  • 结合docker pull可以发现。当docker发现本地不存在某个layer的时候,就会通过manifest.json里面的digest + mediaType(如上述配置中的application/vnd.docker.image.rootfs.diff.tar.gzip)去 docker registry 获取 image config 中的 diff_ids,再在本地找这些layer,还是拿不到就会去拿tar包解压缩。
  1. 每个 diffid 对应一个 layer tar 包的 sha256,可以利用sha256sum对layer.tar包进行分析
    在这里插入图片描述
    可以发现最终解析出来的值就是rootfs下的layer_id
    在这里插入图片描述
    参考链接:
    https://zhuanlan.zhihu.com/p/95900321
    https://github.com/google/go-containerregistry
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值