如何检查失败的`docker build`的文件系统?

Alt*_*eus 238 debugging docker cpanm

我正在尝试为我们的开发过程构建一个新的Docker镜像,使用cpanm安装一堆Perl模块作为各种项目的基本映像.

在开发Dockerfile时,cpanm返回失败代码,因为某些模块没有干净地安装.

我很确定我需要apt安装更多东西.

我的问题是,在哪里可以找到/.cpanm/work输出中引用的目录,以便检查日志?在一般情况下,如何检查失败docker build命令的文件系统?

早上编辑咬了子弹然后跑了一个find我发现的

/var/lib/docker/aufs/diff/3afa404e[...]/.cpanm
Run Code Online (Sandbox Code Playgroud)

这是可靠的,还是我最好建立一个"裸"容器并手动运行东西,直到我拥有我需要的所有东西?

Tho*_*eil 324

每次RUNdocker 成功执行Dockerfile中的命令时,都会提交映像文件系统中的新层.方便地,您可以使用这些图层ID作为图像来启动新容器.

采用以下Dockerfile:

FROM busybox
RUN echo 'foo' > /tmp/foo.txt
RUN echo 'bar' >> /tmp/foo.txt
Run Code Online (Sandbox Code Playgroud)

并构建它:

$ docker build -t so-2622957 .
Sending build context to Docker daemon 47.62 kB
Step 1/3 : FROM busybox
 ---> 00f017a8c2a6
Step 2/3 : RUN echo 'foo' > /tmp/foo.txt
 ---> Running in 4dbd01ebf27f
 ---> 044e1532c690
Removing intermediate container 4dbd01ebf27f
Step 3/3 : RUN echo 'bar' >> /tmp/foo.txt
 ---> Running in 74d81cb9d2b1
 ---> 5bd8172529c1
Removing intermediate container 74d81cb9d2b1
Successfully built 5bd8172529c1
Run Code Online (Sandbox Code Playgroud)

您现在可以开始从一个新的容器00f017a8c2a6,044e1532c6905bd8172529c1:

$ docker run --rm 00f017a8c2a6 cat /tmp/foo.txt
cat: /tmp/foo.txt: No such file or directory

$ docker run --rm 044e1532c690 cat /tmp/foo.txt
foo

$ docker run --rm 5bd8172529c1 cat /tmp/foo.txt
foo
bar
Run Code Online (Sandbox Code Playgroud)

当然你可能想要启动一个shell来探索文件系统并尝试命令:

$ docker run --rm -it 044e1532c690 sh      
/ # ls -l /tmp
total 4
-rw-r--r--    1 root     root             4 Mar  9 19:09 foo.txt
/ # cat /tmp/foo.txt 
foo
Run Code Online (Sandbox Code Playgroud)

当其中一个Dockerfile命令失败时,您需要做的是查找前一层id并在从该id创建的容器中运行shell:

docker run --rm -it <id_last_working_layer> bash -il
Run Code Online (Sandbox Code Playgroud)

进入容器后:

  • 尝试失败的命令,并重现该问题
  • 然后修复命令并测试它
  • 最后用fixed命令更新你的Dockerfile

如果您确实需要在失败的实际图层中进行实验而不是在最后一个工作图层中进行实验,请参阅Drew的答案.

  • 如果看不到哈希 ID,需要添加 `DOCKER_BUILDKIT=0 docker build ......` (15认同)
  • 我认为这不起作用,因为它说"无法找到图像"d5219f1ffda9:最新的"本地".但是,我对多种ID感到困惑.事实证明,您必须使用直接在箭头后面的ID,而不是那些说"在...中运行"的ID. (10认同)
  • 当其中一个Dockerfile命令失败时,你需要做的是查找前一层的id并运行一个带有该id的shell的容器:`docker run --rm -it <id_last_working_layer> bash -il`和一旦在容器中尝试无法重现问题的命令,然后修复命令并测试它,最后使用fixed命令更新Dockerfile. (6认同)
  • 好的,这实际上非常有用,但我确实遇到了一个问题,如果容器构建失败,我不能将这个技巧与它所说的正在工作的容器的哈希一起使用。如果 RUN 失败,则不会创建任何图像。我可以连接到从未清理过的中间容器吗? (3认同)
  • 当我运行“docker build”时,它不会给我每层的哈希 ID。我没有看到任何命令选项来启用此功能。 (3认同)
  • @ADJenks 非常烦人,不是吗!在这里找到答案:/sf/ask/4593006491/ 基本上你需要在超级秘密选项设置中将 buildkit 更改为 false。也许他们应该在上面贴一个“小心豹子”的标志以确保安全。 (3认同)
  • 是的,它确实.当您可以随意重新创建容器时,没有必要保留仅用于调试Dockerfile的容器. (2认同)
  • 此外,你可以`docker diff <container>`并获得在该特定层上进行的特定文件系统更改的完整列表(在该图像的整个文件系统中添加,删除或更改的文件). (2认同)

Dre*_*rew 183

最佳答案适用于您要在失败命令之前检查状态的情况.

但是,该问题询问如何检查故障容器本身的状态.在我的情况下,失败的命令是一个需要几个小时的构建,因此在失败的命令之前倒带并再次运行它需要很长时间并且不是很有用.

这里的解决方案是找到失败的容器:

$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                          PORTS               NAMES
6934ada98de6        42e0228751b3        "/bin/sh -c './utils/"   24 minutes ago      Exited (1) About a minute ago                       sleepy_bell
Run Code Online (Sandbox Code Playgroud)

将其提交给图像:

$ docker commit 6934ada98de6
sha256:7015687976a478e0e94b60fa496d319cdf4ec847bcd612aecf869a72336e6b83
Run Code Online (Sandbox Code Playgroud)

然后运行图像[如果需要,运行bash]:

$ docker run -it 7015687976a4 [bash -il]
Run Code Online (Sandbox Code Playgroud)

现在,您实际上在查看失败时的构建状态,而不是在运行导致失败的命令之前.

  • 这应该是一个公认的答案!谢谢! (13认同)
  • 如果你使用 `DOCKER_BUILDKIT=1` 来构建你的 `Dockerfile`,这将不起作用 (4认同)
  • 对于@nmh 的观点 - 如果您只是在构建输出之后,则不需要提交图像。您可以使用 [docker container cp](https://docs.docker.com/engine/reference/commandline/container_cp/) 从失败的构建容器中提取文件结果。 (2认同)

Jan*_*ber 47

更新较新的 docker 版本 20.10 及以上

使用 DOCKER_BUILDKIT=0 docker build ... 以获得中间容器哈希从旧版本闻名。

在较新版本的 Buildkit 上默认激活。建议仅将其用于调试目的。Build Kit 可以使您的构建速度更快。

供参考:Buildkit 不支持中间容器哈希:https : //github.com/moby/buildkit/issues/1053

Linux 或 MacOS

DOCKER_BUILDKIT=0 docker build ...
Run Code Online (Sandbox Code Playgroud)

视窗

set DOCKER_BUILDKIT=0 docker build ...
Run Code Online (Sandbox Code Playgroud)

感谢@David Callanan

  • 这是多么令人愤怒的变化啊 (4认同)

Meg*_*kie 9

目前使用最新的 docker-desktop,还没有办法选择退出新的 Buildkit,它还不支持调试(请关注此 GitHub 线程上的最新更新: https: //github.com/moby /buildkit/issues/1472)。

现在有对构建错误进行调试的实验性支持: https: //github.com/docker/buildx/blob/master/docs/guides/debugging.md

对于旧版本,你可以使用这个技巧:

  1. 找出 Dockerfile 中的哪一行失败了。
  2. 添加到 Dockerfile 的顶部:FROM xxx as debug
  3. 添加附加目标:FROM xxx as next仅在失败命令之前添加一行(因为您不想构建该部分)。例子:
FROM xxx as debug
RUN echo "working command"

FROM xxx as next
RUN echoo "failing command"

Run Code Online (Sandbox Code Playgroud)
  • 跑步docker build -f Dockerfile --target debug --tag debug .
  • 然后您可以使用以下命令调试容器:docker run -it debug /bin/sh

您可以通过按 CTRL P + CTRL Q 退出 shell


如果您想使用它,可以通过在 下添加docker -compose.ymldocker compose build来代替。 然后通过以下方式启动容器并使用:docker buildtarget: debugbuild
docker compose run xxxYourServiceNamexxx

  • 第二个热门答案是了解如何在容器内运行 shell。
  • 或者在 Dockerfile 中的ENTRYPOINT /bin/sh该行之前添加。FROM xxx as next


Dom*_*omQ 5

在每行成功之后,Docker都会缓存整个文件系统状态RUN

知道:

  • 检查你的失败之前的最新状态RUN命令,注释掉在Dockerfile(以及任何和所有后续RUN命令),然后运行docker builddocker run试。
  • 要检查失败命令的状态RUN,只需将|| true其添加以强制其成功;然后像上面一样继续(保留所有及所有后续RUN命令被注释掉,运行docker builddocker run

Tada,无需理会Docker内部或层ID,此外,Docker会自动将需要重做的工作量降至最低。

  • 当使用 DOCKER_BUILDKIT 时,这是一个特别有用的答案,因为 buildkit 似乎不支持与上面列出的解决方案相同的解决方案。 (3认同)

Ale*_*lke 5

就我而言,我必须:

\n
DOCKER_BUILDKIT=1 docker build ...\n
Run Code Online (Sandbox Code Playgroud)\n

正如 Jannis Sch\xc3\xb6nleber 在他的回答中提到的,目前在这种情况下没有可用的调试(即没有创建中间图像/容器)。

\n

我发现我可以做的是使用以下选项:

\n
... --progress=plain ...\n
Run Code Online (Sandbox Code Playgroud)\n

然后RUN ...在现有的基础上添加各种或附加行RUN ...来调试特定命令。在我看来,这给了你完全访问的感觉(至少如果你的构建相对较快的话)。

\n

例如,您可以像这样检查变量:

\n
RUN echo "Variable NAME = [$NAME]"\n
Run Code Online (Sandbox Code Playgroud)\n

如果您想知道文件是否安装正确,您可以:

\n
RUN find /\n
Run Code Online (Sandbox Code Playgroud)\n

ETC。

\n

在我的情况下,我必须使用私有存储库调试 Go 应用程序的 docker 版本,并且进行该调试非常困难。我在这里还有其他详细信息。

\n