Docker详细介绍(3)

一、Docker总架构图

        Docker对使用者来讲是一个C/S模式的架构,而Docker的后端是一个非常松耦合的架构,模块各司其职,并有机组合,支撑Docker的运行。

Docker架构内各模块的功能与实现分析。

主要的模块有:Docker ClientDocker DaemonDocker RegistryGraphDriverlibcontainer以及Docker container

1-1、Docker Client

        Docker Client是Docker架构中用户用来和Docker Daemon建立通信的客户端。用户使用的可执行文件为docker,通过docker命令行工具可以发起众多管理container的请求。

        Docker Client可以通过以下三种方式和Docker Daemon建立通信:tcp://host:port,unix://path_to_socket和fd://socketfd。与此同时,与Docker Daemon建立连接并传输请求的时候,Docker Client可以通过设置命令行flag参数的形式设置安全传输层协议(TLS)的有关参数,保证传输的安全性。

        Docker Client发送容器管理请求后,由Docker Daemon接受并处理请求,当Docker Client接收到返回的请求相应并简单处理后,Docker Client一次完整的生命周期就结束了。当需要继续发送容器管理请求时,用户必须再次通过docker可执行文件创建Docker Client。

        当需要发送容器管理请求(Docker Client一次完整的生命周期)

        (1)用户通过docker可执行文件创建Docker Client

        (2)Docker Client 发送容器管理请求后,由Docker Daemon接受并处理请求

        (3)Docker Client 接收到返回的请求并简单处理、

Docker Client 与Docker Daemon建立通信的三种方式:

        (1)tcp://host:port

        (2)unix://path_to_socket

        (3)fd://socketfd

备注:

        Docker Client与Docker Daemon建立连接并传输请求的时候,可以通过设置命令行flag参数的形式设置安全传输层协议(TLS)的有关参数,保证传输的安全性。

1-2、Docker Daemon

        Docker Daemon主要是用于接收并处理Docker Client发送的请求,在Docker架构中是一个常驻在后台的系统进程。

        是Docker架构中一个常驻在后台的系统进程,接受并处理Docker Client发送的请求。

        该守护进程在后台启动了一个Server,Server负责接受Docker Client发送的请求;接受请求后,Server通过路由与分发调度,找到相应的Handler来执行请求。

        Docker Daemon启动所使用的可执行文件与Docker Client启动所使用的可执行文件docker相同,但在docker命令执行时,可通过传入的参数来判别Docker Daemon与Docker Client。

        Docker Daemon的架构,大致可以分为以下三部分:Docker Server、Engine和Job。Daemon架构如图4.1。

(图4.1 Docker Daemon架构示意图)

1-2-1、Docker Server

        Docker Server 接收并调度分发Docker Client发送的请求(专门服务于Docker Client的server)。

备注:

        Docker Server本质上也是众多job的一个,该job命名为“serveapi”。但为了强调Docker Server的重要性及为后续job服务的独特性,将该“serveapi”的job单独抽离出来分析,并将其理解为Docker Server。

        Docker Server在Docker架构中是专门服务于Docker Client的server。该server的功能是:接受并调度分发Docker Client发送的请求。Docker Server的架构如图4.2。

(图4.2 Docker Server架构示意图)

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

        其内部创建完mux.Router之后,会将Server的监听地址及mux.Router进行封装,创建一个httpSrv=http.Server{},最终执行httpSrv.Serve()为请求服务。

备注:

        (1)mux.Router创建时机。在Docker的启动过程中,通过包gorilla/mux,创建了一个mux.Router,提供请求的路由功能

        (2)mux.Router是一个强大的URL路由器及调度分发器。该mux.Router中添加了众多的路由项,每一个路由项由HTTP请求方法(PUT、POST、GET或DELETE)、URL、Handler三部分组成。

        在Docker的启动过程中,通过包gorilla/mux,创建了一个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。

1-2-2、Engine

        Engine是Docker架构中的运行引擎。它扮演Docker container存储仓库的角色,并且通过执行job的方式来操纵管理这些容器。

        在Engine数据结构的设计与实现过程中,有一个handler对象。该handler对象存储的都是关于众多特定job的handler处理访问。

        举例说明,Engine的handler对象中有一项为:{"create": daemon.ContainerCreate,},则说明当名为"create"的job在运行时,执行的是daemon.ContainerCreate的handler。

1-2-3、Job

        一个Job可以认为是Docker架构中Engine内部最基本的工作执行单元。Docker可以做的每一项工作,都可以抽象为一个job。

        例如:在容器内部运行一个进程,这是一个job;创建一个新的容器,这是一个job,从Internet上下载一个文档,这是一个job;包括之前在Docker Server部分说过的,创建Server服务于HTTP的API,这也是一个job,等等。

        Job的设计者,把Job设计得与Unix进程相仿。比如说:Job有一个名称,有参数,有环境变量,有标准的输入输出,有错误处理,有返回状态等。

1-3、Docker Registry

        Docker Registry是一个存储容器镜像的仓库。而容器镜像是在容器被创建时,被加载用来初始化容器的文件架构与目录。

        在Docker的运行过程中,Docker Daemon会与Docker Registry通信,并实现搜索镜像、下载镜像、上传镜像三个功能,这三个功能对应的job名称分别为"search","pull" 与 "push"。

        其中,在Docker架构中,Docker可以使用公有的Docker Registry,即大家熟知的Docker Hub,如此一来,Docker获取容器镜像文件时,必须通过互联网访问Docker Hub;同时Docker也允许用户构建本地私有的Docker Registry,这样可以保证容器镜像的获取在内网完成。

1-4、Graph

        Graph在Docker架构中扮演已下载容器镜像的保管者,以及已下载容器镜像之间关系的记录者。

        一方面,Graph存储着本地具有版本信息的文件系统镜像,另一方面也通过GraphDB记录着所有文件系统镜像彼此之间的关系。

(图4.3 Graph架构示意图)

备注:

        (1)GraphDB是一个构建在SQLite之上的小型图数据库,实现了节点的命名以及节点之间关联关系的记录。它仅仅实现了大多数图数据库所拥有的一个小的子集,但是提供了简单的接口表示节点之间的关系。

        (2)在Graph的本地目录中,关于每一个的容器镜像,具体存储的信息有:该容器镜像的元数据,容器镜像的大小信息,以及该容器镜像所代表的具体rootfs。

1-5、Driver

        Driver是Docker架构中的驱动模块。通过Driver驱动,Docker可以实现对Docker容器执行环境的定制。

备注:

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

在Docker Driver的实现中,可以分为以下三类驱动:graphdrivernetworkdriverexecdriver

1-5-1、graphdriver

        主要用于完成容器镜像的管理,包括存储与获取。

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

备注:

在graphdriver的初始化过程之前,有4种文件系统或类文件系统在其内部注册,它们分别是aufs、btrfs、vfs和devmapper。

而Docker在初始化之时,通过获取系统环境变量”DOCKER_DRIVER”来提取所使用driver的指定类型。而之后所有的graph操作,都使用该driver来执行。

graphdriver的架构如图4.4:

(图4.4 graphdriver架构示意图)

1-5-2、networkdriver

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

networkdriver的架构如图4.5:

(图4. 5 networkdriver架构示意图)

1-5-3、execdriver

        作为Docker容器的执行驱动,负责创建容器运行命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。

(图4.6 execdriver架构示意图)

备注:

        在execdriver的实现过程中,原先可以使用LXC驱动调用LXC的接口,来操纵容器的配置以及生命周期,而现在execdriver默认使用native驱动,不依赖于LXC。具体体现在Daemon启动过程中加载的ExecDriverflag参数,该参数在配置文件已经被设为"native"。

1-6、libcontainer

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

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

(图4.7 libcontainer示意图)

1-7、Docker container

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

Docker容器示意图如图4.8:

(图4.8 Docker容器示意图)

备注:

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

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

二、Docker运行案例分析

2-1、docker pull

        docker pull命令的作用为:从Docker Registry中下载指定的容器镜像,并存储在本地的Graph中,以备后续创建Docker容器时的使用。docker pull命令执行流程如图5.1。

(图5.1 docker pull命令执行流程示意图)

        如图,图中标记的红色箭头表示docker pull命令在发起后,Docker所做的一系列运行。以下逐一分析这些步骤。

        (1) Docker Client接受docker pull命令,解析完请求以及收集完请求参数之后,发送一个HTTP请求给Docker Server,HTTP请求方法为POST,请求URL为"/images/create? "+"xxx";

        (2) Docker Server接受以上HTTP请求,并交给mux.Router,mux.Router通过URL以及请求方法来确定执行该请求的具体handler;

        (3) mux.Router将请求路由分发至相应的handler,具体为PostImagesCreate;

        (4) 在PostImageCreate这个handler之中,一个名为"pull"的job被创建,并开始执行;

        (5) 名为"pull"的job在执行过程中,执行pullRepository操作,即从Docker Registry中下载相应的一个或者多个image;

        (6) 名为"pull"的job将下载的image交给graphdriver;

        (7) graphdriver负责将image进行存储,一方创建graph对象,另一方面在GraphDB中记录image之间的关系。

2-1、docker run

        docker run命令的作用是在一个全新的Docker容器内部运行一条指令。

        Docker在执行这条命令的时候,所做工作可以分为两部分:

                (1)创建Docker容器所需的rootfs

                (2)创建容器的网络等运行环境,并真正运行用户指令

        因此,在整个执行流程中,Docker Client给Docker Server发送了两次HTTP请求,第二次请求的发起取决于第一次请求的返回状态。Docker run命令执行流程如图5.2。

(图5.2 docker run命令执行流程示意图)

        如图,图中标记的红色箭头表示docker run命令在发起后,Docker所做的一系列运行。以下逐一分析这些步骤。

        (1) Docker Client接受docker run命令,解析完请求以及收集完请求参数之后,发送一个HTTP请求给Docker Server,HTTP请求方法为POST,请求URL为"/containers/create? "+"xxx";

        (2) Docker Server接受以上HTTP请求,并交给mux.Router,mux.Router通过URL以及请求方法来确定执行该请求的具体handler;

        (3) mux.Router将请求路由分发至相应的handler,具体为PostContainersCreate;

        (4) 在PostImageCreate这个handler之中,一个名为"create"的job被创建,并开始让该job运行;

        (5) 名为"create"的job在运行过程中,执行Container.Create操作,该操作需要获取容器镜像来为Docker容器创建rootfs,即调用graphdriver;

        (6) graphdriver从Graph中获取创建Docker容器rootfs所需要的所有的镜像;

        (7) graphdriver将rootfs所有镜像,加载安装至Docker容器指定的文件目录下;

        (8) 若以上操作全部正常执行,没有返回错误或异常,则Docker Client收到Docker Server返回状态之后,发起第二次HTTP请求。请求方法为"POST",请求URL为"/containers/"+container_ID+"/start";

        (9) Docker Server接受以上HTTP请求,并交给mux.Router,mux.Router通过URL以及请求方法来确定执行该请求的具体handler;

        (10) mux.Router将请求路由分发至相应的handler,具体为PostContainersStart;

        (11) 在PostContainersStart这个handler之中,名为"start"的job被创建,并开始执行;

        (12) 名为"start"的job执行完初步的配置工作后,开始配置与创建网络环境,调用networkdriver;

        (13) networkdriver需要为指定的Docker容器创建网络接口设备,并为其分配IP,port,以及设置防火墙规则,相应的操作转交至libcontainer中的netlink包来完成;

        (14) netlink完成Docker容器的网络环境配置与创建;

        (15) 返回至名为"start"的job,执行完一些辅助性操作后,job开始执行用户指令,调用execdriver;

        (16) execdriver被调用,初始化Docker容器内部的运行环境,如命名空间,资源控制与隔离,以及用户命令的执行,相应的操作转交至libcontainer来完成;

        (17) libcontainer被调用,完成Docker容器内部的运行环境初始化,并最终执行用户要求启动的命令。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sun cat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值