How to debug image build with Dockerfile
Published: 2023-06-15 22:04:57

# How to debug image build with Dockerfile

## Dockerfile to build the env for Ruby on Rails App
```Dockerfile
FROM ruby
# install essential libs/commands
RUN apt-get update && apt-get install lib-ssl
...
# install gems
RUN bundle install
...
# install npm
RUN yarn install
COMMAND bundle exec rails server
```

1. delete the following part causing errors

```
FROM busybox
RUN echo 'hello world' > /tmp/test

RUN exit 1
....
RUN echo 'ready'
```
~~RUN exit 1~~
Just remove `RUN exit 1` and the following

2. run intermediate image with sha
Turn off buildkit to get the sha for each layer
`docker run --rm -it current sh` to debug
```bash
❯ DOCKER_BUILDKIT=0 docker build -t test .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM busybox
 ---> abaa813f94fd
Step 2/3 : RUN echo 'hello world'
 ---> Using cache
 ---> 551ba8324834
Step 3/3 : RUN exit 1
 ---> Running in 5e3c523c38a3
The command '/bin/sh -c exit 1' returned a non-zero code: 1
```

```bash
# pick sha of last successful layer
docker run --rm -it 551ba8324834 sh
```

3. `nsenter` to debug

Enter into the [name space](https://www.nginx.com/blog/what-are-namespaces-cgroups-how-do-they-work) of the process.
Namespaces are a feature of the Linux kernel that partitions kernel resources such that one set of processes sees one set of resources while another set of processes sees a different set of resources.

  * add `RUN sleep infinite` to Dockerfile
  * `docker run -it --rm --privileged --pid=host justincormack/nsenter1` to get to the building host [^1]
  * `ps -ef|grep sleep` to find pid
  * `nsenter -p -m -u -i -n -t 10012 sh`
  
```Dockerfile
FROM busybox
RUN echo 'hello world'
RUN sleep infinite
RUN exit 1
```

```bash
❯ docker build -t test  .
[+] Building 10.7s (5/7)
 => [internal] load build definition from Dockerfile                                                                                             0.0s
 => => transferring dockerfile: 73B                                                                                                              0.0s
 => [internal] load .dockerignore                                                                                                                0.0s
 => => transferring context: 2B                                                                                                                  0.0s
 => [internal] load metadata for docker.io/library/busybox:latest                                                                                0.0s
 => [1/4] FROM docker.io/library/busybox                                                                                                         0.0s
 => CACHED [2/4] RUN echo 'hello world'                                                                                                          0.0s
 => [3/4] RUN sleep infinite
 ```


4. use `target` to build sucessfully the image
[multiple-stage builds](https://docs.docker.com/build/building/multi-stage/)

```
FROM busybox as working
RUN echo 'hello world'

FROM working as error
RUN exit 1
```

```bash
# build image with target
docker build -t test --target working .

# run image to debug
docker run --rm -it test sh
```










---
[^1]: althernative way using alpine and nsenter command

```bash
docker run --privileged --pid=host -it alpine \
nsenter -t 1 -m -u -n -i sh
```

* --rm : removes the container after it is stopped
-ti (or -t -i) : adds a tty and leaves the standard input opened
* --privileged : grants additional permissions to the container, it allows the container to gain access to the devices of the host (/dev)
* --pid=host : allows the containers to use the processus tree of the Docker host (the VM in which the Docker daemon is running)


### check pid 
```bash
❯ docker run -ti --rm busybox sh
❯ docker ps
❯ docker inspect --format '{{.State.Pid}}' a57c56a83e54
18762
```