Docker 架构

Docker CS 架构

Docker 是个典型的 CS 应用,分为 Docker 客户端和 Docker 服务端。客户端本质上市 CLI 程序,负责建立与服务端之间的通信;服务端负责接收处理客户端的请求,以及管理 Docker 容器和镜像。

Docker 服务端常驻后台,也叫 Docker 守护进程(Docker daemon)

docker-frame-daemon-main

服务端大致分为 Server、Engine、Job 三部分

  • Server

    负责接收客户端的请求,并按照相应的路由规则实现请求的路由分发,最后将请求结果返回给客户端。

    docker-frame-daemon-server

  • Engine

    Engine 是 Docker 运行的核心模块,负责处理客户端请求,并根据请求类型创建、执行 Job

  • Job

    Job 可以认为是 Engine 内部最基本的工作执行单元;Docker 中的每项工作都可以抽象为一个 Job

通信

客户端和服务端之间通过 REST API 进行交互:客户端每发送一条指令,底层都会转化为 REST API 调用的形式发送给服务端,服务端处理客户端发送的请求并给出响应。

镜像构建、容器创建、容器运行等工作都是由服务端来完成,客户端只是承担发送指令的角色。

除了通过指令通信,客户端与服务端之间也可使用 Remote API 进行通信。

连接方式

服务端可以通过以下几种方式来监听客户端的请求

UNIX Socket

1
unix:///var/run/docker.sock

默认的连接方式,通过创建的 /var/run/docker.sock 文件进行连接;UNIX 套接字的方式通常用于本地进程之间的通信

FD

1
fd://socketfd

在基于 Systemd 的系统上,通过 Systemd socket activation 进行通信。

1
dockerd -H fd://

想进一步了解,可以参考 Socket activation 初体验Socket activation

TCP

上面两种连接方式都只支持本地通信,想要进行远程通信,可以通过 TCP 连接。

1
tcp://host:port

服务器开启端口监听,客户端通过指定的 IP 和端口访问服务器,默认端口号为 2375。

1
2
3
4
5
// 服务端开启监听
dockerd -H IP:PORT

// 客户端
docker -H IP:PORT

这种方式存在风险,数据通过明文传输,而且只要知道 IP 和端口号,任何人都能访问对应的 Docker 服务。如果需要通过这种方式连接,建议进行加密传输

如果出现无法连接的情况,检查下是否是防火墙的问题,因为 Engine 通过 iptables 进行转发;需要检查是否启动 iptables,另外还要添加 INPUT 策略。

1
2
> iptables -A INPUT -p tcp --dport 2375 -j ACCEPT
>
TCP + TLS

在 TCP 的基础上增加了 SSL 以保证连接安全,默认端口为 2376。

  • 创建 CA 和证书

    生成服务端的 CA 和证书,并同时生成客户端的证书

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ├── ca-key.pem
    ├── ca.pem # clent & server
    ├── ca.srl
    ├── cert.pem # client public key
    ├── client.csr # 请求文件
    ├── extfile.cnf # 配置文件
    ├── key.pem # client private key
    ├── server-cert.pem # server public key
    ├── server.csr # 请求文件
    └── server-key.pem # server private key
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    # 生成 CA 私钥
    $ openssl genrsa -aes256 -out ca-key.pem 4096

    # 生成 CA 公钥
    $ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

    # 生成服务器私钥
    $ openssl genrsa -out server-key.pem 4096

    # 用私钥生成证书请求文件
    $ openssl req -subj "/CN=localhost" -sha256 -new -key server-key.pem -out server.csr

    # 用 CA 来签署证书
    $ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
    -CAcreateserial -out server-cert.pem -extfile extfile.cnf

    # 生成客户端私钥
    $ openssl genrsa -out key.pem 4096

    # 用私钥生成证书请求文件
    $ openssl req -subj '/CN=client' -new -key key.pem -out client.csr
    $ echo extendedKeyUsage = clientAuth >> extfile.cnf

    # 用 CA 来签署证书
    $ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
    -CAcreateserial -out cert.pem -extfile extfile.cnf

    # 删除文件,更改文件权限
    $ rm -v client.csr server.csr
    $ chmod -v 0400 ca-key.pem key.pem server-key.pem
    $ chmod -v 0444 ca.pem server-cert.pem cert.pem
  • 配置服务端证书

    配置服务端证书并重启 Docker

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // etc/docker/daemon.json
    {
    "hosts":[
    "fd://",
    "tcp://0.0.0.0:2376"
    ],
    "tlsverify":true,
    "tlscacert":"/etc/docker/ssl/ca.pem",
    "tlscert":"/etc/docker/ssl/server-cert.pem",
    "tlskey":"/etc/docker/ssl/server-key.pem"
    }
  • 下载客户端证书

    将服务端生成的客户端证书(ca.pemcert.pemkey.pem)下载到本地

    1
    2
    docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \
    -H=$HOST:2376 version

    也可将这些证书放入 ~/.docker 文件目录下,当然也可以选择其它目录,但需要通过 DOCKER_CERT_PATH 变量来指定。