四、Docker Daemon 中NewDaemon的实现(摘自《Docker源码分析》)

1、NewDaemon 作用简介

在 Docker 架构中有很多重要的概念,如:graph,graphdriver,execdriver,networkdriver,volumes,Docker containers 等。Docker 在实现过程中,需要将以上实体进行统一化管理,而 Docker Daemon 中的 daemon 实例就是设计用来完成这一任务的实体。

2、NewDaemon 介绍

NewDaemon 函数的执行完成了 Docker Daemon 创建并加载 daemon 的任务,最终实现统一管理 Docker Daemon 的资源。

NewDaemon的执行流程主要包括12个独立步骤:

1)、处理配置信息
2)、检测系统支持及用户权限
3)、配置工作路径
4)、加载并配置 graphdriver
5)、创建 Docker Daemon 网络环境
6)、创建并初始化 graphdb
7)、创建 execdriver
8)、创建 daemon 实例
9)、检测 DNS 配置
10)、加载已有 container
11)、设置 shutdown 处理方法
12)、以及返回 daemon 实例

2.1 应用配置信息

配置信息的主要功能是:供用户自由配置 Docker 的可选功能,使得 Docker 的运行更贴近用户期待的运行场景。

配置信息的处理包含 4 部分:

1)配置 Docker 容器的 MTU——config 信息中的 Mtu 应用于容器网络的最大传输单元(MTU)特性
2)检测网桥配置信息——检测 config 中 BridgeIface 和 BridgeIP 这两个信息
3)查验容器通信配置——主要是针对 config 中的 EnableIptables 和 InterContainerCommunication 这两个属性。EnableIptables 属性的作用是启用 Docker 对 iptables 规则的添加功能;InterContainerCommunication 的作用是启用 Docker container 之间互相通信的功能。
4)处理 PID 文件配置——主要工作是:为 Docker Daemon 进程运行时的 PID 号创建一个 PID 文件,文件的路径即为 config 中的 Pidfile 属性。并且为 Docker Daemon 的 shutdown 操作添加一个删除该 Pidfile 的函数,以便在 Docker Daemon 退出的时候,可以在第一时间删除该 Pidfile

2.2 检测系统支持及用户权限

处理完 Docker 的配置信息之后,Docker 对自身运行的环境进行了一系列的检测,主要包括三个方面:
1)操作系统类型对 Docker Daemon 的支持
2)用户权限的级别
3)内核版本与处理器的支持

2.3 配置工作路径

主要是创建 Docker Daemon 运行中所在的工作目录。
主要步骤如下:
(1) 使用规范路径创建一个 TempDir,路径名为 tmp;
(2) 通过 tmp,创建一个指向 tmp 的文件符号连接 realTmp;
(3) 使用 realTemp 的值,创建并赋值给环境变量 TMPDIR;
(4) 处理 config 的属性 EnableSelinuxSupport;
(5) 将 realRoot 重新赋值于 config.Root,并创建 Docker Daemon 的工作根目录。

2.4 加载并配置 graphdriver

加载并配置存储驱动 graphdriver,目的在于:使得 Docker Daemon 创建 Docker 镜像管理所需的驱动环境。

Graphdriver 用于完成 Docker 容器镜像的管理,包括存储与获取,主要工作是:在文件系统中指定的 root 目录下,实例化一个全新的 graph 对象,作用为:存储所有标记的文件系统镜像,并记录镜像之间的关系。

1)创建 volumesdriver 以及 volumes graph
Docker 中 volume 的概念是:可以从 Docker 宿主机上挂载到 Docker 容器内部的特定目录。一个 volume 可以被多个 Docker 容器挂载,从而 Docker 容器可以实现互相共享数据等。在实现 volumes 时,Docker 需要使用 driver 来管理它,又由于 volumes 的管理不会像容器文件系统管理那么复杂,故 Docker 采用 vfs 驱动实现 volumes 的管理。

2)创建 TagStore
主要是用于存储镜像的仓库列表(repository list)

·TagStore 类型中的多个属性的含义:
·path:TagStore 中记录镜像仓库的文件所在路径;
·graph:相应的 Graph 实例对象;
·Repositories:记录具体的镜像仓库的 map 数据结构;
·sync.Mutex:TagStore 的互斥锁
·pullingPool :记录池,记录有哪些镜像正在被下载,若某一个镜像正在被下载,则驳回其他 Docker Client 发起下载该镜像的请求;
·pushingPool:记录池,记录有哪些镜像正在被上传,若某一个镜像正在被上传,则驳回其他 Docker Client 发起上传该镜像的请求;

2.5 创建 Docker Daemon 网络环境

创建网络环境是极为重要的一个部分,这不仅关系着容器对外的通信,同样也关系着容器间的通信。
bridge.InitDriver 函数作用为:
·获取为 Docker 服务的网络设备的地址;
·创建指定 IP 地址的网桥;
·启用 Iptables 功能并配置;
·另外还为 eng 实例注册了 4 个 Handler, 如 ”allocate_interface”, ”release_interface”, ”allocate_port”,”link”。
1)创建网桥名为:docker0
2)启用 iptables 包括 container 容器之间所需要的 link 操作提供支持,为 host 主机上所有的对外对内流量制定传输规则等
假设网桥设备名为 docker0,网桥网络地址为 docker0_ip, 设置 iptables 规则,操作步骤如下:
(1) 使用 iptables 工具开启新建网桥的 NAT 功能,使用命令如下:

iptables -I POSTROUTING -t nat -s docker0_ip ! -o docker0 -j MASQUERADE

(2) 通过 icc 参数,决定是否允许 container 间通信,并制定相应 iptables 的 Forward 链。Container 之间通信,说明数据包从 container 内发出后,经过 docker0,并且还需要在 docker0 处发往 docker0,最终转向指定的 container。换言之,从 docker0 出来的数据包,如果需要继续发往 docker0,则说明是 container 的通信数据包。命令使用如下:

iptables -I FORWARD -i docker0 -o docker0 -j ACCEPT

(3) 允许接受从 container 发出,且不是发往其他 container 数据包。换言之,允许所有从 docker0 发出且不是继续发向 docker0 的数据包,使用命令如下:

iptables -I FORWARD -i docker0 ! -o docker0 -j ACCEPT

(4) 对于发往 docker0,并且属于已经建立的连接的数据包,Docker 无条件接受这些数据包,使用命令如下:


iptables -I FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

3)启用系统数据包转发功能
在 Linux 系统上,数据包转发功能是被默认禁止的。数据包转发,就是当 host 主机存在多块网卡的时,如果其中一块网卡接收到数据包,并需要将其转发给另外的网卡。通过修改 /proc/sys/net/ipv4/ip_forward 的值,将其置为 1,则可以保证系统内数据包可以实现转发功能

cat /proc/sys/net/ipv4/ip_forward
1

4)创建 DOCKER 链
在网桥设备上创建一条名为 DOCKER 的链,该链的作用是在创建 Docker container 并设置端口映射时使用。
5) 注册 Handler 至 Engine (引擎)
在创建完网桥,并配置完基本的 iptables 规则之后,Docker Daemon 在网络方面还在 Engine 中注册了 4 个 Handler,这些 Handler 的名称与作用如下:
·allocate_interface:为 Docker container 分配一个专属网卡;
·realease_interface:释放网络设备资源;
·allocate_port:为 Docker container 分配一个端口;
·link:实现 Docker container 间的 link 操作。
6)创建 graphdb 并初始化
Graphdb 是一个构建在 SQLite 之上的图形数据库,通常用来记录节点命名以及节点之间的关联。Docker Daemon 使用 graphdb 来记录镜像之间的关联。
7)创建 execdriver
Execdriver 是 Docker 中用来执行 Docker container 任务的驱动。创建并初始化 graphdb 之后,Docker Daemon 随即创建了 execdriver,在创建 execdriver 的时候,需要 4 部分的信息,以下简要介绍这 4 部分信息:

  • config.ExecDriver:Docker 运行时中指定使用的 exec 驱动类别,在默认配置文件中默认使用”native”, 也可以将这个值改为”lxc”,则使用 lxc 接口执行 Docker container 内部的操作 ;
  • config.Root:Docker 运行时的 root 路径,默认配置文件中为”/var/lib/docker”;
  • sysInitPath: 系统上存放 dockerinit 二进制文件的路径,一般为”/var/lib/docker/init/dockerinit-1.2.0”;
  • sysInfo: 系统功能信息,包括:容器的内存限制功能,交换区内存限制功能,数据转发功能,以及 AppArmor 安全功能等。

8)创建 daemon 实例
分析 Daemon 类型的属性:

属性名
作用
repository
部署所有 Docker 容器的路径
containers
用于存储具体 Docker 容器信息的对象
graph
存储 Docker 镜像的 graph 对象
repositories
存储 Docker 镜像元数据的文件
idIndex
用于通过简短有效的字符串前缀定位唯一的镜像
sysInfo
系统功能信息
volumes
管理 host 主机上 volumes 内容的 graphdriver,默认为 vfs 类型
config
Config.go 文件中的配置信息,以及执行产生的配置 DisableNetwork
containerGraph
存放 Docker 镜像关系的 graphdb
driver
管理 Docker 镜像的驱动 graphdriver,默认为 aufs 类型
sysInitPath
系统 dockerinit 二进制文件所在的路径
execDriver
Docker Daemon 的 exec 驱动,默认为 native 类型
eng
Docker 的执行引擎 Engine 类型实例
9) 检测 DNS 配置
获取 /etc/resolv.conf 中的 DNS 服务器信息。若本地 DNS 文件中有 127.0.0.1,而 Docker container 不能使用该地址,故采用默认外在 DNS 服务器,为 8.8.8.8,8.8.4.4,并将其赋值给 config 文件中的 Dns 属性。
10)启动时加载已有 Docker containers
当 Docker Daemon 启动时,会去查看在 daemon.repository,也就是在 /var/lib/docker/containers 中的内容。若有存在 Docker container 的话,则让 Docker Daemon 加载这部分容器,将容器信息收集,并做相应的维护。
11)设置 shutdown 的处理方法
加载完已有 Docker container 之后,Docker Daemon 设置了多项在 shutdown 操作中需要执行的 handler。也就是说:当 Docker Daemon 接收到特定信号,需要执行 shutdown 操作时,先执行这些 handler 完成善后工作,最终再实现 shutdown。

eng 对象 shutdown 操作执行时主要完成 4 部分的操作:

  • 运行 daemon 对象的 shutdown 函数,做 daemon 方面的善后工作;
  • 通过 portallocator.ReleaseAll(),释放所有之前占用的端口资源;
  • 通过 daemon.driver.Cleanup(),通过 graphdriver 实现 unmount 所有 layers 中的挂载点;
  • 通过 daemon.containerGraph.Close() 关闭 graphdb 的连接。

12)返回 daemon 对象实例

你可能感兴趣的:(四、Docker Daemon 中NewDaemon的实现(摘自《Docker源码分析》))