前往中心的旅程...码头工人形象。 或者如何从没有Docker的注册表中下载图像

新年前3天,我们有一项任务是要通过管理器的闪存驱动器将我们的软件转让给客户。 该软件是一个微服务平台,具有数十个具有许多设置的docker映像和一公里长的舵图。 我们所拥有的:


  • 莫斯科经理(我不在那儿)
  • 窗户
  • 没有直接的互动(如果有的话,那真的没有帮助)
  • 码头工人

PFF,我想! 我将使用Golang,编写程序,并为Windows编译。
……五个小时后,我意识到自己结论的仓促。 在那一刻,尼尔森的笑声第一次被召回。 哈哈 我花了很多时间研究这个问题,这困扰着我。


我发现的大多数示例都需要dockerd。 经过一个小时的搜寻后,找到了两个不使用dockerd的脚本, 一个两个 。 第一个选项帮助我理解了获取所有图像层和配置文件的过程,但是无法在Windows上使用它。 第二个选项表明,不仅仅是各种哈希在屏幕上闪烁,尤其是此FIXME 。 当然,可以停在那儿,它起作用了! 转移去并不难。 但是,如何验证经理的图片与我们注册表中的格式相同? 但是没办法! 因此,我只是将图像上传到共享存储中,使用docker save命令下载,并共享了链接。 并对此冷静下来。


在假期的第四天,他们非常厌倦,正确地下载并组装docker映像的想法再次取代了我,我投入了两个小时的Moby代码。


我这次有什么:


  • 了解如何获取所有图层

我手拿Python并根据此脚本决定修复它。 第二天,我决定从头开始编写脚本。 回忆起我的oauth授权技术,我只是从那里复制了部分代码,以及我在编辑脚本时已经做出的开发。 授权和下载这些数据都没有问题,但是出现了问题:


  • 执行docker pull命令时显示的哈希是什么?
  • 用于在映像压缩文件中命名目录的哈希是什么?
  • 如何构建tar存档,以使校验和与原始图像匹配?

为了研究,我选择了Ubuntu图片:18.04
通过docker docker save sha256sum映像-257cab9137419a53359d0ed76f680fe926ed3645238357bdcdb84070a8f26cd0。


 > docker pull ubuntu:18.04 18.04: Pulling from library/ubuntu 2746a4a261c9: Downloading [==============> ] 6.909MB/26.69MB 4c1d20cdee96: Download complete 0d3160e1d0de: Download complete c8e37668deea: Download complete Digest: sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4 Status: Downloaded newer image for ubuntu:18.04 docker.io/library/ubuntu:18.04 

图片压缩档内容


 tar tvf ubuntu.tar drwxr-xr-x 0 root root 0 Dec 19 11:21 07adecfcb06a1142a69c3e769cb38f2d4ef9d772726ce1e65bc6dbd4448cccc9/ -rw-r--r-- 0 root root 3 Dec 19 11:21 07adecfcb06a1142a69c3e769cb38f2d4ef9d772726ce1e65bc6dbd4448cccc9/VERSION -rw-r--r-- 0 root root 477 Dec 19 11:21 07adecfcb06a1142a69c3e769cb38f2d4ef9d772726ce1e65bc6dbd4448cccc9/json -rw-r--r-- 0 root root 991232 Dec 19 11:21 07adecfcb06a1142a69c3e769cb38f2d4ef9d772726ce1e65bc6dbd4448cccc9/layer.tar drwxr-xr-x 0 root root 0 Dec 19 11:21 3e1d90747aa9d2a7ec6e9693bdd490dff8528b9aec4a2fac2300824e4ba3a60e/ -rw-r--r-- 0 root root 3 Dec 19 11:21 3e1d90747aa9d2a7ec6e9693bdd490dff8528b9aec4a2fac2300824e4ba3a60e/VERSION -rw-r--r-- 0 root root 477 Dec 19 11:21 3e1d90747aa9d2a7ec6e9693bdd490dff8528b9aec4a2fac2300824e4ba3a60e/json -rw-r--r-- 0 root root 15872 Dec 19 11:21 3e1d90747aa9d2a7ec6e9693bdd490dff8528b9aec4a2fac2300824e4ba3a60e/layer.tar -rw-r--r-- 0 root root 3411 Dec 19 11:21 549b9b86cb8d75a2b668c21c50ee092716d070f129fd1493f95ab7e43767eab8.json drwxr-xr-x 0 root root 0 Dec 19 11:21 b0474230e27ddbba2f46397aac85d4d2fd748064ed9c0ff1e57fec4f063fcf6b/ -rw-r--r-- 0 root root 3 Dec 19 11:21 b0474230e27ddbba2f46397aac85d4d2fd748064ed9c0ff1e57fec4f063fcf6b/VERSION -rw-r--r-- 0 root root 1264 Dec 19 11:21 b0474230e27ddbba2f46397aac85d4d2fd748064ed9c0ff1e57fec4f063fcf6b/json -rw-r--r-- 0 root root 3072 Dec 19 11:21 b0474230e27ddbba2f46397aac85d4d2fd748064ed9c0ff1e57fec4f063fcf6b/layer.tar drwxr-xr-x 0 root root 0 Dec 19 11:21 c8ba25f7db9f70220ac92449b238a0697f9eb580ef4f905225a333fc0a5e8719/ -rw-r--r-- 0 root root 3 Dec 19 11:21 c8ba25f7db9f70220ac92449b238a0697f9eb580ef4f905225a333fc0a5e8719/VERSION -rw-r--r-- 0 root root 401 Dec 19 11:21 c8ba25f7db9f70220ac92449b238a0697f9eb580ef4f905225a333fc0a5e8719/json -rw-r--r-- 0 root root 65571328 Dec 19 11:21 c8ba25f7db9f70220ac92449b238a0697f9eb580ef4f905225a333fc0a5e8719/layer.tar -rw-r--r-- 0 root root 432 Jan 1 1970 manifest.json -rw-r--r-- 0 root root 88 Jan 1 1970 repositories 

图像配置


 { ... "rootfs": { "type": "layers", "diff_ids": [ "sha256:2dc9f76fb25b31e0ae9d36adce713364c682ba0d2fa70756486e5cedfaf40012", "sha256:9f3bfcc4a1a8a676da07287a1aa6f2dcc8e869ea6f054c337593481a5bb1345e", "sha256:27dd43ea46a831c39d224e7426794145fba953cd7309feccf4d5ea628072f6a2", "sha256:918efb8f161b4cbfa560e00e8e0efb737d7a8b00bf91bb77976257cd0014b765" ] } ... } 

第一个问题已经足够迅速地解决了,文档https://github.com/opencontainers/image-spec/blob/master/config.md有所帮助。 运行docker pull命令时出现的哈希是从image-config清单的diff_ids列表计算出的chainID,其中第一个chainID始终是diff_ids列表中的第一个,而随后的是来自字符串的散列总和(chain_id[i-1] + " " + diff_id[i]) 。 构建chainID chainID的代码:


 def chain_ids(ids: list) -> list: chain = list() chain.append(ids[0]) if len(ids) < 2: return ids nxt = list() nxt.append("sha256:" + hashlib.sha256(f'{ids[0]} {ids[1]}'.encode()).hexdigest()) nxt.extend(ids[2:]) chain.extend(chain_ids(nxt)) return chain 

必须添加带有算法名称的前缀(在本例中为“ sha256:”),并且包含在opencontainers标准的要求中,即 该字符串应采用“ algorithm:hash”的形式。


他花了两个晚上讨论有关命名目录的问题。 在足够长的时间内,我查看了docker-daemon的源代码,哦! 奇迹! 我设法在这里这里找到生成代码。 要生成目录名称,您需要根据json层配置计算哈希值。 Docker有多个配置版本,在Docker引擎1.9之前,使用版本v1配置。 快说不做! 再一次,尼尔森的身影出现了。 经过短暂的调试后,我意识到问题隐藏在json的生成中。 在Python中,字典中的数据顺序可能与此字典中生成的json中的数据顺序不同。 json中数据的顺序将有所不同,因此其哈希也将有所不同。 我必须去OrderedDict,在其中预注册所需的数据顺序。 这将代码大小增加了一半半。


似乎一切都已纠正,我运行了脚本,然后……在内部的某个深处,臭名昭著的HA-HA弹出了! 最后一个哈希不匹配。 我再次研究代码,然后看到 ,这是另一个v1-config,其中包含有关映像的所有信息,可以使用docker inspect 。 专门为其添加另一个OrderDict,补充代码并...哈哈!
早上已经五点了,我的头真的没想到,所以睡觉后我回到查看代码。 反复查看生成代码时碰到了一条线 。 看到那件事我真高兴。 在我见到她之前,曾有过用 黑杰克和 数据记录。 我打开调试,执行docker save ,...这一点都不荒谬,在macOS的docker-desktop中,行长限制为947个字符,并且生成的配置中断为\" 。在Linux中执行所有这些步骤之后,我设法获得了配置第一个版本的层,在此基础上我编写了代码,并设法获得了所需的最后一层的哈希值。所有文件的哈希值都相同,目录的命名方式与原始图像相同。是时候收集tar存档了……哈哈!


文件大小不匹配,请阅读https://github.com/opencontainers/image-spec/blob/master/layer.mdtar存档格式。 默认值为 10240字节,而我收集的归档文件的大小为9216字节。 起初,我认为有必要将块大小减小到1024个字节,结果证明这是不正确的,因此512字节的块大小等于档案的大小。


 tarfile.RECORDSIZE = 512 

新创建的归档文件的第一行包含根文件夹“ /”。 该选项不合适,因此,我通过扫描文件夹的内容来补充代码,并将其单独排序后添加到存档中。


最终,我们设法实现了相同的文件大小和统一的目录外观,但这还不是全部。 存档中的文件和目录(manifest.json和资源库除外)必须具有st_atime属性,st_mtime等于st_ctime。 对于manifest.json和respoitories文件,st_atime,st_mtime和st_ctime属性的日期必须为1970-01-01 00:00时代的开始。 必须分别根据时区设置所有日期。 由于我在Mac OS中完成了所有工作,因此我注意到了一个区别。 将映像保存到Linux时,归档文件中的文件列表如下所示:


 tar tvf ubuntu.tar drwxr-xr-x 0/0 0 Dec 19 11:21 07adecfcb06a1142a69c3e769cb38f2d4ef9d772726ce1e65bc6dbd4448cccc9/ -rw-r--r-- 0/0 3 Dec 19 11:21 07adecfcb06a1142a69c3e769cb38f2d4ef9d772726ce1e65bc6dbd4448cccc9/VERSION -rw-r--r-- 0/0 477 Dec 19 11:21 07adecfcb06a1142a69c3e769cb38f2d4ef9d772726ce1e65bc6dbd4448cccc9/json -rw-r--r-- 0/0 991232 Dec 19 11:21 07adecfcb06a1142a69c3e769cb38f2d4ef9d772726ce1e65bc6dbd4448cccc9/layer.tar drwxr-xr-x 0/0 0 Dec 19 11:21 3e1d90747aa9d2a7ec6e9693bdd490dff8528b9aec4a2fac2300824e4ba3a60e/ -rw-r--r-- 0/0 3 Dec 19 11:21 3e1d90747aa9d2a7ec6e9693bdd490dff8528b9aec4a2fac2300824e4ba3a60e/VERSION -rw-r--r-- 0/0 477 Dec 19 11:21 3e1d90747aa9d2a7ec6e9693bdd490dff8528b9aec4a2fac2300824e4ba3a60e/json -rw-r--r-- 0/0 15872 Dec 19 11:21 3e1d90747aa9d2a7ec6e9693bdd490dff8528b9aec4a2fac2300824e4ba3a60e/layer.tar -rw-r--r-- 0/0 3411 Dec 19 11:21 549b9b86cb8d75a2b668c21c50ee092716d070f129fd1493f95ab7e43767eab8.json drwxr-xr-x 0/0 0 Dec 19 11:21 b0474230e27ddbba2f46397aac85d4d2fd748064ed9c0ff1e57fec4f063fcf6b/ -rw-r--r-- 0/0 3 Dec 19 11:21 b0474230e27ddbba2f46397aac85d4d2fd748064ed9c0ff1e57fec4f063fcf6b/VERSION -rw-r--r-- 0/0 1264 Dec 19 11:21 b0474230e27ddbba2f46397aac85d4d2fd748064ed9c0ff1e57fec4f063fcf6b/json -rw-r--r-- 0/0 3072 Dec 19 11:21 b0474230e27ddbba2f46397aac85d4d2fd748064ed9c0ff1e57fec4f063fcf6b/layer.tar drwxr-xr-x 0/0 0 Dec 19 11:21 c8ba25f7db9f70220ac92449b238a0697f9eb580ef4f905225a333fc0a5e8719/ -rw-r--r-- 0/0 3 Dec 19 11:21 c8ba25f7db9f70220ac92449b238a0697f9eb580ef4f905225a333fc0a5e8719/VERSION -rw-r--r-- 0/0 401 Dec 19 11:21 c8ba25f7db9f70220ac92449b238a0697f9eb580ef4f905225a333fc0a5e8719/json -rw-r--r-- 0/0 65571328 Dec 19 11:21 c8ba25f7db9f70220ac92449b238a0697f9eb580ef4f905225a333fc0a5e8719/layer.tar -rw-r--r-- 0/0 432 Jan 1 1970 manifest.json -rw-r--r-- 0/0 88 Jan 1 1970 repositories 

与本文开头的列表不同,在Linux上,归档文件使用仅数字标志保存。 tarinfo对象中有两个变量负责此操作,即tarinfo.uname和tarinfo.gname。 Mac OS的第二个问题是缺少根组,它是使用同一tarinfo对象中的tarinfo.gid变量修复的。 好吧,一切似乎都在建立档案...



对于所有文件,散列收敛,目录和文件的名称相同,属性st_atime,st_mtime和st_ctime与原始属性收敛,文件的权限完全相同。 在十六进制编辑器中打开这两个档案库有一点区别:



上方的窗口是原始窗口,下方的是我收集的档案。


了解tar格式。 目录名称后是文件的权限值(橙色矩形)。 不同之处在于,有关要添加到存档中的对象的信息不会添加到目录权限中。 权限中指示的值40755表示这是一个目录,具有755的权限,而100644是一个文件,具有644的权限。红色矩形是魔术字符串,由tarfile代码魔术字符串ustar\000判断,它仅用于PAX和USTAR格式。 PAX格式根本不适合,它使用一种特殊的标头。 蓝色矩形是校验和,并且由于使用了不同的格式来写入文件和魔术头,因此它有所不同。


我将存档格式切换为USTAR,但尚不清楚如何记录文件权限。 我到处都在发生着魔术,我从未使用过八进制系统,而且我也不明白为什么这里需要使用&符(也许评论中的人会分享知识)。 我必须添加一些打印件,以查看哪些数据随参数到达,以及哪些数据用于形成存档块。 从数据块的第二个位置获取一个整数,即16877,并将其引导至八进制微积分,结果发现该值为0o40755 ,这0o40755我所需要的。 只是覆盖了get_info和_create_header函数,从中删除了& 0o7777 (我& 0o7777了),我设法收集了一个与sha256哈希值与原始哈希值一致的tar存档。


PS在撰写文章时,ubuntu的映像已更新:hub.docker.com上的18.04。 所以我不得不在Digest上下载图像。 由于写入了Digest而不是标签,因此哈希值不再与原始值匹配,否则它们是相同的图像。


对我来说,第二个发现是使用docker save使用丢失的标签保存映像时,归档文件中没有存储库文件。


完整的工作代码在这里: https : //github.com/myback/docker_pull


纳尔逊·曼兹(Nelson Manz)的形象以及他的笑容“ HA-HA”是FOX的财产:)

Source: https://habr.com/ru/post/zh-CN484374/


All Articles