Browse code

Added Delve debugger to the development container (including instructions in the contribution guide).

Signed-off-by: Konrad Ponichtera <konpon96@gmail.com>

Konrad Ponichtera authored on 2022/02/21 03:21:10
Showing 10 changed files
... ...
@@ -149,6 +149,18 @@ RUN --mount=type=cache,sharing=locked,id=moby-cross-true-aptlib,target=/var/lib/
149 149
 
150 150
 FROM runtime-dev-cross-${CROSS} AS runtime-dev
151 151
 
152
+FROM base AS delve
153
+# DELVE_VERSION specifies the version of the Delve debugger binary
154
+# from the https://github.com/go-delve/delve repository.
155
+# It can be used to run Docker with a possibility of
156
+# attaching debugger to it.
157
+#
158
+ARG DELVE_VERSION=v1.8.1
159
+RUN --mount=type=cache,target=/root/.cache/go-build \
160
+    --mount=type=cache,target=/go/pkg/mod \
161
+        GOBIN=/build/ GO111MODULE=on go install "github.com/go-delve/delve/cmd/dlv@${DELVE_VERSION}" \
162
+     && /build/dlv --help
163
+
152 164
 FROM base AS tomll
153 165
 # GOTOML_VERSION specifies the version of the tomll binary to build and install
154 166
 # from the https://github.com/pelletier/go-toml repository. This binary is used
... ...
@@ -301,6 +313,7 @@ RUN pip3 install yamllint==1.26.1
301 301
 COPY --from=dockercli     /build/ /usr/local/cli
302 302
 COPY --from=frozen-images /build/ /docker-frozen-images
303 303
 COPY --from=swagger       /build/ /usr/local/bin/
304
+COPY --from=delve         /build/ /usr/local/bin/
304 305
 COPY --from=tomll         /build/ /usr/local/bin/
305 306
 COPY --from=tini          /build/ /usr/local/bin/
306 307
 COPY --from=registry      /build/ /usr/local/bin/
... ...
@@ -68,6 +68,7 @@ DOCKER_ENVS := \
68 68
 	-e DOCKER_TEST_HOST \
69 69
 	-e DOCKER_USERLANDPROXY \
70 70
 	-e DOCKERD_ARGS \
71
+	-e DELVE_PORT \
71 72
 	-e TEST_FORCE_VALIDATE \
72 73
 	-e TEST_INTEGRATION_DIR \
73 74
 	-e TEST_SKIP_INTEGRATION \
... ...
@@ -114,8 +115,9 @@ DOCKER_CONTAINER_NAME := $(if $(CONTAINER_NAME),--name $(CONTAINER_NAME),)
114 114
 
115 115
 DOCKER_IMAGE := docker-dev
116 116
 DOCKER_PORT_FORWARD := $(if $(DOCKER_PORT),-p "$(DOCKER_PORT)",)
117
+DELVE_PORT_FORWARD := $(if $(DELVE_PORT),-p "$(DELVE_PORT)",)
117 118
 
118
-DOCKER_FLAGS := $(DOCKER) run --rm -i --privileged $(DOCKER_CONTAINER_NAME) $(DOCKER_ENVS) $(DOCKER_MOUNT) $(DOCKER_PORT_FORWARD)
119
+DOCKER_FLAGS := $(DOCKER) run --rm -i --privileged $(DOCKER_CONTAINER_NAME) $(DOCKER_ENVS) $(DOCKER_MOUNT) $(DOCKER_PORT_FORWARD) $(DELVE_PORT_FORWARD)
119 120
 BUILD_APT_MIRROR := $(if $(DOCKER_BUILD_APT_MIRROR),--build-arg APT_MIRROR=$(DOCKER_BUILD_APT_MIRROR))
120 121
 export BUILD_APT_MIRROR
121 122
 
... ...
@@ -77,6 +77,8 @@ if [ -z "$mtu" ]; then
77 77
 	mtu=1500
78 78
 fi
79 79
 
80
+dockerd="${DOCKERD:-dockerd}"
81
+
80 82
 if [ -z "$_DOCKERD_ROOTLESS_CHILD" ]; then
81 83
 	_DOCKERD_ROOTLESS_CHILD=1
82 84
 	export _DOCKERD_ROOTLESS_CHILD
... ...
@@ -128,5 +130,7 @@ else
128 128
 		mount --rbind ${realpath_etc_ssl} /etc/ssl
129 129
 	fi
130 130
 
131
-	exec dockerd $@
131
+	# shellcheck disable=SC2068
132
+	# shellcheck disable=SC2086
133
+	exec $dockerd $@
132 134
 fi
... ...
@@ -6,3 +6,4 @@
6 6
  * [Configure Git for contributing](set-up-git.md)
7 7
  * [Work with a development container](set-up-dev-env.md)
8 8
  * [Run tests and test documentation](test.md)
9
+ * [Debugging the daemon](debug.md)
9 10
new file mode 100644
... ...
@@ -0,0 +1,66 @@
0
+### Debugging the daemon
1
+
2
+The Docker daemon inside the development container can be debugged with [Delve](https://github.com/go-delve/delve).
3
+
4
+Delve debugger listens on a port, which has to be exposed outside the development container.
5
+Also, in order to be able to debug the daemon, it has to be compiled with the debugging symbols.
6
+This can be done by launching the development container with the following command:
7
+
8
+```bash
9
+$ make BIND_DIR=. DOCKER_DEBUG=1 DELVE_PORT=127.0.0.1:2345:2345 shell
10
+```
11
+
12
+The `DOCKER_DEBUG` variable disables build optimizations, allowing to debug the binary,
13
+while `DELVE_PORT` publishes the specified port for use with the debugger.
14
+
15
+The `DELVE_PORT` variable accepts the port in the same format as Docker CLI's `--publish` (`-p`) option.
16
+This means that the port can be published in multiple ways:
17
+
18
+1. `DELVE_PORT=127.0.0.1:2345:2345` - exposes debugger on port `2345` for local development only (recommended)
19
+2. `DELVE_PORT=2345:2345` - exposes debugger on port `2345` without binding to specific IP
20
+3. `DELVE_PORT=2345` - same as above
21
+
22
+**IMPORTANT:** Publishing the port without binding it to localhost (127.0.0.1) might expose the debugger
23
+outside the developer's machine and is not recommended.
24
+
25
+## Running Docker daemon with debugger attached
26
+
27
+1. Run development container with build optimizations disabled and Delve enabled:
28
+   ```bash
29
+   $ make BIND_DIR=. DOCKER_DEBUG=1 DELVE_PORT=127.0.0.1:2345:2345 shell
30
+   ```
31
+2. Inside the development container:
32
+   1. Build the Docker daemon:
33
+      ```bash
34
+      $ ./hack/make.sh binary
35
+      ```
36
+   2. Install the newly-built daemon:
37
+      ```bash
38
+      $ make install
39
+      ```
40
+   3. Run the daemon through the `make.sh` script:
41
+      ```bash
42
+      $ ./hack/make.sh run
43
+      ```
44
+      The execution will stop and wait for the IDE or Delve CLI to attach
45
+      to the port, specified with the `DELVE_PORT` variable.
46
+      Once the IDE or Delve CLI is attached, the execution will continue.
47
+   
48
+## Debugging from IDE (on example of GoLand 2021.3)
49
+
50
+1. Open the project in GoLand
51
+2. Click *Add Configuration* on the taskbar
52
+   ![GoLand - adding configuration](images/goland_add_config.png)
53
+3. Create the *Go Remote* configuration. 
54
+   No changes are necessary, unless a different port is to be used.
55
+   ![GoLand - adding remote configuration](images/goland_debug_config.png)
56
+4. Run the Docker binary in the development container, as described in the previous section.
57
+   Make sure that the port in the `DELVE_PORT` variable corresponds to one, used in the *Go Remote* configuration.
58
+5. Run the *Go Remote* configuration.
59
+   The Docker daemon will continue execution inside the container and debugger will stop it on the breakpoints.
60
+   ![GoLand - run Go Remote configuration](images/goland_run_debug_config.png)
61
+
62
+## Where to go next
63
+
64
+Congratulations, you have experienced how to use Delve to debug the Docker daemon
65
+and how to configure an IDE to make use of it.
0 66
\ No newline at end of file
1 67
new file mode 100644
2 68
Binary files /dev/null and b/docs/contributing/images/goland_add_config.png differ
3 69
new file mode 100644
4 70
Binary files /dev/null and b/docs/contributing/images/goland_debug_config.png differ
5 71
new file mode 100644
6 72
Binary files /dev/null and b/docs/contributing/images/goland_run_debug_config.png differ
... ...
@@ -250,3 +250,4 @@ jobs can be triggered and re-ran by the Moby maintainers
250 250
 
251 251
 Congratulations, you have successfully completed the basics you need to
252 252
 understand the Moby test framework.
253
+In the next section you'll [learn how to debug Docker daemon, running inside the development container](debug.md).
... ...
@@ -27,6 +27,11 @@ if [ -n "$DOCKER_PORT" ]; then
27 27
 	listen_port="${ports[-1]}"
28 28
 fi
29 29
 
30
+if [ -n "$DELVE_PORT" ]; then
31
+	IFS=':' read -r -a ports <<< "$DELVE_PORT"
32
+	delve_listen_port="${ports[-1]}"
33
+fi
34
+
30 35
 extra_params="$DOCKERD_ARGS"
31 36
 if [ "$DOCKER_REMAP_ROOT" ]; then
32 37
 	extra_params="$extra_params --userns-remap $DOCKER_REMAP_ROOT"
... ...
@@ -36,7 +41,7 @@ if [ -n "$DOCKER_EXPERIMENTAL" ]; then
36 36
 	extra_params="$extra_params --experimental"
37 37
 fi
38 38
 
39
-dockerd="dockerd"
39
+dockerd="$(command -v dockerd)"
40 40
 socket=/var/run/docker.sock
41 41
 if [ -n "$DOCKER_ROOTLESS" ]; then
42 42
 	user="unprivilegeduser"
... ...
@@ -44,7 +49,6 @@ if [ -n "$DOCKER_ROOTLESS" ]; then
44 44
 	# shellcheck disable=SC2174
45 45
 	mkdir -p -m 700 "/tmp/docker-${uid}"
46 46
 	chown $user "/tmp/docker-${uid}"
47
-	dockerd="sudo -u $user -E XDG_RUNTIME_DIR=/tmp/docker-${uid} -E HOME=/home/${user} -- dockerd-rootless.sh"
48 47
 	socket=/tmp/docker-${uid}/docker.sock
49 48
 fi
50 49
 
... ...
@@ -55,6 +59,29 @@ args="--debug \
55 55
 	$storage_params \
56 56
 	$extra_params"
57 57
 
58
+if [ -n "$DELVE_PORT" ]; then
59
+	dockerd="dlv --listen=0.0.0.0:$delve_listen_port \
60
+		--headless=true \
61
+		--log \
62
+		--api-version=2 \
63
+		--only-same-user=false \
64
+		--check-go-version=false \
65
+		--accept-multiclient \
66
+		exec ${dockerd} --"
67
+fi
68
+
58 69
 echo "${dockerd} ${args}"
59
-# shellcheck disable=SC2086
60
-exec "${dockerd}" ${args}
70
+
71
+if [ -n "$DOCKER_ROOTLESS" ]; then
72
+	# shellcheck disable=SC2068
73
+	# shellcheck disable=SC2086
74
+	exec sudo -u $user \
75
+		-E DOCKERD="$dockerd" \
76
+		-E XDG_RUNTIME_DIR=/tmp/docker-${uid} \
77
+		-E XDG_CONFIG_HOME=/home/${user}/.config \
78
+		-E HOME=/home/${user} \
79
+		-- /go/src/github.com/docker/docker/contrib/dockerd-rootless.sh ${args}
80
+else
81
+	# shellcheck disable=SC2086
82
+	exec ${dockerd} ${args}
83
+fi