一、说明
Docker 镜像是通过构建 Dockerfiles 创建的。构建过程执行 Dockerfile 中的指令来创建构成最终镜像的文件系统层。 如果给出已有图像,您可以检索构建它的 Dockerfile 吗?在本文中,我们将研究两种可以实现此目的的方法。
二、逆向的需求
当您构建自己的 Docker 映像时,您应该将 Dockerfile 作为版本控制文件存储在源存储库中。这种做法可确保您始终可以检索用于组装镜像的说明。
如果您使用公共Registoy中的镜像,您将无法访问它的Dockerfile。或者您可能正在使用不直接提供 Dockerfile 的镜像快照。在这些情况下,您需要一种可以从计算机上的镜像提取 Dockerfile 的技术。
Docker 不提供任何内置功能来实现这一点。您可以自己设法进行逆向工程,以按需生成图像 Dockerfile 的近似版本。
三、使用Docker的history命令
docker history 命令显示镜像的层历史。它显示了用于构建每个连续文件系统层的命令,使其成为复制 Dockerfile 时的一个很好的起点。
这是一个用于 Node.js 应用程序的简单 Dockerfile:
FROM node:16
COPY app.js .
RUN app.js --init
CMD ["app.js"]
编译镜像:docker build
:
$ docker build -t node-app:latest .
现在使用 docker history 检查图像的层历史:
$ docker history node-app:latest
IMAGE CREATED CREATED BY SIZE COMMENT
c06fc21a8eed 8 seconds ago /bin/sh -c #(nop) CMD ["app.js"] 0B
74d58e07103b 8 seconds ago /bin/sh -c ./app.js --init 0B
22ea63ef9389 19 seconds ago /bin/sh -c #(nop) COPY file:0c0828d0765af4dd... 50B
424bc28f998d 4 days ago /bin/sh -c #(nop) CMD ["node"] 0B
<missing> 4 days ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry... 0B
...
历史包括图像中层的完整列表,包括从 node:16 基本镜像继承的层。层是有序的,所以最近的层在第一层。您可以根据创建时间确定示例 Dockerfile 创建的层的起始位置。这些显示了 Dockerfile 中使用的 COPY 和 CMD 指令的内部表示。
当表格仅限于显示每一层的命令时,docker history 输出更有用。您也可以禁用截断以查看与每一层关联的完整命令:
$ docker history node-app:latest --format "{{.CreatedBy}}" --no-trunc
/bin/sh -c #(nop) CMD ["app.js"]
/bin/sh -c ./app.js --init
/bin/sh -c #(nop) COPY file:0c0828d0765af4dd87b893f355e5dff77d6932d452f5681dfb98fd9cf05e8eb1 in .
/bin/sh -c #(nop) CMD ["node"]
/bin/sh -c #(nop) ENTRYPOINT ["docker-entrypoint.sh"]
...
从此命令列表中,您可以大致了解组装镜像的步骤。对于像这样的简单镜像,这些信息完全可以准确地重现 Dockerfile。
四、使用 Whaler自动提取图层
从 docker 历史记录中复制命令是一个费力的过程。基本是手工处理。幸运的是,有可用的社区工具可以根据镜像的层历史自动创建 Dockerfile。另一个选择是,由 Alpine 组织打包到 alpine/dfimage (Dockerfile-from-Image) Docker 镜像中的 Whaler工具。
4.1 什么是Whaler
Whaler 是一个 Go 程序,旨在将 docker 镜像逆向工程到创建它的 Dockerfile 中。它当前执行以下操作
-
从图像生成 Dockerfile
-
搜索添加的文件名以查找潜在的秘密文件
-
提取由 Docker ADD/COPY 指令添加的文件
-
它还显示杂项。信息,如端口打开、它运行的用户和环境变量。
4.2 Whaler使用方法示例
1)安装whaler
# 安装whaler到/usr/local/bin/目录下
wget -cO /usr/local/bin/whaler https://github.com/P3GLEG/Whaler/releases/download/1.0/Whaler_linux_amd64# 赋予可执行权限
chmod +x /usr/local/bin/whaler
2)常用参数
# 将docker客户端ID设置为特定版本-sV
whaler -sV=1.36 # 打印有关图像的所有详细信息 -v
whaler -v 镜像名# 将镜像曾保存到当前目录-x
3)将镜像逆向为dockfile
# 逆向镜像
whaler 镜像名# 示例
[master root ~]# whaler -sV=1.36 nginx
Analyzing nginx
Docker Version: 20.10.7
GraphDriver: overlay2
Environment Variables
|PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|NGINX_VERSION=1.21.5
|NJS_VERSION=0.7.1
|PKG_RELEASE=1~bullseyeOpen Ports
|80Image user
|User is rootPotential secrets:
Dockerfile:
CMD ["bash"]
LABEL maintainer=NGINX Docker Maintainers <docker-maint@nginx.com>
ENV NGINX_VERSION=1.21.5
ENV NJS_VERSION=0.7.1
ENV PKG_RELEASE=1~bullseye
RUN set -x \&& addgroup --system --gid 101 nginx \&& adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx \&& apt-get update \&& apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \&& NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; found=''; for server in hkp://keyserver.ubuntu.com:80 pgp.mit.edu ; do echo "Fetching GPG key $NGINX_GPGKEY from $server"; apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" \&& found=yes \&& break; done; test -z "$found" \&& echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" \&& exit 1; apt-get remove --purge --auto-remove -y gnupg1 \&& rm -rf /var/lib/apt/lists/* \&& dpkgArch="$(dpkg --print-architecture)" \&& nginxPackages=" nginx=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE} " \&& case "$dpkgArch" in amd64|arm64) echo "deb https://nginx.org/packages/mainline/debian/ bullseye nginx" >> /etc/apt/sources.list.d/nginx.list \&& apt-get update ;; *) echo "deb-src https://nginx.org/packages/mainline/debian/ bullseye nginx" >> /etc/apt/sources.list.d/nginx.list \&& tempDir="$(mktemp -d)" \&& chmod 777 "$tempDir" \&& savedAptMark="$(apt-mark showmanual)" \&& apt-get update \&& apt-get build-dep -y $nginxPackages \&& ( cd "$tempDir" \&& DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" apt-get source --compile $nginxPackages ) \&& apt-mark showmanual | xargs apt-mark auto > /dev/null \&& { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \&& ls -lAFh "$tempDir" \&& ( cd "$tempDir" \&& dpkg-scanpackages . > Packages ) \&& grep '^Package: ' "$tempDir/Packages" \&& echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \&& apt-get -o Acquire::GzipIndexes=false update ;; esac \&& apt-get install --no-install-recommends --no-install-suggests -y $nginxPackages gettext-base curl \&& apt-get remove --purge --auto-remove -y \&& rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \&& if [ -n "$tempDir" ]; then apt-get purge -y --auto-remove \&& rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; fi \&& ln -sf /dev/stdout /var/log/nginx/access.log \&& ln -sf /dev/stderr /var/log/nginx/error.log \&& mkdir /docker-entrypoint.d
COPY file:65504f71f5855ca017fb64d502ce873a31b2e0decd75297a8fb0a287f97acf92 in /docker-entrypoint.shCOPY file:0b866ff3fc1ef5b03c4e6c8c513ae014f691fb05d530257dfffd07035c1b75da in /docker-entrypoint.ddocker-entrypoint.d/docker-entrypoint.d/10-listen-on-ipv6-by-default.shCOPY file:0fd5fca330dcd6a7de297435e32af634f29f7132ed0550d342cad9fd20158258 in /docker-entrypoint.ddocker-entrypoint.d/docker-entrypoint.d/20-envsubst-on-templates.shCOPY file:09a214a3e07c919af2fb2d7c749ccbc446b8c10eb217366e5a65640ee9edcc25 in /docker-entrypoint.ddocker-entrypoint.d/docker-entrypoint.d/30-tune-worker-processes.shENTRYPOINT ["/docker-entrypoint.sh"]
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["nginx" "-g" "daemon off;"]
4.3 容器板的Whaler使用方法示例
尝试使用whaler,首先拉取whaler的镜像。
docker pull pegleg/whaler
启动whaler容器,分析nginx:latest镜像
docker run -t --rm -v /var/run/docker.sock:/var/run/docker.sock:ro pegleg/whaler -sV=1.36 nginx:latest
docker build --rm -t pegleg/whaler . alias whaler="docker run -t --rm -v /var/run/docker.sock:/var/run/docker.sock:ro pegleg/whaler" whaler -sV=1.36 nginx:latest
这个工具会自动拉取目标 docker 镜像。参数 -sV=1.36 并不总是必需的。
五、使用 Dfimage 自动提取图层
运行 dfimage 镜像并提供 Docker 标签将输出一个 Dockerfile,可用于复制引用的图像。您必须将主机的 Docker 套接字绑定到 dfimage 容器中,以便它可以访问您的图像列表并在需要时拉取标签。
$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock alpine/dfimage node-app:latest Analyzing node-app:latest Docker Version: 20.10.13 GraphDriver: overlay2 Environment Variables |PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |NODE_VERSION=16.14.2 |YARN_VERSION=1.22.18 Image user |User is root Dockerfile: ... ENTRYPOINT ["docker-entrypoint.sh"] CMD ["node"] COPY file:bcbc3d5784a8f1017653685866d30e230cae61d0da13dae32525b784383ac75f in . app.js RUN ./app.js --init CMD ["app.js"]
创建的 Dockerfile 包含从头开始(空文件系统)到指定镜像的最后一层所需的一切。它包括来自基础镜像的所有层。您可以在上面示例输出的第一个 ENTRYPOINT 和 CMD 指令中看到这些(为简洁起见,其他基础镜像层已被省略)。
除了 COPY 之外,我们镜像的特定指令与原始 Dockerfile 中的内容相匹配。您现在可以将这些指令复制到一个新的 Dockerfile 中,使用整个 dfimage 输出或只获取与最终镜像相关的部分。后一种选择只有在您知道原始基础图像的轮廓时才有可能,因此您可以在文件顶部添加 FROM 指令。
六、逆向工程的局限性
在许多情况下,dfimage 将能够组装一个可用的 Dockerfile。尽管如此,它并不完美,也不能保证完全匹配。与镜像的原始 Dockerfile 相比的差异程度将根据所使用的指令而有所不同。
并非所有指令都在层历史记录中捕获。不受支持的将丢失,您无法确定它们是什么。使用 RUN、ENV、WORKDIR、ENTRYPOINT 和 CMD 等命令和元数据指令可获得最佳准确性。如果他们的命令没有导致文件系统更改,则 RUN 指令仍然可能会丢失,这意味着没有创建新的镜像层。
当您到达最终目的地时,这足以帮助您了解复制的内容以及原因。然后,您可以使用此信息将新的源路径插入到 Dockerfile 中,您可以将其用于未来的构建。在其他情况下,检查图像中的文件可能有助于揭示副本的用途,以便您可以为主机路径确定一个有意义的文件名。
七、总结
Docker 不提供直接从image反推出Dockerfile 的方法。不过,仍然可以将构建过程拼凑起来。对于指令很少的简单图像,您通常可以通过查看 docker history 命令输出中的 CREATED BY 列来手动恢复成指令。
具有更复杂构建过程的更大图像最好使用 dfimage 等工具进行分析。这会为您解析复杂的 dockerfile代码,生成一个新的 Dockerfile,且与可能的原始 Dockerfile 尽量匹配。文章来源:https://www.toymoban.com/news/detail-765568.html
逆向工程工作并不完美,一些 Dockerfile 指令在构建过程中丢失或损坏。因此,您不可假定以这种方式创建的 Dockerfile 是原始文件的准确恢复。您可能还必须对 ADD 和 COPY 指令进行一些手动调整,恢复已转换为上下文引用的宿主机文件路径。文章来源地址https://www.toymoban.com/news/detail-765568.html
到了这里,关于【docker知识】如何从镜像返回它的Dockerfile文件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!