本文已参与「新人创作礼」活动,一起开启掘金创作之路。
作者按:
Docker使用已经比较久了,虽然一直在使用,但对于整体的架构以及一些源码级别的研究还是比较少,上度娘查找资料,发现很多也重复过时的。看到孙宏亮老师写的《Docker源码分析》,基于docker的版本是1.2.0,现在已经更新到1.6.8(截止2022年8月8日),虽然很多文章不是最新的,但对于我们这些小白,这些文章中透露的思维方式是值得学习的,也可以基于此基础之上,对于docker的认识更加深刻,下面就来研究一番。
1、 docker总体架构
docker是一个client+server的架构,所有命令通过RESTful API接口交互传递。
Docker后端是松耦合的结构,各模块各司其职,有机组合。整个架构中daemon是常驻后台运行的进程,接受客户端请求并管理docker容器。Client是命令行终端,包装命令发送api请求,engine是真正处理客户端请求的后端程序。
1、 docker client和darmon建立通信,client发送请求给damon
2、 daemon作为主体部分,提供server功能,能让其接受client的请求
3、 engine执行处理内部的一系列工作
4、每个工作是一个job形式存在,需要镜像时,从docker registry中下载,通过graphdrive镜像管理驱动下载镜像并存储(Graph形式)
5、networkdrive负责创建配置容器的网络
6、运行用户指令或者限制容器资源时,通过execdrive完成
7、execdrive以及networkdrive通过libcontainer具体实现
2、 各模块组件分析
Docker client发起请求
与docker daemon建立通信的客户端,使用docker命令后接参数形式完成一个完整请求(发送请求-处理请求-返回结果)
建立通信方式:tcp://host:port、 unix://path_to_socket 、fd://socketfd
Docker daemon 守护进程
Docker server 调度分发请求
接受请求之后,通过路由分发调度,找到对应的handler执行请求,启动过程中,是通过gorilla/mux,创建一个mux.Router,提供请求的路由功能。(gorilla/mux在golang是一个强大的url路由器以及调度分发器),然后将server的监听地址和router作为参数创建一个httpsrv.server()为请求服务,在srv服务过程中,创建一个goroutine读取路由,解析之后,找到相应路由项,调用相应的handler处理该请求,处理完成之后回复该请求
Engine
运行引擎、运行的和兴模块,扮演的是docker container存储仓库的角色,通过job方式管理容器
Job
docker Engine内部最基本的工作执行单位,每一项工作抽象为一个job
docker registry
是一个存储容器镜像的仓库,在运行过程中与registry通信,实现搜索、下载、上传镜像的工作,对应job名称:search、pull、push(可分为公有和私有仓库)
Graph
内部数据库
Repository
镜像保管者(下载和构建的)
存储类型有aufs、devicemapper、Btrfs、Vfs,centos中使用devicemapper存储类型
GraphDB
容器镜像关系记录者
构建在SQLIte之上得到小型图数据库,实现节点命名以及节点之间关联关系的记录。
Drive
驱动模块,通过driver实现对容器执行环境的定制
Graphdrive
主要用于完成容器镜像的管理例如存储获取
Pull下载的镜像由graphdrive存储到本地graph获取镜像,也由graphdriver到本地Graph获取镜像
Networkdriver
启动时为docker环境创建网桥,容器创建时创建虚拟网卡、容器分配ip、端口与宿主机做端口映射、设置容器防火墙观策略
Execdriver
作为容器的执行驱动,创建命名空间,负责资源统计限制,负责进程运行、默认使用native驱动
Libcontainer
使用go语言设计实现的库,不依靠任何依赖,直接访问内核中容器相关的API
Docker可以直接调用libcontainer,来操作容器namespace、cgroups、apparmor、网络等
Docker container
Docker容器是服务交付的最终体现形式,可以按照用户需求定制相应容器。
3、代码结构
Api:定义api,使用swagger2.0这个工具生成API,文件在api/swagger.yaml
Builder:用来构建镜像的包
Cli:使用cobra工具生成docker客户端命令行解析器
Client:接受cli的请求,调用RESTful API 接口,向server端发送http请求
Cmd:包含一个dockerd包,包含客户端服务端main函数入口
Container:容器配置管理,适配不同的平台,容器的抽象
Contrib:杂物堆堆,包含一些有用的脚本、镜像、还有其他非docker core中的部分
Daemon:暴露docker运行时状态
Distribution:用来docker镜像的pull、push和镜像仓库的维护
Dockerversion:编译时自动生成
Docs:文档
Errdefs:一些常见错误
Hack:创建docker开发环境和编译打包时用到的的脚本和配置文件
Image:用于构建docker镜像
Intergration-cli:集成测试
Layer:管理union file system driver上的read-only和read-write mounts,各种layer fs的抽象
Libcontainerd:访问内核中容器相关系统的调用函数库
Oci:open container interface 库
Opts:命令行选项库
Pkg:类似utils或者helps
Plugin:docker插件后端实现包,插件相关
Profile:有apparmor和seccomp两个目录,用于内核访问控制
Project:项目管理说明文档
Reference:处理docker store里镜像的reference(参考)
Registry:docker registry仓库实现
Restartmanager:处理重启后的动作,是否设置“always”
Runconfig:配置格式解码和校验
Vendor:各种依赖包
Volume:docker volume的实现
至此,对于docker大体的架构有了一定的了解。明白Docker设计并不是分布式,是单机的,是c/s模式。Docker是建立在linux容器之上的,但docker用libcontainer取代了LXC,docker不是容器,是有效管理容器而开发的一种技术或工具,docker也不是虚拟机、也不是配置管理系统。
注意:containerd负责管理容器,runc负责使用来自containerd的输入来运行容器
Docker引擎有docker守护出、rest接口和dockercli组成
今天的学习就到此结束,祝学习顺利!
参考:《docker源码分析》