Docker架构

目录

Docker可以帮助用户在容器内部快速自动化部署应用,并利用Linux内核特性命名空间(namespaces)及控制组(cgroups)等为容器提供隔离的运行环境。Docker借助操作系统层的虚拟化实现资源的隔离,因此Docker容器在运行时与虚拟机(VM)的运行有很大的区别,Docker容器与宿主机共享同一个操作系统,不会有额外的操作系统开销。这样的优势很明显,因而大大提高了资源利用率,并且提升了I/O等方面的性能。

Docker总架构图

Docker的总架构如图1-1所示。架构中主要的模块有:DockerClient、DockerDaemon、Docker Registry、Graph、Driver、libcontainer以及Docker Container。

对用户而言,Docker Client是与Docker Daemon建立通信的最佳途径。用户通过Docker Client发起容器的管理请求,请求最终发往Docker Daemon。

Docker Daemon作为Docker架构中的主体部分,首先具备服务端的功能,有能力接收Docker Client发起的请求;其次具备Docker Client请求的处理能力。Docker Daemon内部所有的任务均由Engine来完成,且每一项工作都以一个Job的形式存在。

Docker Daemon需要完成的任务很多,因此Job的种类也很多。若用户需要下载容器镜像,Docker Daemon则会创建一个名为"pull"的Job,运行时从Docker Registry中下载镜像,并通过镜像管理驱动graphdriver将下载的镜像存储在graph中;若用户需要为Docker容器创建网络环境,Docker Daemon则会创建一个名"allocate_interface"的Job,通过网络驱动networkdriver分配网络接口的资源......

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

当用户执行运行容器这个命令之后,一个Docker容器就处于运行状态,该容器拥有隔离的运行环境、独立的网络栈资源以及受限的资源等。

Docker Client

Docker Client是Docker架构中用户与Docker Daemon建立通信的客户端。在一台安装有Docker的机器上,用户可以使用可执行文件docker作为Docker Client,发起众多Docker容器的管理请求。

Docker Client可以通过以下三种方式和Docker Daemon建立通信,分别为:

  • tcp://host:port、
  • unix://path_to_socket
  • fd://socketfd。

为简单起见,本书主要使用第一种方式作为讲述两者通信的原型。通信方式确定后,DockerClient与Docker Daemon建立连接并传输请求时,可以通过命令行flag参数的形式,设置安全传输层协议(TLS)的有关参数,保证传输的安全性。

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

Docker Daemon

Docker Daemon是Docker架构中一个常驻在后台的系统进程。所谓的"运行Docker",即代表运行Docker Daemon。总之,DockerDaemon的作用主要有以下两方面:

·接收并处理Docker Client发送的请求。

·管理所有的Docker容器。

Docker Daemon运行时,会在后台启动一个Server,Server负责接收Docker Client发送的请求;接收请求后,Server通过路由与分发调度,找到相应的Handler来处理请求。

启动Docker Daemon所使用的可执行文件同样是docker,与Docker Client启动所使用的可执行文件docker相同。既然Docker Client与Docker Daemon都可以通过docker二进制文件创建,那么如何辨别两者就变得非常重要。实际上,执行docker命令时,通过传入的参数可以辨别Docker Daemon与Docker Client,如docker--d代表Docker Daemon的启动,dockerps则代表创建Docker Client,并发送ps请求。

Docker Daemon的架构大致可以分为三部分:Docker Server、Engine和Job。Daemon的架构如图1-2所示。

Docker Server

Docker 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退出前的所有善后工作。

Job

Job可以认为是Docker架构中Engine内部最基本的工作执行单元。DockerDaemon可以完成的每一项工作都会呈现为一个Job。例如,在Docker容器内部运行一个进程,这是一个Job;创建一个新的容器,这是一个Job;在网络上下载一个文档,这是一个Job;包括之前在Docker Server部分谈及的,创建Server服务于HTTP协议的API,这也是一个Job,等等。

有关Job接口的设计,与UNIX进程非常相仿。比如说,Job有一个名称,有运行时参数,有环境变量,有标准输入与标准输出,有标准错误,还有返回状态等。

对于Job而言,定义完毕之后,运行才能完成Job自身真正的使命。Job的运行函数Run()则用以执行Job本身。

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的graphdriver负责镜像本地的管理和存储以及对运行中的容器生成镜像等工作。(与registry不同)

Driver

Driver是Docker架构中的驱动模块。通过Driver驱动,Docker可以实现对Docker容器运行环境的定制,定制的维度主要有网络环境、存储方式以及容器执行方式。需要注意的是,Docker运行的生命周期中,并非用户所有的操作都是针对Docker容器的管理,同时包括用户对Docker运行信息的获取,还包括Docker对Graph的存储与记录等。因此,为了将仅与Docker容器有关的管理从Docker Daemon的所有逻辑中区分开来,Docker的创造者设计了Driver层来抽象不同类别各自的功能范畴。

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

GraphDriver

graphdriver主要用于完成容器镜像的管理,包括从远程Docker Registry上下载镜像并进行存储,也包括本地构建完镜像后的存储。当用户下载指定的容器镜像时,graphdriver将容器镜像分层存储在本地的指定目录下;同时当用户需要使用指定的容器镜像来创建容器时,graphdriver从本地镜像存储目录中获取指定的容器镜像,并按特定规则为容器准备rootfs;另外,当用户需要通过指定Dockerfile构建全新镜像时,graphdriver会负责新镜像的存储管理。

在graphdriver的初始化过程之前,有4种文件系统或类文件系统的驱动Driver在DockerDaemon内部注册,它们分别是aufs、btrfs、vfs和devmapper。其中,aufs、btrfs以及devmapper用于容器镜像的管理,vfs用于容器volume的管理。Docker在初始化之时,优先通过获取系统环境变量"DOCKER_DRIVER"来提取所使用driver的指定类型。因此,之后所有的Graph操作,都使用该driver来执行。Docker镜像是Docker技术中非常关键的。2014年12月,在Linux 3.18-rc2版本中OverlayFS被合并至Linux内核主线,在Docker 1.4.0版本发布时,Docker官方宣布支持overlay这一类graphdriver,即用户在启动Docker Daemon时可以选择制定graphdriver的类型为overlay。graphdriver的架构如图1-5所示。

NetworkDriver

networkdriver的作用是完成Docker容器网络环境的配置,其中包括Docker Daemon启动时为Docker环境创建网桥;Docker容器创建前为其分配相应的网络接口资源;以及为Docker容器分配IP、端口并与宿主机做NAT端口映射,设置容器防火墙策略等。networkdriver的架构如图1-6所示。

ExecDriver

execdriver作为Docker容器的执行驱动,负责创建容器运行时的命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。在Docker 0.9.0版本之前,execdriver只能通过LXC驱动来实现容器的启动管理。实际上,当时Docker通过LXC驱动调用Linux下的LXC工具管理容器的创建,并控制管理容器的生命周期。从Docker 0.9.0开始,在继续支持LXC的情况下,Docker的execdriver默认使用native驱动,native驱动完全独立于LXC,属于Docker项目下第一个全新的子项目,用于容器的创建与管理。Docker默认使用native驱动的具体体现为:Docker Daemon启动过程中加载的ExecDriverflag参数在配置文件中已经被设为native。native这个execdriver的存在,使得Docker对Linux容器的创建与管理有了自己的解决方案。execdriver架构如图1-7所示。

Libcontainer

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

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

另外,libcontainer提供了一整套标准的接口来满足上层对容器管理的需求。或者说,libcontainer屏蔽了Docker上层对容器的直接管理。又由于libcontainer使用Go这种跨平台的语言开发实现,且本身又可以被上层多种不同的编程语言访问,因此,很难说未来的Docker一定会与Linux平台紧紧捆绑在一起。Docker Daemon的逻辑完全有可能位于其他非Linux操作系统的平台上,仅仅通过libcontainer的远程调用来实现对Docker容器的管理。另一方面,libcontainer与Docker Daemon的松耦合设计,似乎让用户感受到了除Linux Container之外其他的容器技术接入Docker Daemon的可能性。libcontainer承接Linux内核与Docker Daemon的同时,也让Docker的生态在跨平台方面充满生机。与此同时,Microsoft在其著名云计算平台Azure中,也添加了对Docker的支持,可见Docker的开放程度与业界的火热度。

暂不谈Docker,由于本身完善的功能以及与应用系统的松耦合特性,libcontainer很有可能会在众多其他以容器为原型的平台出现,同时也很有可能催生出云计算领域全新的项目。

Docker Container

Docker Container(Docker容器)是Docker架构中服务交付的最终体现形式。Docker通过DockerDaemon的管理,libcontainer的执行,最终创建Docker容器。Docker容器作为一个交付单位,功能类似于传统意义上的虚拟机(Virtual Machine),具备资源受限、环境与外界隔离的特点。然而,实现手段却与KVM、Xen等传统虚拟化技术大相径庭。

Docker容器的从无到有,涉及Docker利用到的很多技术。总而言之,用户可以根据自己的需求,通过Docker Client向Docker Daemon发送容器的创建与启动请求,请求中将携带容器的配置信息,从而达到定制相应Docker容器的目的。用户对Docker容器的配置有以下4个基本方面:

  • ·通过指定容器镜像,使得Docker容器可以自定义rootfs等文件系统。
  • ·通过指定物理资源的配额,如CPU、内存等,使得Docker容器使用受限的物理资源。
  • ·通过配置容器网络及其安全策略,使得Docker容器拥有独立且安全的网络环境。
  • ·通过指定容器的运行命令,使得Docker容器执行指定的任务。

Docker容器示意图如图1-9所示。

相关推荐
THMAIL1 小时前
mac M芯片运行docker-desktop异常问题
macos·docker·容器
Kookoos1 小时前
Redis + ABP vNext 构建分布式高可用缓存架构
redis·分布式·缓存·架构·c#·.net
GISer_Jing2 小时前
React底层架构深度解析:从虚拟DOM到Fiber的演进之路
前端·react.js·架构
两点王爷3 小时前
IDEA中springboot项目中连接docker
spring boot·docker·intellij-idea
家庭云计算专家3 小时前
还没用过智能文档编辑器吗?带有AI插件的ONLYOFFICE介绍
服务器·人工智能·docker·容器·编辑器
slam小白白白4 小时前
轨迹误差评估完整流程总结(使用 evo 工具)
eureka
孤的心了不冷5 小时前
【Docker】CentOS 8.2 安装Docker教程
linux·运维·docker·容器·eureka·centos
头疼的程序员6 小时前
docker学习与使用(概念、镜像、容器、数据卷、dockerfile等)
学习·docker·容器
IT小郭.6 小时前
使用 Docker Desktop 安装 Neo4j 知识图谱
windows·python·sql·docker·知识图谱·database·neo4j
淡水猫.6 小时前
hbit资产收集工具Docker(笔记版)
运维·docker·容器