我正在尝试为我们的开发过程构建一个新的 Docker 映像,使用 cpanm
安装一堆 Perl 模块作为各种项目的基础映像。
在开发 Dockerfile 时,cpanm
返回失败代码,因为某些模块未正确安装。
我很确定我需要让 apt
安装更多东西。
我的问题是,我在哪里可以找到输出中引用的 /.cpanm/work
目录,以便检查日志?在一般情况下,如何检查失败的 docker build
命令的文件系统?
早安编辑在硬着头皮运行 find
之后,我发现
/var/lib/docker/aufs/diff/3afa404e[...]/.cpanm
这是可靠的,还是我最好构建一个“裸”容器并手动运行东西,直到我拥有我需要的所有东西?
/var/lib/docker/aufs/diff/3afa404e[...]/.cpanm
这些是 Docker 的内部结构,我不会弄乱它们
每次 docker 从 Dockerfile 成功执行 RUN
命令时,都会提交 a new layer in the image filesystem。您可以方便地使用这些图层 ID 作为图像来启动新容器。
获取以下 Dockerfile:
FROM busybox
RUN echo 'foo' > /tmp/foo.txt
RUN echo 'bar' >> /tmp/foo.txt
并构建它:
$ docker build -t so-26220957 .
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
您现在可以从 00f017a8c2a6
、044e1532c690
和 5bd8172529c1
启动一个新容器:
$ 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
当然,您可能想启动一个 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
当其中一个 Dockerfile 命令失败时,您需要做的是查找前一层的 id 并在从该 id 创建的容器中运行 shell:
docker run --rm -it <id_last_working_layer> bash -il
一旦进入容器:
尝试失败的命令,并重现问题
然后修复命令并测试它
最后用固定的命令更新你的 Dockerfile
如果您确实需要在实际失败的层中进行试验,而不是从最后一个工作层开始,请参阅 Drew's answer。
如果您想检查失败命令之前的状态,最佳答案是有效的。
但是,问题询问如何检查失败容器本身的状态。在我的情况下,失败的命令是一个需要几个小时的构建,因此在失败的命令之前倒带并再次运行它需要很长时间并且不是很有帮助。
这里的解决方案是找到失败的容器:
$ 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
将其提交到图像:
$ docker commit 6934ada98de6
sha256:7015687976a478e0e94b60fa496d319cdf4ec847bcd612aecf869a72336e6b83
然后运行映像 [如果需要,运行 bash]:
$ docker run -it 7015687976a4 [bash -il]
现在,您实际上是在查看构建失败时的状态,而不是在运行导致失败的命令之前的状态。
DOCKER_BUILDKIT=1
来构建您的 Dockerfile
,这将不起作用
更新 docker 版本 20.10 及以后的版本
Linux 或 macOS
DOCKER_BUILDKIT=0 docker build ...
视窗
# Command line
set DOCKER_BUILDKIT=0 docker build ...
# PowerShell
$env:DOCKER_BUILDKIT=0
使用 DOCKER_BUILDKIT=0 docker build ...
获取旧版本中已知的中间容器哈希。
在较新的版本中,默认情况下会激活 Buildkit。建议仅将其用于调试目的。 Build Kit 可以让您的构建速度更快。
供参考:Buildkit 不支持中间容器哈希:https://github.com/moby/buildkit/issues/1053
感谢@David Callanan 和@MegaCookie 的投入。
set DOCKER_BUILDKIT=0
,然后运行 docker build ...
命令。
$env:DOCKER_BUILDKIT=0
DOCKER_BUILDKIT=plain
。
每个成功的 RUN
行之后的 Docker caches the entire filesystem state。
知道:
要检查失败的 RUN 命令之前的最新状态,请在 Dockerfile 中将其注释掉(以及任何和所有后续 RUN 命令),然后再次运行 docker build 和 docker run。
要检查失败的 RUN 命令后的状态,只需添加 ||忠于它以迫使它成功;然后像上面一样继续(保留所有后续 RUN 命令注释掉,运行 docker build 和 docker run)
Tada,不需要弄乱 Docker 内部结构或层 ID,作为奖励,Docker 会自动最小化需要重新完成的工作量。
调试构建步骤失败确实很烦人。
我发现的最佳解决方案是确保执行实际工作的每个步骤都成功,并在失败的步骤之后添加检查。这样,您将获得一个已提交的层,其中包含您可以检查的失败步骤的输出。
一个 Dockerfile,在 # Run DB2 silent installer
行之后有一个示例:
#
# DB2 10.5 Client Dockerfile (Part 1)
#
# Requires
# - DB2 10.5 Client for 64bit Linux ibm_data_server_runtime_client_linuxx64_v10.5.tar.gz
# - Response file for DB2 10.5 Client for 64bit Linux db2rtcl_nr.rsp
#
#
# Using Ubuntu 14.04 base image as the starting point.
FROM ubuntu:14.04
MAINTAINER David Carew <carew@us.ibm.com>
# DB2 prereqs (also installing sharutils package as we use the utility uuencode to generate password - all others are required for the DB2 Client)
RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y sharutils binutils libstdc++6:i386 libpam0g:i386 && ln -s /lib/i386-linux-gnu/libpam.so.0 /lib/libpam.so.0
RUN apt-get install -y libxml2
# Create user db2clnt
# Generate strong random password and allow sudo to root w/o password
#
RUN \
adduser --quiet --disabled-password -shell /bin/bash -home /home/db2clnt --gecos "DB2 Client" db2clnt && \
echo db2clnt:`dd if=/dev/urandom bs=16 count=1 2>/dev/null | uuencode -| head -n 2 | grep -v begin | cut -b 2-10` | chgpasswd && \
adduser db2clnt sudo && \
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
# Install DB2
RUN mkdir /install
# Copy DB2 tarball - ADD command will expand it automatically
ADD v10.5fp9_linuxx64_rtcl.tar.gz /install/
# Copy response file
COPY db2rtcl_nr.rsp /install/
# Run DB2 silent installer
RUN mkdir /logs
RUN (/install/rtcl/db2setup -t /logs/trace -l /logs/log -u /install/db2rtcl_nr.rsp && touch /install/done) || /bin/true
RUN test -f /install/done || (echo ERROR-------; echo install failed, see files in container /logs directory of the last container layer; echo run docker run '<last image id>' /bin/cat /logs/trace; echo ----------)
RUN test -f /install/done
# Clean up unwanted files
RUN rm -fr /install/rtcl
# Login as db2clnt user
CMD su - db2clnt
目前使用最新的 docker-desktop,没有办法选择退出新的 Buildkit,它还不支持调试(请关注此 GitHub 线程上的最新更新:https://github.com/moby/buildkit/issues/1472)。
首先让 docker 尝试构建,并找出 Dockerfile 中的哪一行失败。
接下来,在您的 Dockerfile 中,您可以在顶部添加一个构建目标: FROM xxx as debug
然后,在您的 Dockerfile 中添加一个额外的目标 FROM xxx 作为失败命令之前的下一行(因为您不想构建该部分)。例子:
FROM xxx as debug
# Working command
RUN echo "working command"
FROM xxx as next
# Example of failing command
RUN echoo "failing command"
然后运行 docker build -f Dockerfile --target debug --tag debug 。
接下来你可以运行 docker run -it debug /bin/sh
您可以通过按 CTRL P + CTRL Q 退出 shell
如果您想使用 docker compose build
而不是 docker build
,可以通过在您的 docker-compose.yml under build
中添加 target: debug
。
然后通过 docker compose run xxxYourServiceNamexxx
启动容器并使用:
了解如何在容器内运行 shell 的第二个最佳答案。
或者在 FROM xxx 之前添加 ENTRYPOINT /bin/sh 作为 Dockerfile 中的下一行。
就我而言,我必须:
DOCKER_BUILDKIT=1 docker build ...
正如 Jannis Schönleber 在他的回答中提到的那样,目前在这种情况下没有可用的调试(即没有创建中间图像/容器)。
我发现我可以做的是使用以下选项:
... --progress=plain ...
然后在现有 RUN ...
上添加各种 RUN ...
或附加行以调试特定命令。这给了你我感觉完全访问的感觉(至少如果你的构建速度相对较快)。
例如,您可以像这样检查一个变量:
RUN echo "Variable NAME = [$NAME]"
如果您想知道文件是否安装正确,请执行以下操作:
RUN find /
等等
在我的情况下,我不得不调试带有私有存储库的 Go 应用程序的 docker 构建,并且很难进行调试。我有关于该 here 的其他详细信息。
docker-compose build --progress=plain servicename
!
我要做的是注释掉下面的 Dockerfile 并包括有问题的行。然后就可以运行容器,手动运行docker命令,照常查看日志。例如,如果 Dockerfile 是
RUN foo
RUN bar
RUN baz
我会在酒吧里死去
RUN foo
# RUN bar
# RUN baz
然后
$ docker build -t foo .
$ docker run -it foo bash
container# bar
...grep logs...
我的解决方案是查看 docker 文件中的哪个步骤失败,在我的情况下为 RUN bundle install
,
并将其更改为
RUN bundle install || cat <path to the file containing the error>
这具有打印出失败原因的双重效果,并且这个中间步骤并没有被 docker build 认为是失败的。所以它不会被删除,并且可以通过以下方式进行检查:
docker run --rm -it <id_last_working_layer> bash -il
在那里,您甚至可以重新运行失败的命令并对其进行实时测试。
仍然使用 BuildKit,如 Alexis Wilke 的 answer,您可以使用 ktock/buildg
。
请参阅 Kohei Tokunaga 中的“Interactive debugger for Dockerfile”
buildg 是一个基于 BuildKit 交互式调试 Dockerfile 的工具。源级检查 断点和步骤执行 使用您自己的调试工具在步骤上的交互式 shell 基于 BuildKit(需要未合并的补丁) 支持无根
例子:
$ buildg.sh debug --image=ubuntu:22.04 /tmp/ctx
WARN[2022-05-09T01:40:21Z] using host network as the default
#1 [internal] load .dockerignore
#1 transferring context: 2B done
#1 DONE 0.1s
#2 [internal] load build definition from Dockerfile
#2 transferring dockerfile: 195B done
#2 DONE 0.1s
#3 [internal] load metadata for docker.io/library/busybox:latest
#3 DONE 3.0s
#4 [build1 1/2] FROM docker.io/library/busybox@sha256:d2b53584f580310186df7a2055ce3ff83cc0df6caacf1e3489bff8cf5d0af5d8
#4 resolve docker.io/library/busybox@sha256:d2b53584f580310186df7a2055ce3ff83cc0df6caacf1e3489bff8cf5d0af5d8 0.0s done
#4 sha256:50e8d59317eb665383b2ef4d9434aeaa394dcd6f54b96bb7810fdde583e9c2d1 772.81kB / 772.81kB 0.2s done
Filename: "Dockerfile"
2| RUN echo hello > /hello
3|
4| FROM busybox AS build2
=> 5| RUN echo hi > /hi
6|
7| FROM scratch
8| COPY --from=build1 /hello /
>>> break 2
>>> breakpoints
[0]: line 2
>>> continue
#4 extracting sha256:50e8d59317eb665383b2ef4d9434aeaa394dcd6f54b96bb7810fdde583e9c2d1 0.0s done
#4 DONE 0.3s
...
docker run --rm -it <id_last_working_layer> bash -il
并在容器中尝试无法重现的命令问题,然后修复命令并对其进行测试,最后使用固定命令更新您的 Dockerfile。Unable to find image 'd5219f1ffda9:latest' locally
。但是,我对多种 ID 感到困惑。事实证明,您必须使用直接在箭头之后的 ID,而不是那些显示“正在运行...”的 ID。docker build
时,它不会给我每一层的哈希 ID。我没有看到任何命令选项来启用它。DOCKER_BUILDKIT=0 docker build ......