如果你之前使用过docker应该发现其并不难,把所谓的三大核心“镜像”,“容器”,“仓库”掌握了就能满足日常工作中的大多数应用内容。但如果想要更深入些了解docker,就需要熟悉下docker的框架结构,正巧前段时间同组大佬通过docker源码来分享了一波学习心得,也使得我对docker的理解更深了些,下面来一起分析下docker的框架结构。
docker架构
在我的本地环境执行docker version,会发现里面有client端与server端,证明docker是一个C/S模式的架构。
1 | [root@kubemaster ~]# docker version |
一般来说我们可以把client端与server端分开,默认情况它们通过API进行连接。修改/usr/lib/systemd/system/docker.service
,在ExecStart
处添加 -H 0.0.0.0:31375
,让docker服务端来监听主机下的31375端口。
1 | [root@slions_pc1 ~]# cat /usr/lib/systemd/system/docker.service |
修改好后我们重启服务,此时docker成功监听在31375端口。
1 | [root@slions_pc1 ~]# systemctl daemon-reload |
这时客户端通过指明连接的docker server地址,即可查看对应服务端的镜像情况。
1 | [root@slions_pc1 ~]# docker -H 192.168.100.10:31375 images |
我们知道了docker是c/s模式架构后,下面来细致化的讲解下架构模块。
docker架构图
大致的流程可概况为:
- 用户是使用Docker Client与Docker Daemon建立通信,并发送请求给后者。
- Docker Daemon作为Docker架构中的主体部分,首先提供Server的功能使其可以接受Docker Client的请求;
- Engine执行Docker内部的一系列工作,每一项工作都是以一个Job的形式的存在。
- Job的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动graphdriver将下载镜像以Graph的形式存储;
- 当需要为Docker创建网络环境时,通过网络管理驱动networkdriver创建并配置Docker容器网络环境;
- 当需要限制Docker容器运行资源或执行用户指令等操作时,则通过execdriver来完成。
- 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
。
- docker server:
- Docker Server相当于C/S架构的服务端。功能为接受并调度分发Docker Client发送的请求。接受请求后,Server通过路由与分发调度,找到相应的Handler来执行请求。
- Docker的启动过程中,会创建了一个mux.Router,提供请求的路由功能。该mux.Router中添加了众多的路由项,每一个路由项由HTTP请求方法(PUT、POST、GET或DELETE)、URL、Handler三部分组成。创建完mux.Router之后,Docker将Server的监听地址以及mux.Router作为参数,创建一个httpSrv=http.Server{},最终执行httpSrv.Serve()为请求服务。
- 在Server的服务过程中,Server在listener上接受Docker Client的访问请求,并创建一个全新的goroutine来服务该请求。在goroutine中,首先读取请求内容,然后做解析工作,接着找到相应的路由项,随后调用相应的Handler来处理该请求,最后Handler处理完请求之后回复该请求。
Engine
- Engine是Docke的运行引擎,同时也是Docker运行的核心模块。它扮演Docker container存储仓库的角色,并且通过执行job的方式来操纵管理这些容器。
- 在Engine数据结构的设计与实现过程中,有一个handler对象。该handler对象存储的都是关于众多特定job的handler处理访问。
job
一个Job可以认为是Docker架构中Engine内部最基本的工作执行单元。Docker可以做的每一项工作,都可以抽象为一个job。
Docker Registry
Docker Registry就是镜像注册中心,用户如果不进行配置的话默认的地址是dokcerhub地址,这个大家应该都知道。
Graph
Repository
- 已下载镜像的保管者(包括下载镜像和dockerfile构建的镜像)。
- 镜像的存储类型有aufs,devicemapper,Btrfs,overlay2等。docker info中的
Storage Driver
- 同时在Graph的本地目录中,关于每一个的容器镜像,具体存储的信息有:该容器镜像的元数据,容器镜像的大小信息,以及该容器镜像所代表的具体rootfs。
GraphDB
已下载容器镜像之间对应关系的记录者。
Driver
Driver是Docker架构中的驱动模块。通过Driver驱动,Docker可以实现对Docker容器执行环境的定制。即Graph负责镜像的存储,Driver负责容器的执行。
- graphdriver
- graphdriver主要用于完成容器镜像的管理,包括存储与获取。值得注意的地方是,不同文件系统的存储驱动器也不相同,比如redhat系的操作系统使用的是overlay2,通过xfs文件系统来管理;如果是Ubuntu则使用的是aufs;如果是suse则是btrfs。建议不要修改默认的docker storage driver,跟随系统默认。
- 存储:docker pull下载的镜像由graphdriver存储到本地的指定目录(Graph中)。
- 获取:docker run(create)用镜像来创建容器的时候由graphdriver到本地Graph中获取镜像。
networkdriver
networkdriver的用途是完成Docker容器网络环境的配置,其中包括
- Docker启动时为Docker环境创建网桥;
- Docker容器创建时为其创建专属虚拟网卡设备;
- Docker容器分配IP、端口并与宿主机做端口映射,设置容器防火墙策略等。
- execdriver
- execdriver作为Docker容器的执行驱动,负责创建容器运行命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。
- 现在execdriver默认使用native驱动,不依赖于LXC。
libcontainer
libcontainer是golang编写的第三方库,主要封装了一些linux内核与容器相关的API;因为屏蔽了所有内核操作,保证了容器与底层的松耦合性
Docker可以直接调用libcontainer,而最终操纵容器的namespace、cgroups、apparmor、网络设备以及防火墙规则等。
参考资料:
《Docker源码分析》