【问题标题】:How to verify if the content of two Docker images is exactly the same?如何验证两个 Docker 镜像的内容是否完全相同?
【发布时间】:2018-03-01 12:06:26
【问题描述】:

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

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

【问题讨论】:

    标签: docker docker-image


    【解决方案1】:

    如果你想比较图片的内容,你可以使用docker inspect <imageName> 命令,你可以查看 RootFS 部分

    docker inspect redis

        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:eda7136a91b7b4ba57aee64509b42bda59e630afcb2b63482d1b3341bf6e2bbb",
                "sha256:c4c228cb4e20c84a0e268dda4ba36eea3c3b1e34c239126b6ee63de430720635",
                "sha256:e7ec07c2297f9507eeaccc02b0148dae0a3a473adec4ab8ec1cbaacde62928d9",
                "sha256:38e87cc81b6bed0c57f650d88ed8939aa71140b289a183ae158f1fa8e0de3ca8",
                "sha256:d0f537e75fa6bdad0df5f844c7854dc8f6631ff292eb53dc41e897bc453c3f11",
                "sha256:28caa9731d5da4265bad76fc67e6be12dfb2f5598c95a0c0d284a9a2443932bc"
            ]
        }
    

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

    【讨论】:

    • 这不起作用。我只是在两个同名图像的构建之间触摸了一个文件,并得到了不同的 sha256。
    • 如果你触摸了一个文件,这会导致修改导致不同的哈希,@mljrg
    • 仅获取 RootFS 作为一个字符串:docker inspect --format='{{.RootFS}}' <image>
    【解决方案2】:

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

    整体解决方案是这样的:

    1. 通过docker create ...为您的图像创建一个容器
    2. 通过docker export ... 将其整个文件系统导出到 tar 存档
    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; }
    

    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)
    

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

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

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

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

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

    希望这对阅读的人有所帮助。

    【讨论】:

      【解决方案3】:

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

      #!/bin/sh
      
      docker create $1 | {
        read cid
        docker export $cid | tar Oxv 2>&1 | shasum -a 256
        docker rm $cid > /dev/null
      }
      

      它更短,根本不需要 python 依赖项或第二个脚本,我确信有缺点,但它似乎对我有用,我已经完成了一些测试。

      【讨论】:

        【解决方案4】:

        似乎没有标准的方法来做到这一点。我能想到的最好方法是使用 Docker 多阶段构建功能。 例如,在这里我比较了 apline 和 debian 图像。在您的情况下,将图像名称设置为您要比较的名称

        我基本上将每个图像中的所有文件复制到一个 git 存储库中,并在每次复制后提交。

        FROM alpine as image1
        
        FROM debian as image2
        
        FROM ubuntu
        RUN apt-get update && apt-get install -y git
        RUN git config --global user.email "you@example.com" &&\
         git config --global user.name "Your Name"
        
        RUN mkdir images
        WORKDIR images
        RUN git init
        
        COPY --from=image1 / .
        RUN git add . && git commit -m "image1"
        
        COPY --from=image2 / .
        RUN git add . && git commit -m "image2"
        
        CMD tail > /dev/null
        

        这将为您提供一个带有 git 存储库的图像,该存储库记录两个图像之间的差异。

        docker build -t compare .
        docker run -it compare bash
        

        现在,如果您执行git log,您可以看到日志,您可以使用git diff <commit1> <commit2> 比较两个提交

        注意:如果镜像构建在第二次提交时失败,这意味着镜像是相同的,因为如果没有要提交的更改,git 提交将失败。

        【讨论】:

        • 这个进入评论区
        • 为什么要删除 container-diff 解决方案?不行吗?
        • @mljrg 这是我以前没有尝试过的东西,不确定它是否有效。
        • 你试过你的解决方案了吗?我没有,但据我所知,您最终会发现 alpine 和 debian 之间的差异......
        • @mljrg 是的,我已经尝试过了:)。同样,由于图像不同,我最终会有所不同。在您的情况下,将 alpine 和 debian 更改为您要比较的图像。我用相同的图像尝试过,没有检测到差异(在这种情况下,第二次提交失败,因为没有差异)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-24
        • 1970-01-01
        • 1970-01-01
        • 2020-01-28
        • 1970-01-01
        • 2016-12-23
        相关资源
        最近更新 更多