Added Delve debugger to the development container
<!--
Please make sure you've read and understood our contributing guidelines;
https://github.com/moby/moby/blob/master/CONTRIBUTING.md

** Make sure all your commits include a signature generated with `git commit -s` **

For additional information on our contributing process, read our contributing
guide https://docs.docker.com/opensource/code/

If this is a bug fix, make sure your description includes "fixes #xxxx", or
"closes #xxxx"

Please provide the following information:
-->

Closes #43274

**- What I did**

I've added a way to debug containerized Docker daemon with the use of [Delve](https://github.com/go-delve/delve) debugger, which integrates nicely with various IDEs like VS Code or GoLand.

The implementation does not affect the built binary in any way. What's more, the use of Delve is entirely optional - it is not used unless `DELVE_PORT` environment variable is set.

**- How I did it**

The Delve debugger is used when the `DELVE_PORT` environment variable is set. In such case the development container publishes that port and the scripts that normally execute `dockerd` run it through `dlv` instead. Then it's possible for the IDE running outside the container to attach to that port and debug Docker daemon.

1. Added the Delve binary to the Moby's development container
2. Modified Makefile to optionally publish the port, specified with `DELVE_PORT` environment variable
3. Modified the scripts that normally execute `dockerd` to execute it with Delve when the `DELVE_PORT` variable is set.

**- How to verify it**

1. Run the development container with `make BIND_DIR=. DOCKER_DEBUG=1 DELVE_PORT=2345 shell`. 
    * It's important to specify the port with the `DELVE_PORT` variable as well as to provide `DOCKER_DEBUG`, so that the binary is built with the debugging symbols.
2. Build the Docker daemon: `./hack/make.sh run`
3. Install the built daemon: `make install`
4. Run the daemon: `./hack/make.sh run`. The execution will stop and wait for the IDE to attach.
5. In the IDE (GoLand in this case) create the configuration, necessary for attaching to the debugger (default one in GoLand 2021.3):
![Screenshot from 2022-02-22 00-00-16](https://user-images.githubusercontent.com/4958634/155037939-716f04db-b2f2-4601-9459-6889d0b6ee8c.png)
6. Put a breakpoint in the code - for example at the "API listen" log line in the `server.go` file, line 85, which gets executed during the daemon's start.
7. Run the debug configuration in the IDE
8. Admire the breakpoint being triggered and the debugging information being displayed:
![Screenshot from 2022-02-22 00-31-29](https://user-images.githubusercontent.com/4958634/155038398-08ac45ae-4a02-48b8-a145-582ddcc7396d.png)

**- Description for the changelog**
<!--


Write a short (one line) summary that describes the changes in this
pull request for inclusion in the changelog:
-->

Added possibility to debug Docker daemon with Delve.

**- A picture of a cute animal (not mandatory but encouraged)**
![IMG_20210728_151005](https://user-images.githubusercontent.com/4958634/155035057-ed419eab-7496-486e-8f3c-3aee090817de.jpg)

hack/make/run
#!/usr/bin/env bash

set -e
rm -rf "$DEST"

if ! command -v dockerd &> /dev/null; then
	echo >&2 'error: binary-daemon or dynbinary-daemon must be run before run'
	false
fi

DOCKER_GRAPHDRIVER=${DOCKER_GRAPHDRIVER:-vfs}
DOCKER_USERLANDPROXY=${DOCKER_USERLANDPROXY:-true}

# example usage: DOCKER_STORAGE_OPTS="dm.basesize=20G,dm.loopdatasize=200G"
storage_params=""
if [ -n "$DOCKER_STORAGE_OPTS" ]; then
	IFS=','
	for i in ${DOCKER_STORAGE_OPTS}; do
		storage_params="--storage-opt $i $storage_params"
	done
	unset IFS
fi

listen_port=2375
if [ -n "$DOCKER_PORT" ]; then
	IFS=':' read -r -a ports <<< "$DOCKER_PORT"
	listen_port="${ports[-1]}"
fi

extra_params="$DOCKERD_ARGS"
if [ "$DOCKER_REMAP_ROOT" ]; then
	extra_params="$extra_params --userns-remap $DOCKER_REMAP_ROOT"
fi

if [ -n "$DOCKER_EXPERIMENTAL" ]; then
	extra_params="$extra_params --experimental"
fi

dockerd="dockerd"
socket=/var/run/docker.sock
if [ -n "$DOCKER_ROOTLESS" ]; then
	user="unprivilegeduser"
	uid=$(id -u $user)
	# shellcheck disable=SC2174
	mkdir -p -m 700 "/tmp/docker-${uid}"
	chown $user "/tmp/docker-${uid}"
	dockerd="sudo -u $user -E XDG_RUNTIME_DIR=/tmp/docker-${uid} -E HOME=/home/${user} -- dockerd-rootless.sh"
	socket=/tmp/docker-${uid}/docker.sock
fi

args="--debug \
	--host "tcp://0.0.0.0:${listen_port}" --host "unix://${socket}" \
	--storage-driver "${DOCKER_GRAPHDRIVER}" \
	--userland-proxy="${DOCKER_USERLANDPROXY}" \
	$storage_params \
	$extra_params"

echo "${dockerd} ${args}"
# shellcheck disable=SC2086
exec "${dockerd}" ${args}

#!/usr/bin/env bash

set -e
rm -rf "$DEST"

if ! command -v dockerd &> /dev/null; then
	echo >&2 'error: binary-daemon or dynbinary-daemon must be run before run'
	false
fi

DOCKER_COMMAND="$(command -v dockerd)"

DOCKER_GRAPHDRIVER=${DOCKER_GRAPHDRIVER:-vfs}
DOCKER_USERLANDPROXY=${DOCKER_USERLANDPROXY:-true}

# example usage: DOCKER_STORAGE_OPTS="dm.basesize=20G,dm.loopdatasize=200G"
storage_params=""
if [ -n "$DOCKER_STORAGE_OPTS" ]; then
	IFS=','
	for i in ${DOCKER_STORAGE_OPTS}; do
		storage_params="--storage-opt $i $storage_params"
	done
	unset IFS
fi

listen_port=2375
if [ -n "$DOCKER_PORT" ]; then
	listen_port="${DOCKER_PORT##*:}"
fi

if [ -n "$DELVE_PORT" ]; then
	delve_listen_port="${DELVE_PORT##*:}"
fi

extra_params="$DOCKERD_ARGS"
if [ "$DOCKER_REMAP_ROOT" ]; then
	extra_params="$extra_params --userns-remap $DOCKER_REMAP_ROOT"
fi

if [ -n "$DOCKER_EXPERIMENTAL" ]; then
	extra_params="$extra_params --experimental"
fi

socket=/var/run/docker.sock
if [ -n "$DOCKER_ROOTLESS" ]; then
	user="unprivilegeduser"
	uid=$(id -u $user)
	# shellcheck disable=SC2174
	mkdir -p -m 700 "/tmp/docker-${uid}"
	chown $user "/tmp/docker-${uid}"
	socket=/tmp/docker-${uid}/docker.sock
fi

# shellcheck disable=SC2206
args=(
	--debug
	--host="tcp://0.0.0.0:${listen_port}"
	--host="unix://${socket}"
	--storage-driver="${DOCKER_GRAPHDRIVER}"
	--userland-proxy="${DOCKER_USERLANDPROXY}"
	$storage_params
	$extra_params
)

dockerd=("$DOCKER_COMMAND")

if [ -n "$DELVE_PORT" ]; then
	dockerd=(
		dlv
		--listen="0.0.0.0:$delve_listen_port"
		--headless=true
		--log
		--api-version=2
		--only-same-user=false
		--check-go-version=false
		--accept-multiclient
		exec "${dockerd[@]}" --
	)
fi

if [ -n "$DOCKER_ROOTLESS" ]; then
	dockerd=(
		sudo -u "$user"
		-E DOCKERD="${dockerd[*]}"
		-E XDG_RUNTIME_DIR="/tmp/docker-${uid}"
		-E XDG_CONFIG_HOME="/home/${user}/.config"
		-E HOME="/home/${user}"
		--
		dockerd-rootless.sh
	)
fi

set -x
# shellcheck disable=SC2086
exec "${dockerd[@]}" "${args[@]}"

So as a whole, this will be executed as;

```bash
exec dlv [delve options] exec dockerd -- [dockerd options]
```

Trying to get my head around that part (bear with me, perhaps I'm completely wrong, just glancing over);

- is there an `exec` too many there, or is that correct?
- doesn't the `--` terminal processing flags for `dockerd` ?


Basically, I was also looking if we could make the delve command a separate variable, so that it could all be called as;

```bash
exec ${delve} "${dockerd}" ${args}
```

or

```bash
${delve} exec "${dockerd}" ${args}
```
