? 近期很多朋友都在学习 docker, 笔者也是在慢慢学习中当大家在终端飞快的敲击 docker 的这些命令时:
- ??docker ps
- ??docker run
- ??docker image
- ??docker pull
- ??......
? 大家有没有思考过, 当使用这些命令时究竟发生了什么? 它是一个怎样的执行流程它的架构又是怎样呢? 一起来了解一下吧
Docker 架构
?Docker 使用了传统的 cs 架构模式(cilent-server), 架构图如下图所示用户通过 Docker client 与 Docker daemon 建立通信, 并将请求发送给后者而 Docker 的后端是松耦合结构, 不同模块各司其职, 有机组合, 完成用户的请求
? 从上图中可以看出, Docker daemon 是 Docker 架构中的主要用户接口首先, 它提供了 API Server 用于接收来自于 Docker client 的请求, 其后根据不同的请求分发给 Docker daemon 的不同模块执行相应的工作, 其中对容器运行时 volume 镜像以及网络方面的具体实现放在 daemon 以外的模块或项目中 (其中 libcontainerlibnetwork 已经成为单独项目, 独立于 docker 项目而 volimedriverdistributionregistrylayerimagereference 只是相对独立的代码模块) 值得注意的是, Docker 一直致力于将其进一步解耦, 削减 Docker daemon 的功能, 熟悉 Docker 早期版本的朋友在对照上面的架构图时一定要注意到了这种变化
?Docker 通过 driver 模块来实现对 Docker 容器执行环境的创建和管理:
当需要创建 Docker 容器时, 可通过镜像管理 (image management) 部分的 distribution 和 registry 模块从 Docker registry 中下载镜像, 并通过镜像管理的 imagerefernce 和 layer 存储镜像的元数据, 通过镜像存储驱动 graphdriver 将镜像文件存储于具体的文件中;
当需要为容器创建数据卷 volume 时, 通过 volume 模块来调用某个具体的 volumedriver, 来创建一个数据卷并负责后续的挂载操作;
当需要限制 Docker 容器运行资源或执行用户指令等操作时, 则通过 execdriver 来完成 libcontainer 是对 cgroups 和 namespace 的二次封装, execdriver 是通过 libcontainer 来实现对容器的具体管理, 包括利用 UTSIPCPIDNetwrokMountUser 等 namespace 实现容器之间的资源隔离和利用 cgoups 实现对容器的资源限制
? 当运行容器的命令执行完毕后, 一个实际的容器就处于运行状态, 该容器拥有独立的文件系统相对安全且相互隔离的运行环境 Docker 1.9 版本以后, volumenetwork 的生命周期都是独立于容器的, 与容器一样是 Docker 中的一等公民, Docker 用户可以单独增删改查 volume 或 network, 然后在创建容器的时候根据需要配置给容器
?? 下面对各个模块的功能进行介绍一下
Docker daemon
?Docker daemon 是 Docker 最核心的后台进程, 它负责响应来自 Docker client 的请求, 然后将这些请求翻译成系统调用完成容器管理操作该进程会在后台启动一个 API Server, 负责接收由 Docker client 发送的请求; 接收到的请求将通过 Docker daemon 分发调度, 再由具体的函数来执行请求
Docker client
?Docker client 是一个泛称, 用来向 Docker daemon 发起请求, 执行相应的容器管理操作它既可以是命令行工具 docker, 也可以是任何遵循了 Docker API 的客户端目前, 社区中维护着的 Docker client 种类非常丰富, 涵盖了包括 C# JavaGoRubyJavaScript 等常用语言, 甚至还有使用 Angular 库编写的 webUI 格式的客户端, 足以满足大多数据用户的需求
image management
?Docker 通过 distributionregistrylayerimagereference 等模块实现了 Docker 镜像的管理, 我们将这些模块统称为镜像管理 (image management) 在 Docker 1.10 以前的版本中, 这一功能通过 graph 组件来完成的下面简单介绍一下:
distribution 负责与 Docker registry 交互, 上传下载镜像以及存储 v2 registry 有关的元数据
registry 模块负责与 Docker registry 有关的身份验证镜像查找镜像验证以及管理 registry mirror 等交互操作
image 模块负责与镜像元数据有关的存储查找, 镜像层的索引查找以及镜像 tar 包有关的导入导出等操作
reference 负责存储本地所有镜像的 repository 和 tag 名, 并维护与镜像 ID 之间的映射关系
layer 模块负责与镜像层和容器层元数据有关的增删查改, 并负责将镜像层的增删查改操作映射到实际存储镜像层文件系统的 graphdriver 模块
execdrivervolumedrivergraphdriver
? 前面提到, Docker daemon 负责将用户请求转译成系统调用, 进而创建和管理容器而在具体实现过程中, 为了将这些系统调用抽象成为统一的操作接口方便调用者使用, Docker 把这些操作分成了容器执行驱动 volume 存储驱动镜像存储驱动这 3 种, 分别对应 execdrivervolumedrivergraphdriver:
execdriver 是对 Linux 系统的 namespacescgroupsapparmorSELinux 等容器运行所需的系统操作进行的一层二次封装, 其本质作用类似于 LXC, 但是功能要更全面这也就是为什么 LXC 会作为 execdriver 的一种实现而存在当然, execdriver 最主要的实现, 也是现在的默认实现, 是 Docker 官方编写的 libcontainer 库
volumedriver 是 volume 数据卷存储操作的最终执行者, 负责 volume 的增删改查, 屏蔽不同驱动实现的区别, 为上层调用者提供一个统一的接口 Docker 中作为默认实现的 volumedriver 是 local, 默认将文件存储于 Docker 根目录下的 volume 文件夹里其他的 volumedriver 均是通过外部插件实现的
graphdriver 是所有与容器镜像相关操作的最终执行者 graphdriver 会在 Docker 工作目录下维护一组与镜像层对应的目录, 并记下镜像层之间的关系以及与具体的 graphdriver 实现相关的元数据这样, 用户对镜像的操作最终会被映射成对应这些目录文件以及元数据的增删改查, 从而屏蔽掉不同文件存储实现对于上层调用者的影响在 Linux 环境下, 目前 Docker 已经支持 graphdriver 包括 aufsbtrfszfsoverlay 和 vfs
network
? 在 Docker 1.9 版本以前, 网络是通过 networkdriver 模块以及 libcontainer 库完成的, 现在这部分功能已经分离成一个 libnetwork 库独立维护了, 可参考 https://github.com/docker/libnetwork libnetwork 抽象出了一个容器网络模型 (Container Network Model,CNM), 并给调用者提供了一个抽象接口, 其目标不权限于 Docker 容器 CNM 模型对真实的容器网络抽象出了沙盒(sandbox) 端点 (endpoint) 网络 (network) 这 3 种对象, 由具体网络驱动 (包括内置的 BridgeHostNone 和 overlay 驱动以及通过插件配置的外部驱动) 操作对象, 并通过网络控制器提供一个统一接口供调用者管理网络网络驱动负责实现具体的操作, 包括创建容器通信所需的网络, 容器的 network namespace, 这个网络所需的虚拟网卡, 分配通信所需的 IP, 服务访问的端口和容器与宿主机之间的端口映射, 设置 hostsresolv.confiptables 等
来源: http://www.bubuko.com/infodetail-2507901.html