如何验证两张Docker图片的内容是否完全一样?
How to verify if the content of two Docker images is exactly the same?
如何判断两张Docker图像的文件系统结构完全相同,且无论文件时间戳如何,对应文件的内容都相同?
我尝试了图像 ID,但它们在从相同的 Docker 文件和干净的本地存储库构建时有所不同。我通过构建一个图像来完成此测试,清理本地存储库,然后触摸其中一个文件以更改其修改日期,然后构建第二个图像,并且它们的图像 ID 不匹配。我使用 Docker 17.06(我相信是最新版本)。
如果你想比较图像的内容,你可以使用 docker inspect <imageName>
命令,你可以查看 RootFS
部分
docker inspect redis
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:eda7136a91b7b4ba57aee64509b42bda59e630afcb2b63482d1b3341bf6e2bbb",
"sha256:c4c228cb4e20c84a0e268dda4ba36eea3c3b1e34c239126b6ee63de430720635",
"sha256:e7ec07c2297f9507eeaccc02b0148dae0a3a473adec4ab8ec1cbaacde62928d9",
"sha256:38e87cc81b6bed0c57f650d88ed8939aa71140b289a183ae158f1fa8e0de3ca8",
"sha256:d0f537e75fa6bdad0df5f844c7854dc8f6631ff292eb53dc41e897bc453c3f11",
"sha256:28caa9731d5da4265bad76fc67e6be12dfb2f5598c95a0c0d284a9a2443932bc"
]
}
如果所有层都相同,则图像包含相同的内容
似乎没有执行此操作的标准方法。我能想到的最好方法是使用 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 提交将失败。
经过一些研究,我想出了一个解决方案,根据我的测试,它既快速又干净。
整体解决方案是这样的:
- 通过
docker create ...
为您的图像创建一个容器
- 通过
docker export ...
将其整个文件系统导出到 tar 存档
- 将存档目录名称、符号链接名称、符号链接内容、文件名和文件内容通过管道传输到哈希函数(例如 MD5)
- 比较不同图像的哈希值以验证它们的内容是否相等
就是这样。
从技术上讲,这可以按如下方式完成:
1) 创建文件md5docker
,并赋予其执行权限,如chmod +x md5docker
:
#!/bin/sh
dir=$(dirname "[=10=]")
docker create | { 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("[0;31mIGNORING:[0m ", tarinfo.name, file=sys.stderr)
3) 现在调用 ./md5docker <image>
,其中 <image>
是您的图像名称或 ID,以计算图像整个文件系统的 MD5 哈希值。
要验证两个图像是否具有相同的内容,只需检查它们的哈希是否与步骤 3 中计算的相同)。
请注意,此解决方案仅考虑内容目录结构、常规文件内容和符号链接(软链接和硬链接)。如果您需要更多,只需更改 tarcat
脚本,添加更多 elif
子句测试您希望包含的内容(请参阅 Python's tarfile,并查找与需要的内容)。
我在这个解决方案中看到的唯一限制是它对 Python 的依赖(我正在使用 Python3,但它应该很容易适应 Python2)。一个没有任何依赖的更好的解决方案,而且可能更快(嘿,这已经非常快了),是用一种支持静态链接的语言编写 tarcat
脚本,这样一个独立的可执行文件就足够了(即,一个不需要任何外部依赖项,但唯一的 OS)。我把它作为 C、Rust、OCaml 的未来练习,Haskell,你选择。
注意,如果 MD5 不适合您的需要,只需将第一个脚本中的 md5
替换为您的哈希实用程序。
希望这对阅读的人有所帮助。
令我惊讶的是 docker 并没有开箱即用。这是@mljrg 技术的变体:
#!/bin/sh
docker create | {
read cid
docker export $cid | tar Oxv 2>&1 | shasum -a 256
docker rm $cid > /dev/null
}
它更短,根本不需要 python 依赖项或第二个脚本,我确信它有缺点,但它似乎对我完成的少数测试有效。
如何判断两张Docker图像的文件系统结构完全相同,且无论文件时间戳如何,对应文件的内容都相同?
我尝试了图像 ID,但它们在从相同的 Docker 文件和干净的本地存储库构建时有所不同。我通过构建一个图像来完成此测试,清理本地存储库,然后触摸其中一个文件以更改其修改日期,然后构建第二个图像,并且它们的图像 ID 不匹配。我使用 Docker 17.06(我相信是最新版本)。
如果你想比较图像的内容,你可以使用 docker inspect <imageName>
命令,你可以查看 RootFS
docker inspect redis
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:eda7136a91b7b4ba57aee64509b42bda59e630afcb2b63482d1b3341bf6e2bbb",
"sha256:c4c228cb4e20c84a0e268dda4ba36eea3c3b1e34c239126b6ee63de430720635",
"sha256:e7ec07c2297f9507eeaccc02b0148dae0a3a473adec4ab8ec1cbaacde62928d9",
"sha256:38e87cc81b6bed0c57f650d88ed8939aa71140b289a183ae158f1fa8e0de3ca8",
"sha256:d0f537e75fa6bdad0df5f844c7854dc8f6631ff292eb53dc41e897bc453c3f11",
"sha256:28caa9731d5da4265bad76fc67e6be12dfb2f5598c95a0c0d284a9a2443932bc"
]
}
如果所有层都相同,则图像包含相同的内容
似乎没有执行此操作的标准方法。我能想到的最好方法是使用 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 提交将失败。
经过一些研究,我想出了一个解决方案,根据我的测试,它既快速又干净。
整体解决方案是这样的:
- 通过
docker create ...
为您的图像创建一个容器
- 通过
docker export ...
将其整个文件系统导出到 tar 存档
- 将存档目录名称、符号链接名称、符号链接内容、文件名和文件内容通过管道传输到哈希函数(例如 MD5)
- 比较不同图像的哈希值以验证它们的内容是否相等
就是这样。
从技术上讲,这可以按如下方式完成:
1) 创建文件md5docker
,并赋予其执行权限,如chmod +x md5docker
:
#!/bin/sh
dir=$(dirname "[=10=]")
docker create | { 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("[0;31mIGNORING:[0m ", tarinfo.name, file=sys.stderr)
3) 现在调用 ./md5docker <image>
,其中 <image>
是您的图像名称或 ID,以计算图像整个文件系统的 MD5 哈希值。
要验证两个图像是否具有相同的内容,只需检查它们的哈希是否与步骤 3 中计算的相同)。
请注意,此解决方案仅考虑内容目录结构、常规文件内容和符号链接(软链接和硬链接)。如果您需要更多,只需更改 tarcat
脚本,添加更多 elif
子句测试您希望包含的内容(请参阅 Python's tarfile,并查找与需要的内容)。
我在这个解决方案中看到的唯一限制是它对 Python 的依赖(我正在使用 Python3,但它应该很容易适应 Python2)。一个没有任何依赖的更好的解决方案,而且可能更快(嘿,这已经非常快了),是用一种支持静态链接的语言编写 tarcat
脚本,这样一个独立的可执行文件就足够了(即,一个不需要任何外部依赖项,但唯一的 OS)。我把它作为 C、Rust、OCaml 的未来练习,Haskell,你选择。
注意,如果 MD5 不适合您的需要,只需将第一个脚本中的 md5
替换为您的哈希实用程序。
希望这对阅读的人有所帮助。
令我惊讶的是 docker 并没有开箱即用。这是@mljrg 技术的变体:
#!/bin/sh
docker create | {
read cid
docker export $cid | tar Oxv 2>&1 | shasum -a 256
docker rm $cid > /dev/null
}
它更短,根本不需要 python 依赖项或第二个脚本,我确信它有缺点,但它似乎对我完成的少数测试有效。