Docker源码分析:docker架构

一、Docker总架构

Docker对用户而言是一个简单的C/S架构,用户通过客户端与服务器端建立通信,而Docker的后端是一个松耦合的架构,架构中的模块各司其职、有机组合,支撑着Docker运行。

docker架构中主要的模块有:DockerClient、DockerDaemon、Docker Registry、Graph、Driver、libcontainer以及Docker Container。

Docker客户端是用户与Docker交互的主要方式,用户通过命令行或者图形界面与Docker API通信,向Docker Daemon发出请求来创建、管理、启动、停止或删除容器等。

Docker Daemon是运行在主机上的后台服务,负责接收来自Docker客户端的请求,管理本地的Docker对象(如镜像、容器、网络等),并执行这些请求。Docker Daemon内部所有的任务均由Engine来完成,且每一项工作都以一个Job的形式存在。

Driver是Docker用来与底层操作系统的存储和网络子系统交互的接口。不同的驱动程序可以实现不同的存储和网络后端,比如使用AUFS、OverlayFS等文件系统来实现镜像层的存储,使用bridge、overlay等网络驱动来实现容器之间的网络通信。

libcontainer是一套独立的容器管理解决方案,这套解决方案涉及了大量Linux内核方面的特性,如:namespaces、cgroups以及capabilities等。libcontainer很好地抽象了Linux的内核特性,并提供完整、明确的接口给Docker Daemon。

  总架构图

二、Docker Daemon

Docker Daemon是Docker架构中一个常驻在后台的系统进程。所谓的“运行Docker”,即代表运行Docker Daemon。总之,DockerDaemon的作用主要有以下两方面:1、接收并处理Docker Client发送的请求;2、管理所有的Docker容器。 

Docker Daemon运行时,会在后台启动一个Server,Server负责接收Docker Client发送的请求;接收请求后,Server通过路由与分发调度,找到相应的Handler来处理请求。Docker Daemon的架构大致可以分为三部分:Docker Server、Engine和Job。 Docker Daemon架构图:

1、Docker Sever

在Docker的启动过程中,通过包gorilla/mux(golang的类库解析),创建了一个mux.Router,提供请求的路由功能。在Golang中,gorilla/mux是一个强大的URL路由器以及调度分发器。该mux.Router中添加了众多的路由项,每一个路由项由HTTP请求方法(PUT、POST、GET或DELETE)、URL、Handler三部分组成。

若Docker Client通过HTTP的形式访问Docker Daemon,创建完mux.Router之后,Docker将Server的监听地址以及mux.Router作为参数,创建一个httpSrv=http.Server{},最终执行httpSrv.Serve()为请求服务。

在Server的服务过程中,Server在listener上接受Docker Client的访问请求,并创建一个全新的goroutine来服务该请求。在goroutine中,首先读取请求内容,然后做解析工作,接着找到相应的路由项,随后调用相应的Handler来处理该请求,最后Handler处理完请求之后回复该请求。

需要注意的是:Docker Server的运行在Docker的启动过程中,是靠一个名为”serveapi”的job的运行来完成的。原则上,Docker Server的运行是众多job中的一个,但是为了强调Docker Server的重要性以及为后续job服务的重要特性,将该”serveapi”的job单独抽离出来分析,理解为Docker Server。

2、Engine

Engine是Docker架构中的运行引擎,同时也是Docker运行的核心模块。Engine存储着大量的容器信息,同时管理着Docker大部分Job的执行。换言之,Docker中大部分任务的执行都需要Engine协助,并通过Engine匹配相应的Job完成Job的执行。在Docker源码中,有关Engine的数据结构定义中含有一个名为handlers的对象。该handlers对象存储的是关于众多特定Job各自的处理方式handler。举例说明,Engine的handlers对象中有一项为:{"create":daemon.ContainerCreate,},则说明当执行名为“create”的Job时,执行的是daemon.ContainerCreate这个handler。除了容器管理之外,Engine还接管Docker Daemon的某些特定任务。当Docker Daemon遭遇到自身进程需要退出的情况时,Engine还负责完成DockerDaemon退出前的所有善后工作

3、Job

Engine是Docker架构中的运行引擎,同时也是Docker运行的核心模块。Engine存储着大量的容器信息,同时管理着Docker大部分Job的执行。换言之,Docker中大部分任务的执行都需要Engine协助,并通过Engine匹配相应的Job完成Job的执行。在Docker源码中,有关Engine的数据结构定义中含有一个名为handlers的对象。该handlers对象存储的是关于众多特定Job各自的处理方式handler。举例说明,Engine的handlers对象中有一项为:{"create":daemon.ContainerCreate,},则说明当执行名为“create”的Job时,执行的是daemon.ContainerCreate这个handler。除了容器管理之外,Engine还接管Docker Daemon的某些特定任务。当Docker Daemon遭遇到自身进程需要退出的情况时,Engine还负责完成DockerDaemon退出前的所有善后工作

三、Docker Registry

Docker Registry是一个存储容器镜像(Docker Image)的仓库。容器镜像(Docker Image)是容器创建时用来初始化容器rootfs的文件系统内容。Docker Registry将大量的容器镜像汇集在一起,并为分散的Docker Daemon提供镜像服务。Docker的运行过程中,有三种情况可能与Docker Registry通信,分别为搜索镜像、下载镜像、上传镜像。这三种情况所对应的Job名称分别为search、pull和push。不同场景下,Docker Daemon可以使用不同的Docker Registry。公有Registry与私有Regsitry就是两种场景模式不同的Docker Registry。其中,大家熟知的Docker Hub,就是全球范围内最大的公有Registry。Docker可以通过互联网访问Docker Hub,并下载容器镜像;同时Docker也允许用户构建本地私有Registry,使容器镜像的获取在内网完成

四、Graph

Graph在Docker架构中扮演的角色是容器镜像的保管者。不论是Docker下载的镜像,还是Docker构建的镜像,均由Graph统一化管理。由于Docker支持多种不同的镜像存储方式,如aufs、devicemapper、Btrfs等,故Graph对镜像的存储也会因以上种类而存在一些差异。对Docker而言,同一种类型的镜像被称为一个repository,如名称为ubuntu的镜像都同属一个repository;而同一个repository下的镜像则会因tag存在差异而不同,如ubuntu这个repository下有tag为12.04的镜像,也有tag为14.04的镜像。Docker中Graph的架构如图1-4所示。[插图]图1-4 Graph架构示意图本书对Graph以及容器镜像(Docker Image)的分析将以aufs为主,并在第8章进行深入分析。

五、Driver

Driver是Docker架构中的驱动模块。通过Driver驱动,Docker可以实现对Docker容器执行环境的定制。由于Docker运行的生命周期中,并非用户所有的操作都是针对Docker容器的管理,另外还有关于Docker运行信息的获取,Graph的存储与记录等。因此,为了将Docker容器的管理从Docker Daemon内部业务逻辑中区分开来,设计了Driver层驱动来接管所有这部分请求。

在Docker Driver的实现中,可以分为以下三类驱动:graphdriver、networkdriver和execdriver。

1、graphdriver架构

graphdriver主要用于完成容器镜像的管理,包括存储与获取。即当用户需要下载指定的容器镜像时,graphdriver将容器镜像存储在本地的指定目录;同时当用户需要使用指定的容器镜像来创建容器的rootfs时,graphdriver从本地镜像存储目录中获取指定的容器镜像。

在graphdriver的初始化过程之前,有4种文件系统或类文件系统在其内部注册,它们分别是aufs、btrfs、vfs和devmapper。而Docker在初始化之时,通过获取系统环境变量”DOCKER_DRIVER”来提取所使用driver的指定类型。而之后所有的graph操作,都使用该driver来执行。

2、networkdriver架构

networkdriver的用途是完成Docker容器网络环境的配置,其中包括Docker启动时为Docker环境创建网桥;Docker容器创建时为其创建专属虚拟网卡设备;以及为Docker容器分配IP、端口并与宿主机做端口映射,设置容器防火墙策略等。

3、execdriver架构

execdriver作为Docker容器的执行驱动,负责创建容器运行命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。在execdriver的实现过程中,原先可以使用LXC驱动调用LXC的接口,来操纵容器的配置以及生命周期,而现在execdriver默认使用native驱动,不依赖于LXC。具体体现在Daemon启动过程中加载的ExecDriverflag参数,该参数在配置文件已经被设为”native”。这可以认为是Docker在1.2版本上一个很大的改变,或者说Docker实现跨平台的一个先兆。

六、libcontainer

libcontainer是Docker架构中一个使用Go语言设计实现的库,设计初衷是希望该库可以不依靠任何依赖,直接访问内核中与容器相关的API。

正是由于libcontainer的存在,Docker可以直接调用libcontainer,而最终操纵容器的namespace、cgroups、apparmor、网络设备以及防火墙规则等。这一系列操作的完成都不需要依赖LXC或者其他包。libcontainer架构如图

另外,libcontainer提供了一整套标准的接口来满足上层对容器管理的需求。或者说,libcontainer屏蔽了Docker上层对容器的直接管理。又由于libcontainer使用Go这种跨平台的语言开发实现,且本身又可以被上层多种不同的编程语言访问,因此很难说,未来的Docker就一定会紧紧地和Linux捆绑在一起。而于此同时,Microsoft在其著名云计算平台Azure中,也添加了对Docker的支持,可见Docker的开放程度与业界的火热度。暂不谈Docker,由于libcontainer的功能以及其本身与系统的松耦合特性,很有可能会在其他以容器为原型的平台出现,同时也很有可能催生出云计算领域全新的项目。

七、Docker container

Docker container(Docker容器)是Docker架构中服务交付的最终体现形式。

Docker按照用户的需求与指令,订制相应的Docker容器:

  • 用户通过指定容器镜像,使得Docker容器可以自定义rootfs等文件系统;
  • 用户通过指定计算资源的配额,使得Docker容器使用指定的计算资源;
  • 用户通过配置网络及其安全策略,使得Docker容器拥有独立且安全的网络环境;
  • 用户通过指定运行的命令,使得Docker容器执行指定的工作。

Docker容器示意图如图

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值