[docker](三)docker-client模式和daemon模式

docker项目GitHub源码

client模式

Docker命令对应的源文件是docker/docker.go,它的使用方式如下:

docker [OPTIONS] COMMAND [arg ...]

其中OPTIONS参数称为flag,任何时候执行一个docker命令,Docker都需要先解析这些flag,然后按照用户声明的COMMAND向子命令执行对应的操作。

如果子命令为daemon,Docker就会创建一个运行在宿主机的daemon进程(docker/daemon.go#mainDaemon),即执行daemon模式。其余子命令都会执行client模式。处于client模式下的docker命令工作流程包含如下几个步骤。

1、解析flag信息

这里列出几个client模式比较重要的OPTIONS

  • Debug,对应-D和--debug参数,这个flag用于启动调试模式
  • LogLevel,对应-l和--log-level参数,默认等级是info,可选参数有:panic、error、warn、info、debug。
  • Hosts,对应-H和--host=[]参数,对于client模式,就是指本次操作需要连接的Docker daemon位置,而对于daemon模式,则提供所要监听的地址。若Hosts变量或者系统环境变量DOCKER_HOST不为空,说明用户指定了host对象;否则使用默认设定,默认情况下Linux系统设置为unix:///var/run/docker.sock.
  • protoAddrParts,这个信息来自于-H参数中://前后的两部分组合,即与Docker daemon建立通信的协议方式与socket地址。

2、创建client实例

client的创建就是在已有配置参数信息的基础上,调用api/client/cli.go#NewDockerCli,需要设置好proto(传输协议)、addr(host的目标地址)和tlsConfig(安全传输层协议的配置),另外还会配置标准输入输出及错误输出。

3、执行具体的命令

Docker client对象创建成功后,剩下的执行具体命令的过程就交给cli/cli.go来处理。

daemon模式

Docker运行时如果使用docker daemon 子命令,就会运行Docker daemon。一旦docker进入了daemon模式,剩下的初始化和启动工作就都由Docker的docker/daemon.go#CmdDaemon来完成。

Docker daemon通过一个server模块(api/server/server.go)接收来自client的请求,然后根据请求类型,交由具体的方法去执行。

下面是Docker daemon启动与初始化过程的详细解析

1、API Server的配置和初始化过程

首先,在docker/daemon.go#CmdDaemon中,Docker会继续按照用户的配置完成server的初始化并启动它。这个server为API Server,就是专门负责响应用户请求并将请求交给daemon具体方法去处理的进程。它的启动过程如下。

(I)整理解析用户指定的各项参数。

(2)创建PID文件。

(3)加载所需的serve峭助配置,包括日志、是否允许远程访问、版本以及TLS认证信息等。

(4)根据上述server配置,加上之前解析出来的用户指定的server配置(比如Hosts ),通过goroutine的方式启动API Server。这个server监听的socket位置就是Hosts的值。

(5)创建一个负责处理业务的daemon对象(对应daemon/damone.go)作为负责处理用户请求的

逻辑实体。

(6)对APIserver中的路由表进行初始化,即将用户的请求和对应的处理函数相对应起来。

(7)设置一个channel,保证上述goroutine只有在server出错的情况下才会退出。

(8)设置信号捕获,当Docker daemon进程收到INT, TERM, QUIT信号时,关闭API Server,调用shutdownDaemon停止这个daemon。

(9)如果上述操作都成功,API ServergjG会与上述daemon绑定,并允许接受来自client的连接。

(10)最后,Docker daemon进程向宿主机的init守护进程发送“READY=1”信号,表示这个Docker daemon已经开始正常工作了。

2、daemon对象的创建与初始化过程

这个过程需要完成的配置至少包括了如下功能点:Docker容器的配置信息、检测系统支持及用户权限、配置工作路径、加载并配置graphdriver、创建Docker网络环境、创建并初始化镜像数据库、创建容器管理驱动、检测DNS配置和加载已有Docke溶器等。

  • Docker容器的配置信息
 - 设置默认的网络最大传输单元:当用户没有对一mt参数进行指定时,则将其设置为1500。
 - 检测网桥配置信息:此部分配置为进一步配置Docker网络提供铺垫。
  • 检测系统支持及用户权限

初步处理完Docker的配置信息之后,Docker对自身运行的环境进行了一系列的检测,主要包括3个方面。

 - 操作系统类型对Docker daemon的支持,目前Docker daemon只能运行在Linux系统上。
 - 用户权限的级别,必须是root权限。
 - 内核版本与处理器的支持,只支持amd64架构的处理器,且内核版本必须升至3.10.0及以上。
  • 配置工作路径

配置Docker daemon的工作路径,主要是创建Docker daemon运行中所在的工作目录,默认为/var/lib/docker。若该目录不存在,则会创建,并赋予0700权限。

  • 配置Docker容器所需的文件环境
 - 第一,创建容器配置文件目录。Docker daemon在创建Docker容器之后,需要将容器内的配置文件放到这个目录下统一管理。目录默认位置为:/var/lib/docker/containers,它下面会为每个具体容器保存如下几个配置文件,其中xxx为容器ID,这些配置文件里包含了这个容器的所有元数据。
 - 第二,配置graphdriver目录。它用于完成Docker容器镜像管理所需的底层存储驱动层。所以,在这一步的配置工作就是加载并配置镜像存储驱动graphdriver,创建存储驱动管理镜像层文件系统所需的目录和环境,初始化镜像层元数据存储。
 - 第三,配置镜像目录。主要工作是在Docker主目录下创建一个image目录,来存储所有镜像和镜像层管理数据,默认目录为‘`/var/lib/docker/image/"。在image目录下,每一个graphdriver都有一个具体的目录用于存储使用该graphdrive存储的镜像相关的元数据。
 - 第四,调用volume/local/local.go#New创建volume驱动目录(默认为/lvar/lib/docker/volumes ),Docker中volume是宿主机上挂载到Docke溶器内部的特定目录。
 - 第五,准备“可信镜像”所需的工作目录。在Docker工作根目录下创建trust目录。这个存储目录可以根据用户给出的可信url加载授权文件,用来处理可信镜像的授权和验证过程。
 - 第六,创建distributionMetadataStore和referenceStoreoreferenceStore用于存储镜像的仓库列表。记录镜像仓库的持久化文件位于Docker根目录下的image/[graphdriver]/repositories.json中,主要用于做镜像ID与镜像仓库名之间的映射。distributionMetadataStore存储与第二版镜像仓库registry有关的元数据,主要用于做镜像层的diff id与registry中镜像层元数据之间的映射。
 - 第七,将持久化在Docker根目录中的镜像、镜像层以及镜像仓库等的元数据内容恢复到daemon的imageStore, layerStore和referenceStore中。
 - 第八,执行镜像迁移。

综上,这里Docker daemon需要在Docker根目录(/var/lib/docker)下创建并初始化一系列跟容器文件系统密切相关的目录和文件。这些文件和目录的具体作用为:

docker文件作用.png
  • 创建Docker网络环境

创建Docker daemon运行环境的时候,创建网络环境是极为重要的一个部分。这不仅关系着容器对外的通信,同样也关系着容器间的通信。网络部分早已被抽离出来作为一个单独的模块,称为libnetworko libnetwork通过插件的形式为Docker提供网络功能,使得用户可以根据自己的需求实现自己的driver来提供不同的网络功能。

  • 初始化execdriver

execdrive提Docker中用来管理Docker容器的驱动。Docker调用execdrivers中的NewDriver()函数来创建新的execdriver。

在创建execdriver的时候,需要注意以下5部分信息。

 - 运行时中指定使用的驱动类别,在默认配置文件中默认使用native,即其对应的容器运行时为libcontainer;
 - 用户定义的execdriver选项,即-exec-opt参数值;
 - 用户定义的-exec-root参数值,Docker execdriver运行的root路径,默认为/var/run/docker;
 - Docker运行时的root路径,默认为/var/lib/docker ;
 - 系统功能信息,包括容器的内存限制功能、交换区内存限制功能、数据转发功能以及AppArmo按全功能等。
  • 创建容器管理驱动

Docker daemon进程在经过以上诸多设置以及创建对象之后,最终创建出了daemon对象实例,其属性总结如下。

 - ID:根据传人的证书生成的容器ID,若没有传人则自动使用ECDSA加密算法生成。
 - repository:部署所有Docke溶器的路径。
 - containers:用于存储具体Docke溶器信息的对象。
 - execCommands: Docker容器所执行的命令。
 - referenceStore:存储Docker像仓库名和镜像D的映射。
 - distributionMetadata5tore:v2版registry相关的元数据存储。
 - trustKey:可信任证书。
 - idIndex:用于通过简短有效的字符串前缀定位唯一的镜像。
 - sysInfo: Docker所在宿主机的系统信息。
 - configStore: Docker}h需要的配置信息。
 - execDriver: Docker容器执行驱动,默认为native类型。
 - statsCollector:收集容器网络及cgroup状态信息。
 - defaultLogConfig:提供日志的默认配置信息。
 - registryService:镜像存储服务相关信息。
 - EventsService:事件服务相关信息。
 - volumes: volume所使用的驱动,默认为local类型。
 - root : Docker运行的工作根目录。
 - uidMaps: uid的对应图。
 - gidMaps: gid的对应图。
 - seccompEnabled:是否使用seccomputeo
 - nameIndex:记录键和其名字的对应关系。
 - linkIndex:容器的link目录.记录容器的link关系。
  • 恢复已有的Docker容器

当Docker daemon启动时,会去查看在daemon.repository也就是在/var/lib/docker/containers中的内容。若有已经存在的Docker容器,则将相应信息收集并进行维护,同时重启restart policy为always的容器。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏戚银技术成长之路

【Go】strings.Replace 与 bytes.Replace 调优

标准库中函数大多数情况下更通用,性能并非最好的,还是不能过于迷信标准库,最近又有了新发现,strings.Replace 这个函数自身的效率已经很好了,但是在特...

5110
来自专栏GitHubDaily

GitHub 标星破万,超全 Chrome 插件汇总整理!

今天跟大家分享 GitHub 上一个非常棒的开源项目。打开这个开源项目,你就仿佛打开了潘多拉的盒子,里面还有更多惊喜。至少目前,这个项目已经集合了数十个可用在 ...

13320
来自专栏学院君的专栏

Go 语言网络编程系列(四)—— HTTP 编程篇:http.Client 底层实现剖析

除了上篇教程介绍的基本 HTTP 操作,Go 语言标准库也提供了比较底层的 HTTP 相关库,让开发者可以基于这些库灵活定制 HTTP 服务器并使用 HTTP ...

12240
来自专栏Rust语言学习交流

「推荐」从openresty谈到rust

本文转载自知乎:https://zhuanlan.zhihu.com/p/87922545?utm_source=wechat_session&utm_medi...

12830
来自专栏3D可视化

3D可视化开发之使用QGIS修改GeoJSON数据的心得

       最近也是在使用ThingJS制作智慧城市可视化应用,发现ThingJS的CityBuilder使用起来还是特别方便的,但是直接将GeoJSON.i...

30740
来自专栏小诚信驿站

Gin-Web-Framework官方指南中文(下篇)

ShouldBind,ShouldBindJSON,ShouldBindXML,ShouldBindQuery,ShouldBindYAML

14240
来自专栏学院君的专栏

Go 语言网络编程系列(三)—— HTTP 编程篇:客户端如何发起请求

通过前面介绍的 net.Dial 或 net.DialTimeout 函数来访问基于 HTTP 协议的网络服务是完全没有问题的,因为 HTTP 协议是基于 TC...

8120
来自专栏AI科技评论

开发 | TensorFlow 2.0开发者测试版来啦,正式版推出指日可待

在过去的几年里,在 GitHub 社区的高效参与下,TensorFlow 开发团队审查了 RFC,添加了许多新功能,实现了 TensorFlow 2.0 的大部...

9380
来自专栏戚银技术成长之路

【Go】深入剖析slice和array

array 和 slice 看似相似,却有着极大的不同,但他们之间还有着千次万缕的联系 slice 是引用类型、是 array 的引用,相当于动态数组, 这些都...

7130
来自专栏SH的全栈笔记

两分钟让你明白Go中如何继承

最近在重构代码的时候,抽象了大量的接口。也使用这些抽象的接口做了很多伪继承的操作,极大的减少了代码冗余,同时也增加了代码的可读性。

8150

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励