1. 简介

通常,我们是从 Dockerfile 构建出镜像(image),但有没有可能反过来操作,从镜像还原出 Dockerfile 呢?

虽然 Docker 官方并没有提供标准工具来从镜像生成 Dockerfile,但我们可以借助一些技巧实现近似的效果。本文将介绍几种从镜像中反向生成 Dockerfile 的方法,包括使用 docker history 和第三方工具如 dfimagededockify 等。

2. 使用 docker history

docker history 是一个查看镜像各层构建历史的命令,它可以帮助我们还原出镜像中使用的构建指令。通过这些指令,我们就可以手动还原出一个近似原始的 Dockerfile。

整个过程大致分为以下几个步骤:

  • 创建一个原始 Dockerfile 并构建镜像
  • 使用 docker history 查看镜像的构建历史
  • 根据输出信息还原 Dockerfile
  • 对还原出的 Dockerfile 进行调整以保证可用性

2.1. 创建 Dockerfile

以下是一个简单的 Python Web 应用的 Dockerfile 示例:

$ cat ParentDockerfile
FROM python:3.10-bullseye
EXPOSE 80

COPY static static
COPY app.py app.py
COPY index.html index.html

RUN pip install flask

ENTRYPOINT [ "/bin/bash", "-c", "flask run --debug -p 80 -h 0.0.0.0" ]

这个 Dockerfile 使用了 Python 官方镜像作为基础镜像,复制了几个文件,并安装了 Flask 框架,最后设置了容器启动命令。

2.2. 构建镜像

使用以下命令构建镜像:

$ docker build . -f ParentDockerfile -t baeldung-image

确认镜像是否成功生成:

$ docker images
REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
baeldung-image   latest    a4a6f7d78efc   15 seconds ago   923MB

这个镜像体积较大,但不影响我们做测试。

2.3. 使用 docker history 查看镜像构建历史

运行以下命令查看镜像构建历史:

$ docker history baeldung-image

输出结果中的 CREATED BY 列包含了构建该层所使用的指令。但默认输出可能会被截断,我们可以加上 --no-trunc 参数避免截断:

$ docker history --no-trunc baeldung-image

为了更清晰地查看 CREATED BY 列,可以使用 --format 参数只显示该列内容:

$ docker history --no-trunc --format {{.CreatedBy}} baeldung-image

⚠️ 注意:输出是倒序的,即 Dockerfile 中的最后一条指令会出现在输出的第一行。我们可以用 tac 命令反转输出顺序:

$ docker history --no-trunc --format {{.CreatedBy}} baeldung-image | tac

这样输出就和原始 Dockerfile 的顺序一致了。

2.4. 还原 Dockerfile

通过上述命令输出的内容,我们可以手动还原出一个 Dockerfile:

$ docker history --no-trunc --format {{.CreatedBy}} baeldung-image | tac

输出内容如下(截取关键部分):

EXPOSE map[80/tcp:{}]
COPY static static # buildkit
COPY app.py app.py # buildkit
COPY index.html index.html # buildkit
RUN /bin/sh -c pip install flask # buildkit
ENTRYPOINT ["/bin/bash" "-c" "flask run --debug -p 80 -h 0.0.0.0"]

根据这些信息,我们可以写出如下 Dockerfile:

FROM python:3.11-bookworm
EXPOSE 80
COPY static static
COPY app.py app.py
COPY index.html index.html
RUN pip install flask
ENTRYPOINT ["/bin/bash", "-c", "flask run --debug -p 80 -h 0.0.0.0"]

2.5. 调整 Dockerfile

在还原过程中,需要注意以下几点:

EXPOSE 指令格式:原始输出是 EXPOSE map[80/tcp:{}],应简化为 EXPOSE 80
删除注释# buildkit 这类注释应删除
ENTRYPOINT 格式:确保 JSON 数组中的命令用逗号分隔,如:

ENTRYPOINT ["/bin/bash", "-c", "flask run --debug -p 80 -h 0.0.0.0"]

最终,构建新的镜像验证是否可用:

$ docker build . -t new-image

如果构建成功,说明还原的 Dockerfile 是可用的。

3. 使用第三方工具

除了 docker history,还有一些第三方工具也能帮助我们从镜像生成 Dockerfile,比如:

  • dfimage
  • dedockify

这些工具通常能提供更结构化的输出,但依然需要人工调整。

3.1. 使用 dfimage

可以使用如下命令创建别名:

$ alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage"

然后运行:

$ dfimage baeldung-image

输出内容包括:

  • 环境变量
  • 暴露的端口
  • 用户信息
  • 可能的敏感信息
  • Dockerfile 指令

示例输出:

Dockerfile:
CMD ["bash"]
...truncated...
ENTRYPOINT ["/bin/bash" "-c" "flask run --debug -p 80 -h 0.0.0.0"]

⚠️ 注意:虽然输出顺序通常已经是正序,但建议仍需检查是否与实际构建顺序一致。

4. 小结

方法 是否官方支持 输出是否有序 是否需要人工调整
docker history ❌ 否 ❌ 否(需 tac ✅ 是
dfimage ❌ 否 ✅ 是 ✅ 是
dedockify ❌ 否 ✅ 是 ✅ 是

虽然没有官方工具能直接从镜像生成 Dockerfile,但借助 docker history 或第三方工具,我们仍然可以还原出一个近似的 Dockerfile。不过,生成的结果通常需要手动调整后才能使用。

建议:

  • ✅ 优先使用 docker history + tac 的方式还原 Dockerfile
  • ✅ 第三方工具如 dfimage 可作为辅助,提高效率
  • ✅ 不要盲目信任生成的 Dockerfile,务必验证其正确性

💡 本文参考自 Baeldung 的相关文章,并经过原创改写,内容更贴合中文技术圈习惯。


原始标题:Generating a Dockerfile From an Image