如何在Dockerfile中设置断点?

q3qa4bjr  于 2023-05-28  发布在  Docker
关注(0)|答案(5)|浏览(245)

搜索上面的内容可以看到很多关于如何为在docker容器中运行的应用设置断点的结果,但我对在Dockerfile * 本身 * 中设置断点感兴趣,这样docker build就在断点处暂停。例如Dockerfile

FROM ubuntu:20.04

RUN echo "hello"
RUN echo "bye"

我正在寻找一种在RUN echo "bye"上设置断点的方法,这样当我调试这个Dockerfile时,映像将以非交互方式构建到RUN echo "bye"点,独占。之后,我将能够与容器交互地运行命令。在我拥有的实际Dockerfile中,在断点之前有RUN,它们会更改正在构建的映像的文件系统,我希望能够在断点处交互式地运行cd/ls/find等命令,从而分析断点处映像的文件系统。

rn0zuynd

rn0zuynd1#

您不能设置断点 * 本身 *,但是您可以在构建序列中的任意点(步骤之间)获得交互式shell。
让我们建立你的形象:

Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM ubuntu:20.04
 ---> 1e4467b07108
Step 2/3 : RUN echo "hello"
 ---> Running in 917b34190e35
hello
Removing intermediate container 917b34190e35
 ---> 12ebbdc1e72d
Step 3/3 : RUN echo "bye"
 ---> Running in c2a4a71ae444
bye
Removing intermediate container c2a4a71ae444
 ---> 3c52993b0185
Successfully built 3c52993b0185

每一行写有十六进制ID的---> 0123456789ab都有一个有效的图像ID。所以从这里你可以

docker run --rm -it 12ebbdc1e72d sh

这将在第一个RUN命令生成的部分图像上给予一个交互式shell。
不要求构建作为一个整体成功。如果RUN步骤失败,您可以使用此技术在该步骤之前立即在映像上获取交互式shell,然后手动重新运行命令。如果您有一个很长的RUN命令,您可能需要将它分成两个,以便能够在命令序列中的特定点获得调试shell。

vatpfxk5

vatpfxk52#

我不认为这是可能的直接-该功能已被discussed和拒绝。
我通常在调试Dockerfile时会注解“断点”之后的所有步骤,然后运行docker build,然后运行docker run -it image bashdocker run -it image sh(取决于容器中是否安装了bash)。
然后,我有一个交互式shell,我可以运行命令来调试为什么后面的阶段失败。
不过,我同意能够设置断点并四处查看将是一个方便的功能。

fd3cxomn

fd3cxomn3#

您可以使用Remote shell debugging技巧在中间容器中运行命令。
确保你的容器镜像包含基本的实用程序,如netcat(nc)和fuser。这些实用程序支持从任何中间容器映像“调用home”。在家里,你可以用netcat(或socat)接听电话。这个netcat会将你的命令发送到容器,并打印它们的结果。这种调试方法甚至可以在云中某个未知工作节点上构建的Dockerfile上工作。
示例:

FROM debian:testing-slim

# Set environment variables for calling home from breakpoints (BP)
ENV BP_HOME=<IP-ADDRESS-OF-YOUR-HOST>
ENV BP_PORT=33720
ENV BP_CALLHOME='BP_FIFO=/tmp/$BP.$BP_HOME.$BP_PORT; (rm -f $BP_FIFO; mkfifo $BP_FIFO) && (echo "\"c\" continues"; echo -n "($BP) "; tail -f $BP_FIFO) | nc $BP_HOME $BP_PORT | while read cmd; do if test "$cmd" = "c" ; then echo -n "" >$BP_FIFO; sleep 0.1; fuser -k $BP_FIFO >/dev/null 2>&1; break; else eval $cmd >$BP_FIFO 2>&1; echo -n "($BP) "  >$BP_FIFO; fi; done'

# Install needed utils (netcat, fuser)
RUN apt update && apt install -y netcat psmisc

# Now you are ready to run "eval $BP_CALLHOME" wherever you want to call home.

RUN BP=before-hello eval $BP_CALLHOME

RUN echo "hello"

RUN BP=after-hello eval $BP_CALLHOME

RUN echo "bye"

在启动Docker构建之前,开始等待和应答来自Dockerfile * 的调用。在主主机上运行nc -k -l -p 33720(或者socat STDIN TCP-LISTEN:33720,reuseaddr,fork)。
这就是上面的例子在家里的样子:

$ nc -k -l -p 33720
"c" continues
(before-hello) echo *
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
(before-hello) id
uid=0(root) gid=0(root) groups=0(root)
(before-hello) c
"c" continues
(after-hello)
...
mklgxw1f

mklgxw1f4#

最近(2022年5月)的项目ktock/buildg提供了断点。
参见Kohei Tokunaga中的“Interactive debugger for Dockerfile
buildg是一个基于BuildKit交互调试Dockerfile的工具。

  • 源级检查
  • 断点和步骤执行
  • 使用您自己的调试工具在一个步骤上进行交互式shell
  • 基于BuildKit(需要未合并的修补程序)
  • 支持无根

命令breakb LINE_NUMBER设置断点。
示例:

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

PR 24
添加--cache-reuse选项,允许在buildg debug调用之间共享构建缓存,以使第二次调试更快。
这对于加快运行buildg多次调试出错步骤非常有用。
请注意,缓存步骤上的断点从现在起被忽略
由于此限制,此功能目前是可选的。我们应该修复这个限制,并使其成为未来的默认行为。

9gm1akwq

9gm1akwq5#

Docker让事情变得“困难”。以下是我想出的一个解决方案:
1.在需要断点的位置插入FROM scratch

  • 运行docker build . --target=<n-1>,其中<n>是“断点”之前的FROM命令数。例如,如果是单阶段构建,则使用--target=0
  • 或者,如果您已经使用FROM <image> AS <stage>命名了想要断点的阶段,则可以使用--target=<stage>

Docker已经缓存了所有成功的层(即使你看不到它们),并且因为FROM“断点”位于(可能不成功的)兴趣点之前,所以构建应该都来自缓存并且非常快。
例如,如果我的Dockerfile看起来像这样:

FROM debian:bullseye AS build

RUN apt-get update && apt-get install -y \
    build-essential cmake ninja-build \
    libfontconfig1-dev libdbus-1-dev libfreetype6-dev libicu-dev libinput-dev libxkbcommon-dev libsqlite3-dev libssl-dev libpng-dev libjpeg-dev libglib2.0-dev

<SNIP lots of other setup commands>

ADD my_source.tar.xz /
WORKDIR /my_source

RUN ./configure -option1 -option2
RUN cmake --build . --parallel
RUN cmake --install .

FROM alpine
COPY --from=build /my_build /my_build

...

然后我可以像这样添加一个“断点”:

FROM debian:bullseye AS build

RUN apt-get update && apt-get install -y \
    build-essential cmake ninja-build \
    libfontconfig1-dev libdbus-1-dev libfreetype6-dev libicu-dev libinput-dev libxkbcommon-dev libsqlite3-dev libssl-dev libpng-dev libjpeg-dev libglib2.0-dev

<SNIP lots of other setup commands>

ADD my_source.tar.xz /
WORKDIR /my_source

#### BREAKPOINT ###
FROM scratch
#### BREAKPOINT ###

RUN ./configure -option1 -option2
RUN cmake --build . --parallel
RUN cmake --install .

FROM alpine
COPY --from=build /my_build /my_build

...

然后用docker build . --target=build触发

相关问题