docker如何确定dockerd的地址

intro

docker这个命令行工具本身通常是接触容器的第一关:示例中一般会使用docker来制作(build)镜像/运行(run)容器。但是docker这个可执行程序本身并没有太复杂的逻辑,它更多的是提供了一个类似于bash这种和人类用户更友好的命令/子命令,真正的workhorse是dockerd进程(当然,也可能是dockerd的后端containerd,不过暂时只是从docker和dockerd的角度看这个问题)。docker将用户请求解析为dockerd理解的协议转发给dockerd执行。

不过在执行docker的时候,通常也没有人指定dockerd的地址,那么dockerd是如何找到dockerd的地址并和它通讯的呢?这个问题本身并不复杂,这里主要是基于这个问题从源码的角度来解答这个问题。

docker构建

这里只是docker这个可执行文件本身,而不包括dockerd。根据docker工程说明文档,执行

docker buildx bake

进行构建,生成的可执行文件为build/docker。但是使用gdb调试时无法看到调试信息:

tsecer@harry: gdb -quiet build/docker
Reading symbols from build/docker...
Dwarf Error: DW_FORM_strx1 found in non-DWO CU [in module /home/tsecer/docker-cli/build/docker-linux-amd64]
(No debugging symbols found in build/docker)
warning: Missing auto-load script at offset 0 in section .debug_gdb_scripts
of file /home/tsecer/docker-cli/build/docker-linux-amd64.
Use `info auto-load python-scripts [REGEXP]' to list them.
(gdb) start
Temporary breakpoint 1 at 0xf30f60
Starting program: /home/tsecer/docker-cli/build/docker 
[New LWP 39041]
[New LWP 39042]
[New LWP 39043]
[New LWP 39044]
[New LWP 39045]

Thread 1 "docker" hit Temporary breakpoint 1, 0x0000000000f30f60 in main.main ()
(gdb) 

构建参数

镜像的生成使用了docker-cli/scripts/build/.variables文件中的变量,是否strip调试信息是通过GO_STRIP变量控制,并且默认的配置是不会strip调试信息的,这一点从构建过程中输出的构建命令也可以验证。

if [ -n "$GO_STRIP" ]; then
    # if stripping enabled and building with llvm < 12 against darwin/amd64
    # platform, it will fail with:
    #  # github.com/docker/cli/cmd/docker
    #  /usr/local/go/pkg/tool/linux_amd64/link: /usr/local/go/pkg/tool/linux_amd64/link: running strip failed: exit status 1
    #  llvm-strip: error: unsupported load command (cmd=0x5)
    # more info: https://github.com/docker/cli/pull/3717
    GO_LDFLAGS="$GO_LDFLAGS -s -w"
fi

gdb版本

注意到gdb启动时的错误输出

Dwarf Error: DW_FORM_strx1 found in non-DWO CU [in module /home/tsecer/docker-cli/build/docker-linux-amd64]
(No debugging symbols found in build/docker)

可以从晚上找到相应的答案:主要是由于gdb版本低于10.1的时候不识别新添加的选项。可能是因为构建时拉取的很多组件都是最新的,而host上的软件相对比较久导致的。

但是之前遇到gdb不识别某些新特性只会导致某些/部分功能不可用,像这种某个内容不是别而导致整个调试信息不可用还是有些奇怪的(或许如果gdb输出是"error:"引导的都会导致整个调试信息不可用?)。

问题

This is a known issue. GDB older then v10.1 does not support some DWARFv5 features, while clang gets DWARFv5 enabled by default. You can either use newer version of GDB or switch to DWARFv4 (-gdwarf-4).

Here are some details Gdb <10.1 can't read clang's DWARF v5 - #12 by tstellar

按照这说明,从源码构建一个最新的gdb版本(gdb15),就可以正确加载调试符号:-)。

关闭优化

但是调试的时候还是会发现变量被优化掉了。

(gdb) f 1
#1  main.main () at /go/src/github.com/docker/cli/cmd/docker/docker.go:33
warning: 33     /go/src/github.com/docker/cli/cmd/docker/docker.go: No such file or directory
(gdb) p ctx
$1 = <optimized out>

可以通过添加额外的 -N -l选项来关闭优化。

-gcflags '-N -l'

简单粗暴的将scripts/build/binary脚本中的

(set -x ; go build -o "${TARGET}" -tags "${GO_BUILDTAGS}" -ldflags "${GO_LDFLAGS}" ${GO_BUILDMODE} "${SOURCE}")

修改为

(set -x ; go build -gcflags '-N -l' -o "${TARGET}" -tags "${GO_BUILDTAGS}" -ldflags "${GO_LDFLAGS}" ${GO_BUILDMODE} "${SOURCE}")

也就是在go build之后添加文档中说明的 -gcflags '-N -l' 选项。

服务器地址

docker连接的dockerd执行的通讯协议,在docker-cli\opts\hosts.go文件配置为默认通过unix socket访问/var/run/docker.sock

const (
   // defaultHTTPPort Default HTTP Port used if only the protocol is provided to -H flag e.g. dockerd -H tcp://
   // These are the IANA registered port numbers for use with Docker
   // see http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=docker
   defaultHTTPPort = "2375" // Default HTTP Port
   // defaultTLSHTTPPort Default HTTP Port used when TLS enabled
   defaultTLSHTTPPort = "2376" // Default TLS encrypted HTTP Port
   // defaultUnixSocket Path for the unix socket.
   // Docker daemon by default always listens on the default unix socket
   defaultUnixSocket = "/var/run/docker.sock"
   // defaultTCPHost constant defines the default host string used by docker on Windows
   defaultTCPHost = "tcp://" + defaultHTTPHost + ":" + defaultHTTPPort
   // DefaultTLSHost constant defines the default host string used by docker for TLS sockets
   defaultTLSHost = "tcp://" + defaultHTTPHost + ":" + defaultTLSHTTPPort
   // DefaultNamedPipe defines the default named pipe used by docker on Windows
   defaultNamedPipe = `//./pipe/docker_engine`
   // hostGatewayName defines a special string which users can append to --add-host
   // to add an extra entry in /etc/hosts that maps host.docker.internal to the host IP
   // TODO Consider moving the hostGatewayName constant defined in docker at
   // github.com/docker/docker/daemon/network/constants.go outside of the "daemon"
   // package, so that the CLI can consume it.
   hostGatewayName = "host-gateway"
)

毫无意外,如果通过tcp访问外部服务器,缺省的主机地址为本机docker-cli\opts\hosts_unix.go


//go:build !windows

package opts

// defaultHost constant defines the default host string used by docker on other hosts than Windows
const defaultHost = "unix://" + defaultUnixSocket

// defaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. dockerd -H tcp://:8080
const defaultHTTPHost = "localhost"

outro

docker在unix系统中默认是通过代码中配置的、路径名为/var/run/docker.sock的unix socket和dockerd通讯。

posted on 2024-05-23 18:08  tsecer  阅读(22)  评论(0编辑  收藏  举报

导航