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

时间:2014-10-06 16:50:20

标签: debugging docker cpanm

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

在开发Dockerfile时,cpanm会返回失败代码,因为某些模块没有完全安装。

我非常确定我需要让apt安装更多东西。

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

早上编辑咬完子弹并运行find我发现了

/var/lib/docker/aufs/diff/3afa404e[...]/.cpanm

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

6 个答案:

答案 0 :(得分:297)

每次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-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

您现在可以从00f017a8c2a6044e1532c6905bd8172529c1开始新容器:

$ 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

答案 1 :(得分:158)

如果你想在失败的命令之前检查状态,那么最常见的答案是有用的。

然而,问题是如何检查失败的容器本身的状态。在我的情况下,失败的命令是一个需要几个小时的构建,因此在失败的命令之前倒退并再次运行它需要很长时间并且不是很有帮助。

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

$ 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]

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

答案 2 :(得分:31)

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

使用 DOCKER_BUILDKIT=0 docker build ... 获取旧版本中已知的中间容器哈希。

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

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

答案 3 :(得分:4)

每次成功RUN行后

Docker caches the entire filesystem state

知道:

  • 检查失败的RUN命令之前的最新状态,在Dockerfile中注释掉它(以及任何和所有后续RUN命令),然后运行docker build和{{ 1}}再次。
  • 在失败的docker run命令之后检查状态,只需向其添加RUN以强制它成功;然后像上面一样继续(保留所有后续|| true个命令,运行RUNdocker build

Tada,无需弄乱Docker内部或图层ID,作为奖励Docker会自动减少需要重新完成的工作量。

答案 4 :(得分:2)

调试构建步骤失败确实非常烦人。

我找到的最佳解决方案是确保执行实际工作的每个步骤都成功,并在失败的步骤之后添加检查。这样,您将获得一个包含您可以检查的失败步骤的输出的已提交层。

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

答案 5 :(得分:1)

我要做的是注释下面的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...