我们感谢社区的每一位成员帮助我们顺利到达Docker1.12这个里程碑,这个版本已经为生产环境做好了准备。1.12版本是自Docker项目开始以来特性增加最大也是最先进的一个版本。包括Docker员工和外部贡献者在内的数十位工程师对1.12编排的做出了重大贡献,包括核心算法、集成到Docker Engine,文档和测试。
我们非常感谢社区,他们提供了很多反馈、bug和新的思路。这次的成功离不开数万名Mac和Windows的Docker测试用户对1.12特性的测试。这些贡献小到bash中tab自动补全,大到UX中启动和关闭选举,帮助我们理解用户所最需要的功能。比起我们在DockerCon中揭幕的内容,我们针对swarm节点加入工作流、错误报告、UX改进、网络部分做出了显著地改进。
核心团体特别感谢一位外部的社区维护者和主要贡献者,Chanwit Kaewkasi,他利用工作之便,推动DockerSwarm2000项目,围绕1.12的RC版本进行测试,并启动了约24000个节点并运行了100000个容器。这一壮举依靠我们全球社区得以实现,他们贡献了各式各样的机器,有树莓派、x86架构的云虚拟机,还有ARM系统。通过这次实际数据的测试,我们认为Docker内置编排工具在半年内达到了初次发布时的两倍。尽管这次验证了该架构的扩展性,依然存在很大的性能优化空间。
xxxxxxxxxxxx
接下来,深入了解内嵌的编排架构以及为什么我们采用了一个与其他容器编排方案不同的架构。
###Swarm模型拓扑架构
xxxxxxxxx
在Docker 1.12中内置容器编排是一个可选特性,包括了打开一个swarm节点的功能。一个swarm是一个分散并高可用的Docker节点群组。每一个节点是一个自包含的容器编排子系统,它拥有全部的内在特性用于创建一个常规资源池来调度Docker服务。
一个Docker节点swarm创建一个可编程的拓扑结构,用于选择哪些节点是主节点和哪些节点为工作节点。这包括常规配置,例如跨多个可用zone的分布式管理者。由于这些角色是动态的,他们可以通过API或CLI随时进行更改。
管理节点需要负责集群的编排、服务API的运行、调度任务、定位健康检查出错的容器等等。相对的,工作节点运行的功能比较简单,它需要执行任务来启动容器、路由数据流到指定容器。在生产环境中,我们强烈推荐节点仅定义为管理节点
或工作节点
二者之一。这样管理节点就不需要运行容器,以降低节点的负载。另外,在swarm节点的安全方面,工作节点没有权限获取数据仓库信息或者服务API信息。工作节点仅接收任务和状态报告。并且,工作节点对系统的破坏影响受限于它的工作范围内。
我们团队相当注意架构中各节点间的通信。管理节点和工作节点在内容一致性、速度和容量方面有不同的通信需求;因此,他们采用两种不同的通信方法。Raft用于管理节点之间对高一致性要求的数据通信;gossip用于工作节点之间对快速通信和大容量的通信场景。管理节点和工作节点之间的通信有单独的要求。这些要求中的共同点就是对消息的加密处理,默认采用mTLS。
###管理节点间通信:固定数可用
当一个节点被定义为管理节点,它加入一个Raft组交换信息并进行主节点投票。主节点是中心管理,掌握状态信息,包括节点列表、swarm中的服务和任务,以及制定调度策略。状态可由每个管理节点通过内嵌的Raft存储进行分布式获取。也就是说,管理节点没有类似etcd或Consul那样的外部key-value存储依赖。非主节点的管理节点扮演者热备份和转发API请求到当前主节点的角色。因此这个系统具有容错性且高可用性。
相对于使用一个外部通用存储,内嵌一个分布式数据存储可以实现许多优化,以致内嵌编排系统的速度可以非常快。一个主要的优化就是整个swarm的状态存储在内存中,便于即时读取。这种读取优化对编排的益处非常大。调度器需要进行数百种读取操作:读取节点列表、获取工作节点上运行了哪些任务等。通过这种优化,在速度上得到提升,减少了数百次依赖网络传输的外部数据库信息交互。
对编排来讲,写数据操作也是很重要的。对它的优化主要是:swarm节点可以在一次网络数据交互中批量写数据。一个常见的写数据场景是当扩展服务时,编排器需要对用户请求的每一个实例创建一个对象。如果采用外部存储,我们需要对每个创建的对象发送网络数据请求,然后等待消息反馈并写数据,如此反复。每个请求这可能花掉数十毫秒,并线性增加(特别是增加了数百个实例之后)。采用我们的模型,我们可以将这数百个请求批量处理到一次写操作中。
写操作优化对系统弹性也有重要影响。例如,当一个节点有100个容器需要关闭,可以通过一次数据写操作来代替100次写操作来移除它们。
最终的优化是关于如何有效的进行数据一致性处理,包括协议缓冲单元大小和领域相关索引的性能处理。我们可以从内存中实时查询某个特定机器上的容器、某个特定服务的不健康容器等等。
xxxxxxxxxxx
###管理节点到工作节点的通信
工作节点需要采用gRPC与管理节点通信。这是一个在严格的网络条件下可以很好的工作的快速通信协议,协议可以通过网络链路(基于HTTP/2)和内嵌变体版本(确保运行了不同版本Engine的工作节点可以与同一个管理节点通信)进行交互。管理节点发送任务到工作节点上运行。工作节点发送给管理节点任务状态和心跳信息,确保管理节点知道工作节点还可用。
下图描述了管理节点的分发模块如何与工作节点交互。它负责分发任务给每个工作节点,工作节点负责启动和创建相应容器。
xxxxxxxxx
基于上图,我们简要的理一下Docker服务创建和产生相应容器组的过程:
-
服务创建
- 用户发送服务定义到API。API接收并存储
- 编排器确定目标状态和实际状态。它将通过API创建一个新的服务,并通过创建任务来做出响应。
- 分配器分配资源给任务。它将识别由API创建的新服务和新任务,并分配给它们IP地址。
- 调度器负责分配任务给工作节点。它将发现没有分配节点的任务并开始调度。它将尝试找到最佳匹配的节点,最后分配任务给这些节点。
- 分发器与工作节点相连。一旦工作节点链接到分发器,它们等待指示。同时,由调度器下发的任务将传递给工作节点。
-
服务更新
- 用户通过API更新服务定义。API接受并存储。
- 编排器确认目标状态和实际状态。它将发现如果用户需要3个实例,但是只有1个在运行,那么将会创建2个额外的任务。
- 分配器,调度器和分发器将如上面流程中一样去执行两个新任务到工作节点。
-
节点失败
- 分发器将探测链接到它的节点失败情况。它将标志节点为DOWN状态。
- 编排器确认需要3个实例运行,但有1个失败了,它将创建一个新的任务。
- 分配器,调度器和分发器将如上面流程中一样去执行1个新任务到工作节点.
xxxxxxxxxxxx
工作节点之间用Gossip网络进行通信。Gossip是一种高容量、p2p网络,被设计用于高扩展性的场景。一个节点接受了任务,启动容器并通知其他节点该容器已经在特定网络启动。该通信在工作节点层广播。规模的实现是因为信息被传递给固定数量的随机节点,而不是相同工作方式的全部节点,并与swarm的规模无关。
###上面这些到底意味着什么?
Docker 1.12的编排到底对开发者和使用者意味着什么呢?这次的发布中有三条非常重要的主题:
- 应用部署平台的容错。现代应用程序在快速增长,它们被设计为微服务架构模式,该模式中需要调用多个不同的服务以返回用户的请求数据。真实世界中的机器会出现失败的情况,但这些服务需要继续服务以面对不同的失败。Docker 1.12提供了zero-SPOF的能力设计实现一组管理节点和服务抽象,它创建多个冗余备份并在主机不可用时进行快速调度。
- 规模与性能。Docker 1.12的swarm节点编排被设计用于大规模和高性能场景。例如,内嵌Raft分布式存储用于优化为从内存缓存层中快速读取数据。缓存提供快速读取,但是如何处理写操作呢?当然每台机器上的缓存必须作废和更新。我们的解决方案是设计编排系统为
读密集
类型,只有必须进行写操作时才写到Raft存储。设计策略的综合使得编排系统可以发挥更好的性能,超过那些采用外部常规key-value存储的系统。 - 安全网络。在很多系统中,安全意味着你必须采用TLS授权、运行系统在不同端口、配置数据流以确保没有数据包进入非加密网络。在Docker 1.12中,上述事情已经全解决了,该系统默认是安全的,也就是说不需要对应用管理平台的安全而成为安全专家。
[*] 还有一个小的例外,对于网络流量,目前Docker版本要求你去手动设定标志-o encrypted
(在docker network create -d overlay
)来开启加密。对去其他流量,加密是默认启用的。
更多Docker 1.12 Swarm 模式的深入分析: