当您启动Docker守护进程时,
-H fd://
将告诉Docker该服务由Systemd启动并使用套接字激活。然后,systemd将创建目标套接字并将其传递给Docker守护程序以供使用。这在
介绍Systemd和
介绍套接字激活中有描述。博客内容相当长,但确实值得一读,以下是理解此问题的关键要点的简短总结:
- Systemd是一个旨在取代传统SysV init系统的新型
init
系统,其关键特性之一是更快的初始化过程。
套接字激活
是Systemd中用于加速服务初始化的技术之一。
- 为了接收请求,服务需要侦听的套接字。以Docker为例,它需要像
/var/run/docker.sock
这样的unix域套接字
或TCP套接字。当然,大多数情况下,服务本身会在启动时创建这些套接字。
- 使用套接字激活时,Systemd将创建这些套接字并侦听服务,并在服务启动时使用
exec
将这些套接字传递给服务。其中一个好处是,一旦成功创建套接字,客户端请求可以排队在套接字缓冲区中,即使相关服务尚未启动。
- Systemd使用的特定服务的套接字信息位于
socket
单元文件中,对于Docker来说,它是[docker.socket][3]
,其内容如下:
[Unit]
Description=Docker Socket for the API
PartOf=docker.service
[Socket]
ListenStream=/var/run/docker.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker
[Install]
WantedBy=sockets.target
现在让我们看看整个过程是如何工作的。我在/etc/systemd/system
下有文件docker.socket
和docker.service
。对于docker.service
,ExecStart
行是:
ExecStart=/usr/bin/dockerd -H fd://
停止Docker服务:systemctl stop docker
$> ps aux | grep 'docker' # the `grep` itself in the output is ignored
$> lsof -Ua | grep 'docker'
$>
没有正在运行的Docker进程,也没有docker.sock文件。
执行命令
systemctl start docker.socket
:
$> systemctl start docker.socket
$> ps aux | grep 'docker'
$> lsof -Ua | grep 'docker'
systemd 1 root 27u unix 0xffff880036da6000 0t0 140748188 /var/run/docker.sock
启动docker.socket
后,我们可以看到仍然没有docker
进程运行,但是套接字/var/run/docker.sock
已经被创建,并且属于systemd
进程。
(离题:实际上,现在套接字已经准备好接收请求,尽管docker
还没有运行。当第一个请求到来时,systemd将启动docker.service
,并将已经创建的套接字传递给Docker。这被称为按需自动生成)
启动docker.service
$> systemctl start docker.service
$> ps aux | grep 'docker'
root 26302 0.0 1.8 431036 38712 ? Ssl 14:57 0:00 /usr/bin/dockerd -H fd://
<....>
你可以看出Docker现在正在运行。让我们往回走一步,尝试从终端手动执行/usr/bin/dockerd -H fd://
:
$> /usr/bin/dockerd -H fd://
FATA[0000] no sockets found via socket activation: make sure the service was started by systemd
现在你看到了区别。当你使用-H fd://
时,Docker将期望它的父进程传递套接字而不是自己创建。当它由Systemd启动时,Systemd会执行这项工作,但当您在终端上手动启动它时,您没有这样做,因此Docker守护进程进程失败并中止。 这是Docker进程在Docker守护程序启动时如何处理fd://的代码,如果您有兴趣,可以查看。
另一方面,对于Docker客户端,Docker CLI将从-H
中指定的host
解析协议/地址,并向Docker守护程序发出http
请求。默认主机是unix:///var/run/docker.sock
。支持的协议包括tcp
,unix
,npipe
和fd
。就我从源代码中探索到的内容来看,fd
的传输配置与tcp
相同,因此如果您具有正在侦听的tcp套接字,则可以使用以下命令进行操作:
$> docker -H fd://localhost:4322 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
这与以下内容相同:
docker -H tcp://localhost:4322 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES