感谢团队的所有成员,正是有了他们的帮助,我们才能实现这一伟大里程碑,使Docker 1.12能够普遍用于生产环境。Docker 1.12版本将自Docker项目启动以来的最大且最复杂的一套新特性添加到一个单一版本中。许多工程师,包括Docker员工和外部参与者,为1.12 编排的各个方面,包括核心算法、集成Docker 引擎、文档以及测试做出了重大贡献。
我们非常感谢这个团队,他们帮助我们收集反馈、整理bug报告以及发现新的想法。特别是数以万计的Docker for Mac and Windows 的测试版用户自6月份DockerCon发布以来一直对我们的1.12版的特性进行测试,没有他们的帮助,我们不可能完成这个项目。我们看到了大家的贡献,从Bash Tab自动完成到用户体验投票,这些都帮助我们了解了用户的根本需求。同我们在DockerCon所公布的相比,我们已经取得了显著的改善:在swarm 节点加入工作流(更简便)、错误报告(更易于查看)、UX 改进(更合逻辑)、网络设计(修复了可靠性问题)等等。
在此,核心团队还特别感谢我们的一个外部维护人员兼Docker主管,Chanwit Kaewkasi,他亲历亲为,推动了DockerSwarm2000项目的进展,该项目集合整个团队,围绕1.12 的候选版本的swarm模式进行测试,运行了将近2.4k节点和近100K的容器。这一成就的实现,离不开我们的全球团体人员,他们允许我们对他们的不同形态、不同规格的计算机的访问,包括裸机、Raspberry Pis,各种云,从x86架构到基于ARM系统的虚拟机。通过使用实时数据进行评价,我们发现,仅仅半年时间,Docker中内置编排已经较其初次发布的版本的编排规模翻了一倍。虽然这一点证明了该架构的可扩展性,但未来在性能优化上仍然有上升空间。
接着,我们来深度了解一下这个新的内置编排架构,以及为什么我们选择了一个与其他容器编排方案截然不同的架构方法。
Swarm 模式结构拓扑
Docker 1.12中的内置容器编排是一个可选的特征集,包括打开一个swarm 模式的性能。Swarm是一个分散且高度可用的Docker节点组。每个节点都是一个自包含的编排子系统,都具有内在的特征,能够创建共同的资源库来调度Docker服务。
Docker节点的swarm创建一个可编程的拓扑结构,使操作者能够选择哪些节点的是主节点和哪些节点是工作节点。这包括常规配置,比如在多重可用区分布管理者。因为这些角色都是动态的,他们可以通过API或CLI随时更改。
管理阶段负责编排集群、服务Service API,调度任务(容器),提出未通过健康检查的容器以及其他事项。相比之下,工作节点的功能简单得多,用于执行启动容器的任务和为特定容器的数据流量选择路由。在生产环境中,我们强烈建议指定为“管理节点”或“工作节点”。这样管理节点不执行容器,从而减少了他们的负载和攻击界面。另外,swarm 模式的一个安全特性是,工作节点无法访问数据库或Service API 的信息。工作节点只能接收工作和报告状态。因此,一个工作节点对系统的损害将仅限于其所能损害的范围内。
我们的团队非常专注于为各节点之间的通信进行架构。管理节点和工作节点在一致性、速度和容量方面有不同的通信要求,因此他们使用两种不同的通信方法。Raft是用来共享管理节点之间的数据,以求高度的一致性(代价是写入速度和容量有限);而gossip是用于工作节点之间的通信,以求速度快和容量大(尽管只有最终的一致性)。而管理节点和工作节点之间的通信有不同的要求。但都有一个共同点是,他们有加密通信的默认设置:mTLS。
管理节点通信:Quorums总可用
当一个节点被定义为管理节点时,它就加入了一个Raft共识组来共享信息和进行主节点投票。主节点是维护状态的中央管理者,包括在swarm中处理节点、服务和任务列表,此外还可以制定调度决策。该状态通过一个内置的Raft存储被分配在每个管理节点。也就是说,管理节点并不依靠如etcd或Consul的外部键值存储,这是运算管理的一个较小的部件。非主节点的管理节点作为热备份,向当前主节点提出API要求。因此该系统具有容错性和高可用性。
一个集成的分布式数据存储,可以实现许多通用存储不能实现的优化,这将会使我们的内置编排系统极为快速。一个主要的优化是整个swarm状态被存储在内存中,从而可以即时读取。这种读取优化对一个关键的编排而言非常有益;调和了读取繁重的工作流的状态。通常情况下,调度程序必须执行数百个读取操作:读取节点列表、获取节点上运行的其他任务等等。有了读取的优化,速度得到了提升,减少了数百次读取往返到外部数据库的网络传输的需要。
写入数据对编排而言也很关键。对它的优化是,在swarm模式中一次网络数据交互中批量写入数据。一个常见的写入数据例子是当你扩展服务时,编排器要为用户请求的每个实例创建一个新对象。如果采用外部存储,我们需要为创建的每个对象向存储发送一个网络请求,等待他们保存写入并进行重复。每一次请求可能需要几十毫秒的时间,时间会累加起来(尤其是当你添加了数百个新实例的时候)。而采用我们的模型,我们可以把这数百个对象批量处理成一次写入操作。
同样,写入优化对系统弹性也有重大影响。例如,如果一个节点有100个容器需要关闭,可以通过一次写入数据操作,而不是执行100次写入来将它们移动到不同的节点。
最终的优化在于如何有效地保持数据的大小(协议缓冲)和性能(领域相关索引)。我们可以即时查询某个特定计算机上运行的容器内存,特定服务的不健康容器等等。
管理节点与工作节点间的通信
工作节点向管理节点通信使用GRPC,这是一个在恶劣的网络条件下运转良好的快速协议。它允许通过互联网链接(基于HTTP / 2)的通信,并且具有内嵌版本控制(这样运行不同的版本引擎的工作节点可以和同一个管理节点对话)。管理节点向工作节点发送运行任务集,工作节点向管理节点发送分配给他们的任务状态和一个心跳,这样管理节点可以确认工作节点还可用。
如下图所示,管理节点代码的调度器组件就是最终与工作节点通信的内容。它负责将任务分派给每一个工作节点,而工作节点(通过它的执行组件)负责将这些任务转换成容器并创建容器。
基于上面的图表,我们来简单地演练一下创建Docker服务并最终产生容器组的情况:
服务\创建
用户向API 发送服务定义。API接受并存储
编排器协调理想状态(用户定义的)与实际状态(当前在swarm上运行的)。它将接载由API创建的新服务,并通过创建一个任务(假设在这种情况下,用户只请求服务的一个实例)做出响应。
分配器为任务分配资源。它会识别新服务(由API创建)和新任务(由编排器创建),并为两者分配IP 地址。
调度程序负责将任务分配给工作节点。它将发现没有分配节点的任务,并开始调度。它尝试找到最好的匹配(以约束条件、资源等为基础),最终它将会把任务分配给这些节点。
分发器与工作节点相连。一旦工作节点连接到了分发器,他们就等待指令。同时,调度程序所分配的任务将会最终传递到工作节点。
服务\更新
用户通过API更新服务定义(如:从1个实例更改为3个实例)API接收并存储。
编排器协调理想状态和实际状态。它将发现如果用户想要3个实例,但只有1个实例在运行,则会通过创建另外两个任务对其作出回应。
分配器、调度程序和分发器将会按照上面所述执行同一步骤,并且将两个新任务分配给工作节点。
节点\失效
调度器将检测到连接到它的一个节点的失效情况(根据心跳)。它会把节点标记为DOWN。
编排器协调。应该有3个实例运行,但是其中一个失效。则通过创建一个新任务作出回应。
分配器、调度程序、和分发器将会按照上面所述执行同一步骤,并且两个新任务将会分配给新的工作节点。
工作节点使用一个Gossip网络进行通信,将覆盖网络信息传递给对方。Gossip是一个高容量、点对点(P2P)的网络,设计用于高扩展性工作。一个节点接受任务启动容器,然后通知其他的节点,该容器已经在特定的网络上起动。该通信广播发生在工作节点层。规模的实现是因为信息被传递给了一个固定数量的随机节点,而不是无论swarm的大小是多少都传给以同样方式工作的所有节点。
这到底意味着什么?
Docker 1.12编排对于开发商和运营商而言,到底意味着什么呢?在这个版本中有三个非常重要的主题,并且有上述丰富的架构:
容错应用程序部署平台。现代应用的设计越来越重视微服务的架构模式,在这种模式中,可能需要调用几个不同的服务以返回用户请求的数据。现实世界的计算机总出现故障,这些微服务需要在即使面对这样的随机故障时仍能继续提供服务。Docker 1.12通过提供一个zero-SPOF设计,影响管理节点数量再加上服务抽象。服务抽象能生成多个备份,并在主机不可用时迅速重新调度。
规模和性能。Docker 1.12的swarm 模式编排是从零开始设计,从一开始规模和性能就一直是重点。比如,内嵌Raft分布式存储已经进行优化,高速读取内存缓存层中的数据。缓存能够快速读取,那么写入操作时呢?当然在每台计算机上的缓存必须作废和更新。对于这个问题,我们的解决方案是设计编排系统的为读取-密集型,只有绝对必要时才写入Raft存储。这一设计决策的结合使编排系统能够比基于柴桂key-value存储的编排实现更好的性能。
安全网络。在许多系统中安全指的是你必须通过“开启”生成TLS证书,在不同的端口上运行系统,配置数据流,以确保没有数据包进入非加密网络。有了Docker 1.12,这些问题都解决了[*]。该系统是“默认安全”的,这意味着你不需要为获得一个安全的应用管理平台而成为安全专家。
评论前必须登录!
注册