如何验证两个Docker映像的内容是否完全相同?

mlj*_*jrg 6 docker docker-image

我们如何确定两个Docker映像具有完全相同的文件系统结构,并且无论文件时间戳如何,相应文件的内容都相同?

我尝试了映像ID,但是在使用相同的Dockerfile和干净的本地存储库进行构建时,它们会有所不同。我通过构建一个图像,清理本地存储库,然后触摸其中一个文件以更改其修改日期,然后构建第二个图像以及它们的图像ID不匹配来进行此测试。我使用了Docker 17.06(我相信是最新版本)。

Buk*_*gey 5

如果要比较图像的内容,可以使用docker inspect <imageName>命令,然后查看RootFS部分

docker inspect redis

    "RootFS": {
        "Type": "layers",
        "Layers": [
            "sha256:eda7136a91b7b4ba57aee64509b42bda59e630afcb2b63482d1b3341bf6e2bbb",
            "sha256:c4c228cb4e20c84a0e268dda4ba36eea3c3b1e34c239126b6ee63de430720635",
            "sha256:e7ec07c2297f9507eeaccc02b0148dae0a3a473adec4ab8ec1cbaacde62928d9",
            "sha256:38e87cc81b6bed0c57f650d88ed8939aa71140b289a183ae158f1fa8e0de3ca8",
            "sha256:d0f537e75fa6bdad0df5f844c7854dc8f6631ff292eb53dc41e897bc453c3f11",
            "sha256:28caa9731d5da4265bad76fc67e6be12dfb2f5598c95a0c0d284a9a2443932bc"
        ]
    }
Run Code Online (Sandbox Code Playgroud)

如果所有图层都相同,则图像包含相同内容

  • 如果您触摸了文件,则会导致修改,从而导致不同的哈希@mljrg (4认同)
  • 仅将 RootFS 作为一个字符串获取: `docker inform --format='{{.RootFS}}' &lt;image&gt;` (4认同)

mlj*_*jrg 5

经过一些研究,我想出了一个根据我的测试快速而干净的解决方案。

整体解决方案是这样的:

  1. 通过以下方式为您的图像创建一个容器 docker create ...
  2. 通过以下方式将其整个文件系统导出到 tar 存档 docker export ...
  3. 将存档目录名称、符号链接名称、符号链接内容、文件名和文件内容通过管道传输到哈希函数(例如 MD5)
  4. 比较不同图像的哈希值以验证它们的内容是否相等

就是这样。

从技术上讲,这可以按如下方式完成:

1)创建文件md5docker,并赋予其执行权限,例如chmod +x md5docker

#!/bin/sh
dir=$(dirname "$0")
docker create $1 | { read cid; docker export $cid | $dir/tarcat | md5; docker rm $cid > /dev/null; }
Run Code Online (Sandbox Code Playgroud)

2)创建文件tarcat,并赋予其执行权限,例如chmod +x tarcat

#!/usr/bin/env python3
# coding=utf-8

if __name__ == '__main__':
    import sys
    import tarfile
    with tarfile.open(fileobj=sys.stdin.buffer, mode="r|*") as tar:
        for tarinfo in tar:
            if tarinfo.isfile():
                print(tarinfo.name, flush=True)
                with tar.extractfile(tarinfo) as file:
                    sys.stdout.buffer.write(file.read())
            elif tarinfo.isdir():
                print(tarinfo.name, flush=True)
            elif tarinfo.issym() or tarinfo.islnk():
                print(tarinfo.name, flush=True)
                print(tarinfo.linkname, flush=True)
            else:
                print("\33[0;31mIGNORING:\33[0m ", tarinfo.name, file=sys.stderr)
Run Code Online (Sandbox Code Playgroud)

3) 现在 invoke ./md5docker <image>,其中<image>是您的图像名称或 ID,以计算图像的整个文件系统的 MD5 哈希值。

要验证两个图像是否具有相同的内容,只需检查它们的哈希值是否与步骤 3 中计算的相同)。

请注意,此解决方案仅考虑内容目录结构、常规文件内容和符号链接(软和硬)。如果您需要更多,只需tarcat通过添加更多elif测试您希望包含的内容的子句来更改脚本(请参阅Python 的 tarfile,并查找TarInfo.isXXX()与所需内容相对应的方法)。

我在这个解决方案中看到的唯一限制是它对 Python 的依赖(我使用的是 Python3,但它应该很容易适应 Python2)。一个没有任何依赖的更好的解决方案,而且可能更快(嘿,这已经非常快了),是tarcat用一种支持静态链接的语言编写脚本,这样一个独立的可执行文件就足够了(即,不需要任何外部依赖,但是唯一的操作系统)。我把它留作 C、Rust、OCaml、Haskell 的未来练习,你选择。

请注意,如果 MD5 不适合您的需要,只需将md5第一个脚本内部替换为您的哈希实用程序。

希望这对任何阅读的人都有帮助。


ric*_*ich 5

让我惊讶的是 docker 并没有开箱即用地做这种事情。这是@mljrg 技术的一个变体:

#!/bin/sh

docker create $1 | {
  read cid
  docker export $cid | tar Oxv 2>&1 | shasum -a 256
  docker rm $cid > /dev/null
}
Run Code Online (Sandbox Code Playgroud)

它更短,根本不需要 python 依赖项或第二个脚本,我确定有缺点,但它似乎对我进行了一些测试。