S_lion's Studio

浅谈docker框架

字数统计: 2.1k阅读时长: 8 min
2021/07/11 Share

如果你之前使用过docker应该发现其并不难,把所谓的三大核心“镜像”,“容器”,“仓库”掌握了就能满足日常工作中的大多数应用内容。但如果想要更深入些了解docker,就需要熟悉下docker的框架结构,正巧前段时间同组大佬通过docker源码来分享了一波学习心得,也使得我对docker的理解更深了些,下面来一起分析下docker的框架结构。

docker架构

在我的本地环境执行docker version,会发现里面有client端与server端,证明docker是一个C/S模式的架构。

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
[root@kubemaster ~]# docker version
Client: Docker Engine - Community
Version: 19.03.4
API version: 1.40
Go version: go1.12.10
Git commit: 9013bf583a
Built: Fri Oct 18 15:52:22 2019
OS/Arch: linux/amd64
Experimental: false

Server: Docker Engine - Community
Engine:
Version: 19.03.4
API version: 1.40 (minimum version 1.12)
Go version: go1.12.10
Git commit: 9013bf583a
Built: Fri Oct 18 15:50:54 2019
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.3.7
GitCommit: 8fba4e9a7d01810a393d5d25a3621dc101981175
runc:
Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version: 0.18.0
GitCommit: fec3683

一般来说我们可以把client端与server端分开,默认情况它们通过API进行连接。修改/usr/lib/systemd/system/docker.service,在ExecStart处添加 -H 0.0.0.0:31375,让docker服务端来监听主机下的31375端口。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
[root@slions_pc1 ~]# cat /usr/lib/systemd/system/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
BindsTo=containerd.service
After=network-online.target firewalld.service containerd.service
Wants=network-online.target
Requires=docker.socket

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H 0.0.0.0:31375
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229.
# Both the old, and new location are accepted by systemd 229 and up, so using the old location
# to make them work for either version of systemd.
StartLimitBurst=3

# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230.
# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make
# this option work for either version of systemd.
StartLimitInterval=60s

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this option.
TasksMax=infinity

# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes

# kill only the docker process, not all processes in the cgroup
KillMode=process

[Install]
WantedBy=multi-user.target

修改好后我们重启服务,此时docker成功监听在31375端口。

1
2
3
4
5
[root@slions_pc1 ~]# systemctl daemon-reload
[root@slions_pc1 ~]# systemctl restart docker
[root@slions_pc1 ~]# ss -lntp|grep docker
LISTEN 0 128 :::31375 :::* users:(("dockerd",pid=10808,fd=3))

这时客户端通过指明连接的docker server地址,即可查看对应服务端的镜像情况。

1
2
3
4
[root@slions_pc1 ~]# docker -H 192.168.100.10:31375 images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat latest 36ef696ea43d 8 days ago 667MB

我们知道了docker是c/s模式架构后,下面来细致化的讲解下架构模块。

docker架构图

img

大致的流程可概况为:

  1. 用户是使用Docker Client与Docker Daemon建立通信,并发送请求给后者。
  2. Docker Daemon作为Docker架构中的主体部分,首先提供Server的功能使其可以接受Docker Client的请求;
  3. Engine执行Docker内部的一系列工作,每一项工作都是以一个Job的形式的存在。
  4. Job的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动graphdriver将下载镜像以Graph的形式存储;
  5. 当需要为Docker创建网络环境时,通过网络管理驱动networkdriver创建并配置Docker容器网络环境;
  6. 当需要限制Docker容器运行资源或执行用户指令等操作时,则通过execdriver来完成。
  7. libcontainer是一项独立的容器管理包,networkdriver以及execdriver都是通过libcontainer来实现具体对容器进行的操作。

模块组件分析

Docker Client

client是docker架构中与daemon建立通讯联系的客户端。用户可以通过docker可执行文件向docker daemon发送请求管理容器。

Docker Client可以通过以下三种方式和Docker Daemon建立通信:tcp://host:port,unix://path_to_socket和fd://socketfd。

Docker Client发送容器管理请求后,由Docker Daemon接受并处理请求,当Docker Client接收到返回的请求相应并简单处理后,Docker Client一次完整的生命周期就结束了。

Docker Daemon

docker daemon是docker的常驻进程,主要作用是接收并分发client请求和管理所有容器。

docker daemon有三驾马车一个是server,一个是 engine,一个是 job

img

  • docker server:
    1. Docker Server相当于C/S架构的服务端。功能为接受并调度分发Docker Client发送的请求。接受请求后,Server通过路由与分发调度,找到相应的Handler来执行请求。
    2. Docker的启动过程中,会创建了一个mux.Router,提供请求的路由功能。该mux.Router中添加了众多的路由项,每一个路由项由HTTP请求方法(PUT、POST、GET或DELETE)、URL、Handler三部分组成。创建完mux.Router之后,Docker将Server的监听地址以及mux.Router作为参数,创建一个httpSrv=http.Server{},最终执行httpSrv.Serve()为请求服务。
    3. 在Server的服务过程中,Server在listener上接受Docker Client的访问请求,并创建一个全新的goroutine来服务该请求。在goroutine中,首先读取请求内容,然后做解析工作,接着找到相应的路由项,随后调用相应的Handler来处理该请求,最后Handler处理完请求之后回复该请求。

img

  • Engine

    1. Engine是Docke的运行引擎,同时也是Docker运行的核心模块。它扮演Docker container存储仓库的角色,并且通过执行job的方式来操纵管理这些容器。
    2. 在Engine数据结构的设计与实现过程中,有一个handler对象。该handler对象存储的都是关于众多特定job的handler处理访问。
  • job

    ​ 一个Job可以认为是Docker架构中Engine内部最基本的工作执行单元。Docker可以做的每一项工作,都可以抽象为一个job。

Docker Registry

Docker Registry就是镜像注册中心,用户如果不进行配置的话默认的地址是dokcerhub地址,这个大家应该都知道。

Graph

img

  • Repository

    1. 已下载镜像的保管者(包括下载镜像和dockerfile构建的镜像)。
    2. 镜像的存储类型有aufs,devicemapper,Btrfs,overlay2等。docker info中的Storage Driver
    3. 同时在Graph的本地目录中,关于每一个的容器镜像,具体存储的信息有:该容器镜像的元数据,容器镜像的大小信息,以及该容器镜像所代表的具体rootfs。
  • GraphDB

    ​ 已下载容器镜像之间对应关系的记录者。

Driver

Driver是Docker架构中的驱动模块。通过Driver驱动,Docker可以实现对Docker容器执行环境的定制。即Graph负责镜像的存储,Driver负责容器的执行。

  • graphdriver
    1. graphdriver主要用于完成容器镜像的管理,包括存储与获取。值得注意的地方是,不同文件系统的存储驱动器也不相同,比如redhat系的操作系统使用的是overlay2,通过xfs文件系统来管理;如果是Ubuntu则使用的是aufs;如果是suse则是btrfs。建议不要修改默认的docker storage driver,跟随系统默认。
    2. 存储:docker pull下载的镜像由graphdriver存储到本地的指定目录(Graph中)。
    3. 获取:docker run(create)用镜像来创建容器的时候由graphdriver到本地Graph中获取镜像。

img

  • networkdriver

    networkdriver的用途是完成Docker容器网络环境的配置,其中包括

    1. Docker启动时为Docker环境创建网桥;
    2. Docker容器创建时为其创建专属虚拟网卡设备;
    3. Docker容器分配IP、端口并与宿主机做端口映射,设置容器防火墙策略等。

img

  • execdriver
    1. execdriver作为Docker容器的执行驱动,负责创建容器运行命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。
    2. 现在execdriver默认使用native驱动,不依赖于LXC。

img

libcontainer

libcontainer是golang编写的第三方库,主要封装了一些linux内核与容器相关的API;因为屏蔽了所有内核操作,保证了容器与底层的松耦合性

Docker可以直接调用libcontainer,而最终操纵容器的namespace、cgroups、apparmor、网络设备以及防火墙规则等。

img

参考资料:

《Docker源码分析》

CATALOG
  1. 1. docker架构
  2. 2. docker架构图
  3. 3. 模块组件分析
    1. 3.1. Docker Client
    2. 3.2. Docker Daemon
    3. 3.3. Docker Registry
    4. 3.4. Graph
    5. 3.5. Driver
    6. 3.6. libcontainer
  4. 4. 参考资料: