如何查找Docker镜像的基础镜像

66
我有一个Docker镜像,我想知道它是从哪个镜像创建的。当然,有多个层,但我想找出最后一个镜像(Dockerfile中此图像的FROM语句)?
我尝试使用“docker image history”和“docker image inspect”,但在其中找不到此信息。
我尝试使用以下命令,但它给了我一个错误消息。
alias dfimage="sudo docker run -v /var/run/docker.sock:/var/run/docker.sock --rm xyz/mm:9e945ff"
dfimage febae8978318

这是我收到的错误信息

container_linux.go:235: starting container process caused "exec: \"febae8978318\": executable file not found in $PATH"
/usr/bin/docker-current: Error response from daemon: oci runtime error: container_linux.go:235: starting container process caused "exec: \"febae8978318\": executable file not found in $PATH".

答案在这里:https://dev59.com/3WIk5IYBdhLWcg3wn_b1#53841690 - Arkadiusz Drabczyk
4个回答

39

使用的简单方法是

docker image history deno

这个命令将会给你输出以下内容:

enter image description here

接着,只需要看IMAGE列并获取刚刚在第一个<missing>上面的那个图像ID a24bb4013296

然后进行以下操作:

对于Linux系统

docker image ls | grep a24bb4013296

适用于Windows操作系统

docker image ls | findstr a24bb4013296

这将为您提供基本图像名称

在此输入图像描述


23
为了让这个工作起来,我认为你需要自己构建镜像。至少在 Docker 1.10 之后,当从仓库拉取镜像时,这些是中间层,你无法获取它们。请参见 https://dev59.com/QFsW5IYBdhLWcg3wKkk0#35312577。 - Petri Ryhänen
9
是的,我刚刚尝试了一下,所有内容都显示为丢失。 - KillerSnail
据我记忆,这曾经能运行,但现在不能了。 - Jason
2
为什么我在IMAGE列中看到缺失,像这样:<missing> 9个月前 - John Xiao

20

这些信息实际上并不存在。镜像包含其父级的层,但是很难通过反向层摘要来还原 FROM 指令,除非你恰好拥有(或能够找到)包含这些层的镜像。

如果你手头有父镜像(或者能够找到它们),你可以通过对比层推断出你的镜像用于其 FROM 指令的镜像(或祖先)。

理论示例

假设你的镜像 FOO 包含层 1 2 3 4 5 6。如果你的系统中有另一个镜像 BAR 包含层 1 2 3,那么你可以推断出镜像 BAR 是镜像 FOO 的祖先--也就是说,在其层次结构中的某个时刻使用了 FROM BAR

进一步假设你有另一个镜像 BAZ,其中包含层 1 2 3 4 5。你可以推断出镜像 BAZ 具有镜像 BAR 在其祖先中,并且镜像 FOO 继承自镜像 BAZ(因此间接继承自 BAR)。

从这些信息中,你可以推断出这些镜像的 Dockerfile 大致如下:

# Dockerfile of image BAR
FROM scratch
# layers 1 2 and 3
COPY ./one /
COPY ./two /
COPY ./three /
# Dockerfile of Image BAZ
FROM BAR
RUN echo "this makes layer 4" > /four
RUN echo "this makes layer 5" > /five
# Dockerfile of image FOO
FROM BAZ
RUN echo "this makes layer 6" > /six

通过查看每个镜像的 docker image history 命令,您可以获得确切的命令。

但是,请记住 Docker 标签是可变的;维护人员会创建新镜像并将标签移动到这些镜像上。因此,如果您今天使用 FROM python:3.8.1 构建了一个镜像,则它不会包含与几周前使用相同的 FROM 行构建镜像时相同的层。您需要 SHA256 摘要以确保您使用的是完全相同的镜像。

实际例子,本地镜像

现在我们了解了识别镜像及其基础的理论知识,让我们通过一个实际例子来实践一下。

注意:由于我使用的标签会随时间而变化(参见上述关于标签可变性的说明),因此我将使用 SHA256 摘要来拉取图像,以便可以由此答案的观众再现它。

假设我们有一个特定的镜像,我们想找到它的基础镜像。我们将在此处使用官方的 maven 镜像。

首先,我们将查看其层次结构。

# maven:3.6-jdk-11-slim at time of writing, on my platform
IMAGE="docker.io/maven@sha256:55f1c145a04e01706233d68fe0b6b20bf76f765ab32f3fe6e29c8ef933917af6"
docker pull $IMAGE
docker image inspect $IMAGE | jq -r '.[].RootFS.Layers[]'

这将输出图层:

sha256:6e06900bc10223217b4c78081a857866f674c462e4f90593b01894da56df336d
sha256:eda2f4da9b1e70500ac340d40ee039ef3877e8be13b9a24cd345406bf6693412
sha256:6bdb7b3c3e226bdfaa911ba72a95fca13c3979cd150061d570cf569e93037ce6
sha256:ce217e530345060ca0973807a3288560e1e15cf1a4eeec44d6aa594a926c92dc
sha256:f256c980a7d17a00f57fd42a19f6323fcc2341fa46eba128def04824cafa5afa
sha256:446b1af848de2dcb92bbd229ca6ecaabf2f48dab323c19f90d02622e09a8fa67
sha256:10652cf89eaeb5b5d8e0875a6b1867b5cf92c509a9555d3f57d87fab605115a3
sha256:d9a4cf86bf01eb170242ca3b0ce456159fd3fddc9c4d4256208a9d19bae096ca

现在,我们可以尝试查找具有这些层的(严格)子集的其他图像。假设你手头上已经有这些图像,你可以通过交叉引用磁盘上已有的图像的层来找到它们,例如使用docker image inspect

在这种情况下,我恰巧知道这些图像是什么,并且已经拥有了它们(如果你没有这些图像,稍后我将讨论可能需要采取的步骤),因此我们将拉取这些图像并查看这些层。

如果您想跟随操作:

# openjdk:11.0.10-jdk-slim at time of writing, on my platform
OPENJDK='docker.io/openjdk@sha256:fe6a46a26ff7d6c31b258e07b3d53f0c42fe68f55f646cc39d60d0b17cbc827b'

# debian:buster-20210329-slim at time of writing on my platform
DEBIAN='docker.io/debian@sha256:088be7d6017ad3ae98325f47707112e1f61687c371be1865e55d5e5531ca97fd'

docker pull $OPENJDK
docker pull $DEBIAN
如果我们检查这些镜像并将它们与使用docker image inspect命令查看maven镜像的输出中看到的层进行比较,我们可以确认openjdkdebian的层存在于我们原始的maven镜像中。
$ docker image inspect $DEBIAN | jq -r '.[].RootFS.Layers[]'
sha256:6e06900bc10223217b4c78081a857866f674c462e4f90593b01894da56df336d

$ docker image inspect $OPENJDK | jq -r '.[].RootFS.Layers[]'
sha256:6e06900bc10223217b4c78081a857866f674c462e4f90593b01894da56df336d
sha256:eda2f4da9b1e70500ac340d40ee039ef3877e8be13b9a24cd345406bf6693412
sha256:6bdb7b3c3e226bdfaa911ba72a95fca13c3979cd150061d570cf569e93037ce6
sha256:ce217e530345060ca0973807a3288560e1e15cf1a4eeec44d6aa594a926c92dc

如前所述,由于这5层是maven镜像的8层严格子集,因此我们可以得出结论:openjdkdebian镜像至少都在maven镜像的祖先路径中。

我们还可以推断出,最后3层很可能来自maven镜像本身(或者可能来自某个未知的镜像)。

注意事项:当您没有本地镜像时

当然,上述内容仅适用于我手头恰好有所有镜像的情况。因此,您需要拥有这些镜像或能够通过层摘要定位它们。

对于像Docker Hub或您自己的私有存储库之类的注册表中可能可用的信息,您仍然可以通过该信息找出答案。

对于官方镜像,docker-library/repo-info 包含了关于官方镜像的历史信息,包括目录中各种标记的层摘要。例如,您可以将其用作层信息源。

如果您将其想象为层摘要数据库,则至少可以推断出这些官方镜像的祖先。

“分发”(远程)摘要与“内容”(本地)摘要

需要注意的一个重要细节是,当您在本地检查图像以获取其层摘要时,您会得到层的内容摘要。如果您在注册表清单中查看层摘要(例如出现在docker-library/repo-info项目中),则会获得压缩的分发摘要,并且无法将层摘要与内容进行比较。

因此,您只能进行local <--> local OR remote <--> remote 的摘要比较。

示例:使用远程镜像

假设我想执行相同的操作,但我想将远程仓库中的映像与其基础关联起来。我们可以通过查看远程清单中的层来完成相同的操作。

您可以找到有关如何为您特定的注册表执行此操作的参考,如此答案中对dockerhub的描述所述。

使用上述示例中的相同镜像,我们将发现分发层摘要也以相同的方式匹配。

$ get-remote-layers $IMAGE
sha256:6fcf2156bc23db75595b822b865fbc962ed6f4521dec8cae509e66742a6a5ad3
sha256:96fde6667c188c81fcddee021ccbb3e054ebe83350fd4609e17a3d37f0ec7f9d
sha256:74d17759dd2a1b51afc740fadd96f655260689a2087308e40d1865a0098c5fae
sha256:bbe8ebb5d0a64d265558901c7c6c66e1d09f664da57cdb2e5f69ba52a7109d31
sha256:b2edaadd7dd62cfe7f551b902244ee67b84bc5c0b6538b9480ac9ca97a0a4986
sha256:0fca65d33e353bdfdd5edd8d4c8ab5efde52c078bd25e2dcf454f995e5420725
sha256:d6d771d0512387eee1e419a965b929a9a3b0365cf1935b3719d60bf9feffcf63
sha256:dee8cd26669373102db07820072127c46bbfdad340a586ee9dfe60ae933eac2b

$ get-remote-layers $DEBIAN
sha256:6fcf2156bc23db75595b822b865fbc962ed6f4521dec8cae509e66742a6a5ad3

$ get-remote-layers $OPENJDK
sha256:6fcf2156bc23db75595b822b865fbc962ed6f4521dec8cae509e66742a6a5ad3
sha256:96fde6667c188c81fcddee021ccbb3e054ebe83350fd4609e17a3d37f0ec7f9d
sha256:74d17759dd2a1b51afc740fadd96f655260689a2087308e40d1865a0098c5fae
sha256:bbe8ebb5d0a64d265558901c7c6c66e1d09f664da57cdb2e5f69ba52a7109d31

在仓库中使用分发摘要有个限制,就是你只能比较相同清单模式版本的摘要。所以,如果一个镜像是使用清单v1推送的,那么再次使用清单v2推送时它将具有不同的摘要。

太长不想读?

镜像包含其祖先镜像的层。因此,如果镜像A包含镜像B的一个严格子集,那么你知道镜像B是镜像A的后代。

你可以利用Docker镜像的这个属性来确定你的镜像从哪些基础镜像派生而来。


get-remote-layers 定义在哪里? - Jason
@Jason 可能取决于您使用的注册表。对于 Dockerhub,链接的答案 中有一个可行的实现,用于从远程仓库检索清单。层摘要在清单中。 - sytech
根据我的观察,在 Docker tarball 的 manifest.json 中,Layers 数组中的子镜像总是缺少基础镜像的最后一层。 - Hritik

10

您可以使用此答案中建议的方法:https://dev59.com/3WIk5IYBdhLWcg3wn_b1#53841690

首先,拉取chenzj/dfimage

docker pull chenzj/dfimage

获取您的图片ID:

docker images | grep <IMAGE_NAME> | awk '{print $3}'
用你的图像名称替换<IMAGE_NAME>。使用此ID作为chenzj/dfimage的参数:
docker run -v /var/run/docker.sock:/var/run/docker.sock --rm chenzj/dfimage <IMAGE_ID>

如果您觉得这太难了,只需拉取 chenzj/dfimage 镜像,然后使用以下 docker-get-dockerfile.sh 脚本:

#!/usr/bin/env sh

if [ "$#" -lt 1 ]
then
    printf "Image name needed\n" >&2
    exit 1
fi

image_id="$(docker images | grep "^$1 " | awk '{print $3}')"
if [ -z "$image_id" ]
then
    printf "Image not found\n" >&2
    exit 2
fi

docker run -v /var/run/docker.sock:/var/run/docker.sock --rm chenzj/dfimage "$image_id"
您需要将图像名称作为参数传递。例如用法:
$ ./docker-get-dockerfile.sh alpine
FROM alpine:latest
ADD file:fe64057fbb83dccb960efabbf1cd8777920ef279a7fa8dbca0a8801c651bdf7c in /
CMD ["/bin/sh"]

8
这将把所讨论的图像显示为基础图像。 - Rakesh Gupta
2
当我这样做时,它显示“FROM”作为我试图找到基础的图像。我不太明白 - 从我的图像 - 做很多事情 - 就有了我的图像?? - mjaggard
我确信这个方法在2019年曾经有效,但自那以后可能发生了一些变化。 - Arkadiusz Drabczyk
dfimage 图像对我来说根本不起作用,它只是无限期地挂起。 - Speeddymon
是的,对我来说也显示相同的图像是基本图像...这很奇怪。 - xbmono

-3
docker run image:tag cat /etc/*release*

使用上述命令(将“image:tag”更改为您的镜像名称和标签),从该镜像运行一个Docker容器。您的容器将打印出您需要回答问题的详细信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接