Get container ID from Docker buildkit for interactive debugging

It's commonly known that you can run docker commit against a failed build process to take a snapshot of a container for debugging purposes. The container ID is gleaned from the running in <ID> text. However, this text is not emitted during builds that happen with Docker's newer BuildKit buildx functionality.

I tried using --progress plain on the Docker build command, but that hasn't shown me the container IDs. Plus, I cannot run a new container from the image layer IDs (SHA hashes) that are spit out.

Sample BuildKit Output

Using this command:

#1 [internal] load build definition from Dockerfile
#1 sha256:0e70418d547c3ccb20da7b100cf4f69564bddc416652e3e2b9b514e9a732b4aa
#1 transferring dockerfile: 32B done
#1 DONE 0.0s

#2 [internal] load .dockerignore
#2 sha256:396b2cfd81ff476a70ecda27bc5d781bd61c859b608537336f8092e155dd38bf
#2 transferring context: 34B done
#2 DONE 0.0s

#3 [internal] load metadata for docker.io/library/node:latest
#3 sha256:1c0b05b884068c98f7acad32e4f7fd374eba1122b4adcbb1de68aa72d5a6046f
#3 DONE 0.0s

#4 [1/4] FROM docker.io/library/node
#4 sha256:5045d46e15358f34ea7fff145af304a1fa3a317561e9c609f4ae17c0bd3359df
#4 DONE 0.0s

#5 [internal] load build context
#5 sha256:49d7a085caed3f75e779f05887e53e0bba96452e3a719963993002a3638cb8a3
#5 transferring context: 35.17kB 0.0s done
#5 DONE 0.1s

#6 [2/4] ADD [trevortest/*, /app/]
#6 sha256:6da32965a50f6e13322efb20007ff49fb0546e2ff55799163b3b00d034a62c57
#6 CACHED

Question: How can I obtain the container IDs of the build process, during each step, specifically when using Docker BuildKit?


Solution 1:

The BuildKit works differently than the legacy docker build system. At the moment, there is no direct way to spawn a container from a step in the build and troubleshoot it.

To use the BuildKit potential up to the maximum, best approach is to organize the builds in smaller logical stages. Once the build is organized in this way, When running the builds, you can specify that you want to stop at a certain stage by using --target. When the target is specified, Docker creates an image with the results of the build up to that stage. You can use this container to further troubleshoot in the same way that was possible with the old build system.

Take this example. Here I have 4 stages out of which 2 are parallel stages:

FROM debian:9.11 AS stage-01
# Prepare for installation
RUN apt update && \
    apt upgrade -y

FROM stage-01 as stage-02
# Install building tools
RUN apt install -y build-essential

FROM stage-02 as stage-02a

RUN echo "Build 0.1" > /version.txt

FROM stage-02 as stage-03

RUN apt install -y cmake gcc g++

Now you can use the --target option to tell Docker that you want to stop at the stage-02 as follows:

$ docker build -f test-docker.Dockerfile -t test . --target stage-02                                                                                                                                   [+] Building 67.5s (7/7) FINISHED
 => [internal] load build definition from test-docker.Dockerfile                                 0.0s
 => => transferring dockerfile: 348B                                                             0.0s
 => [internal] load .dockerignore                                                                0.0s
 => => transferring context: 2B                                                                  0.0s
 => [internal] load metadata for docker.io/library/debian:9.11                                   0.0s
 => [stage-01 1/2] FROM docker.io/library/debian:9.11                                            0.0s
 => CACHED [stage-01 2/2] RUN apt update &&     apt upgrade -y                                   0.0s
 => [stage-02 1/1] RUN apt install -y build-essential                                           64.7s
 => exporting to image                                                                           2.6s
 => => exporting layers                                                                          2.5s
 => => writing image sha256:ac36b95184b79b6cabeda3e4d7913768f6ed73527b76f025262d6e3b68c2a357     0.0s
 => => naming to docker.io/library/test                                                          0.0s

Now you have the image with the name test and you can spawn a container to troubleshoot.

docker run -ti --rm --name troubleshoot test /bin/bash
root@bbdb0d2188c0:/# ls

Using multiple stages facilitates the troubleshooting, however it really speeds up the build process since the parallel branches can be build on different instances. Also, the readability of the build file is significantly improved.