Docker采用传统的client-server架构模式,如下图。用户通过Docker client与Docker daemon建立通信。
由上图可以看出,Docker daemon是Docker 架构中的主要接口。它提供APIserver用于接受来自Docker client的请求,然后根据不同的请求分发给Docker daemon的不同模块执行相应工作。
Docker通过driver模块来实现对Docker模块执行环境的定制。当需要创建Docker 容器时可从Docker registry中下载镜像,并通过镜像驱动graphdriver将下载的镜像以graph的形式存储在本地;当需要相纸Docker容器运行资源或执行用户指令等操作时,则通过execdriver来完成。libcontainer是一个独立的容器管理包,networkdriver和execdriver都通过libcontainer来实现对容器的操作,包括namespace实现容器间的资源隔离和利用cgroup实现对容器的资源限制。当容器的命令执行完毕,一个拥有独立的文件系统、安全且相互隔离的运行环境的容器就处于运行状态。
下表是对各部分的详细介绍:
模块名 | 功能介绍 |
---|---|
Docker daemon | Docker最核心的后台进程,负责响应来自Docker client的请求(启动API server),接收到的请求再由具体的函数来执行 |
Docker client | 指任何可以向指定Docker daemon发起请求的客户端,可以是Docker cli,也可以是任何遵循Docker API的客户端。 |
graph | 负责维护已下载的镜像信息以及它们之间的关系,所有大部分Docker镜像的操作会由graph组件来完成。常常出现性能瓶颈 |
GraphDB | 记录Docker daemon所维护的所有容器以及它们之间的关系,具体说,GraphDB就是一个SQLite的最简单版本的图形数据库,能够提供增,删,遍历,连接以及所有父子节点的查询等功能.这些节点对应的就是一个容器. |
driver | 前面提到,Docker daemon负责将用户的请求转译成系统调用,进而创建和管理容器的核心进程,在实现过程中,为了将这些系统调用抽象成为统一的操作接口方便调用,Docker把这些操作分类成容器管理驱动,网络管理驱动,文件存储驱动3种,分别对应execdriver, networkdriver和graphdriver. 1. execdriver 是对Linux操作系统的namespaces, cgroups, apparmor, SELinux等运行所需的系统操作进行的一层二次封装,本质类似于LXC. 2.networkdriver是对容器网路环境的封装.3. graphdriver是所有与容器相关操作的最终执行者. |
-
client模式:
docker 指令对应的源文件是docker/docker.go,它的格式如下:
docker [OPTIONS] COMMAND [arg...]
其中OPTIONS参数为flag,执行指令时,Docker需要解析这些flag,如果在解析过程中发现用户声明了-d,Docker就会创建一个运行在宿主主机的daemon(docker.daemon.go#mainDaemon),然后原告docker -d xxx指令执行成功,否则,docker继续解析剩余的flag,按照用户声明的COMMEND向指定的Docker Daemon发送对应的请求,这便是client模式.
(1)重要的flag信息
flags 参数 功能 flDebug -D, --debug, -l/--log-level=debug 在系统中添加DEBUG变量并赋值为1,并把日志显示等级调为DEBUG级 flHosts -H 对于-H参数,就是值本次操作需要链接的Docker daemon位置,而对于daemon模式则提供所要监听的地址.若flHosts参数或者系统环境变量DOCKER_HOST不为空,说明用户指定了host对象.否则默认设置为"unix:///var/run/docker.sock" flDaemon -d 表示将Docker作为daemon启动,默认情况下Docker不作为daemon启动 protoAddrParts -H参数中://前后两部分 与Docker daemon建立通信的协议方式与Socket地址 (2)创建client实例
client的创建就是在已有配置参数的基础上,调用api/client/cli.go#NewDockerCli,需要设置好proto(传输协议),addr(host的目标地址)和tlsConfig(阿宽安全传输层协议的配置),另外还有配置标准输入输出以及错误输出.
(3)执行具体的命令
Docker client对象创建成功后,剩下执行具体指令的过程就交给api/client/cli.go来处理
从命令映射到对应的方法
cli主要通过反射机制,从用户输入的命令(如run)得到匹配的执行方法(比如CmdRun)这也是所谓"约定大于配置",同时会判断是否用于多级Docker命令支持(如docker group run)
执行对应的方法,发起请求
找到具体的执行方法后,开始执行.基本的执行流程如下:
<1> 解析传入的参数,并针对参数进行配合处理
<2>获取与Docker daemon通信所需要的认证配置信息
<3> 根据命令业务类型,给Docker daemon发送POST, GET等请求
<4>读取来自Docker daemon的返回结果
-
daemon模式(Docker 1.7版本):
Docker运行时有-d参数,就会运行Docker daemon.
一旦Docker进入daemon模式,剩下的初始化和启动工作都由Docker的docker.daemon.go#mainDaemon来完成
Docker daemon通过一个server模块(api/server/server.go)接收来自client的请求,然后根据请求类型交由具体的方法去执行.因此,Docker进程需要首先启动并初始化这个server,之后,Docker进程需要初始化一个daemon对象(daemon/daemon.go)来负责处理server接收到的请求.