巅峰对决之Swarm、Kubernetes、Mesos
另外一篇 https://www.sohu.com/a/157185937_287582
Docker
Docker是一个主流容器管理工具,它是第一个基于Linux容器(LXC)的[2],但是现在被runC[46]所取代了(runC是是一个由Open Containers Initiative开发的CLI工具,它能够创建和运行容器[36])。Docker容器支持分层的文件系统,因此它能够和宿主机共享系统内核。这个特性意味着即便一个Docker镜像基于一个1GB的操作系统,在同一个主机上运行10个容器实例并不需要消耗10GB的空间,相比之下,每一台虚拟机都需要一个完整的1GB操作系统。
Docker的镜像可以理解为一个操作系统的快照。如果你想要创建一个新的镜像,你需要启动一个基础镜像,然后做一些修改,最后提交修改,形成新的镜像。这些镜像能够发布在私有或者公有的库上[10]供其他开发者使用,开发者只需要将镜像pull下来即可。
使用镜像可以非常方便的创建操作系统的快照,并且使用它们来创建新的容器,这些功能非常的轻量和易用,这些都是Docer CLI和商业模式的核心。[12]
容器包含了所有运行所需要的东西(代码,运行时,系统工具,库),因此Docker给予开发者一个轻量的,稳定的环境来快速地进行创建和运行作业。
容器调度简介(Description of container schedulers)
容器调度工具的主要任务就是负责在最合适的主机上启动容器,并且将它们关联起来。它必须能够通过自动的故障转移(fail-overs)来处理错误,并且当一个实例不足以处理/计算数据时,它能够扩展容器来解决问题。
这篇文章比较了三个主流容器调度框架:Docker Swarm ,Apache Mesos (running the Marathon framework) 和 Google Kubernetes]
1.Docker Swarm
Docker Swarm是一个由Docker开发的调度框架。由Docker自身开发的好处之一就是标准Docker API的使用[17]。Swarm的架构由两部分组成:
Docker Swarm architecture, ©Alexandre Beslic (Docker Inc.) [14]
其中一个机器运行了一个Swarm的镜像(就像运行其他Docker镜像一样),它负责调度容器[4],在图片上鲸鱼代表这个机器。Swarm使用了和Docker标准API一致的API,这意味着在Swarm上运行一个容器和在单一主机上运行容器使用相同的命令。尽管有新的flags可用,但是开发者在使用Swarm的同时并不需要改变他的工作流程。
Swarm由多个代理(agent)组成,把这些代理称之为节点(node)。这些节点就是主机,这些主机在启动Docker daemon的时候就会打开相应的端口,以此支持Docker远程API[5]。其中三个节点显示在了图上。这些机器会根据Swarm调度器分配给它们的任务,拉取和运行不同的镜像。
当启动Docker daemon时,每一个节点都能够被贴上一些标签(label),这些标签以键值对的形式存在,通过标签就能够给予每个节点对应的细节信息。当运行一个新的容器时,这些标签就能够被用来过滤集群,具体的细节在后面的部分详谈。
策略(Strategies)
Swarm采用了三个策略(比如说,策略可以是如何选择一个节点来运行容器)[22]:
策略名:节点选择
- spread:最少的容器,并且忽视它们的状态
- binpack:最拥挤(比如说,拥有最少数量的CPU/RAM)
- random:随机选择
如果多个节点被选中,调度器会从中随机选择一个。在启动管理器(manager)时,策略需要被定义好,否则“spread”策略会被默认使用。
过滤器(Filters)
为了在节点子集中调度容器,Swarm提供了两个节点过滤器(constraint和health),还有三个容器配置过滤器(affinity,dependency和port)。
约束过滤器(Constraint filter)
每一个节点都关联有键值对。为了找都某一个关联多个键值对的节点,你需要在docker daemon启动的时候,输入一系列的参数选项。当你在实际的生产环境中运行容器时,你可以指定约束来完成查找,比如说一个容器只会在带有环境变量key=prod的节点上运行。如果没有节点满足要求,这个容器将不会运行。
一系列的标准约束已经被设置,比如说节点的操作系统,在启动节点时,用户并不需要设置它们。
健康过滤器(Health filter)
健康过滤器用来防止调度不健康的节点。在翻看了Swarm的源代码后,只有少量关于这个概念的信息是可用的。
吸引力过滤器(Affinity filter)
吸引力过滤器是为了在运行一个新的容器时,创建“吸引力”。涉及到容器、镜像和标签的吸引力存在有三类。
对容器来说,当你想要运行一个新的容器时,你只需要指定你想要链接的容器的名字(或者容器的ID),然后这些容器就会互相链接。如果其中一个容器停止运行了,剩下的容器都会停止运行。
镜像吸引力将会把想要运行的容器调度到已经拥有该镜像的节点上。
标签吸引力会和容器的标签一起工作。如果想要将某一个新的容器紧挨着指定的容器,用户只需要指定一个key为container,value为<container_name>的吸引力就可以了。
吸引力和约束的语法接受否定和软强制(soft enforcement),即便容器不可能满足所有的需求。[18]
依赖过滤器(Dependency filter)
依赖过滤器能够用来运行一个依赖于其他容器的容器。依赖意味着和其他容器共享磁盘卷,或者是链接到其他容器,亦或者和其他容器在同一个网络栈上。
端口过滤器(Port filter)
如果你想要在具有特定开发端口的节点上运行容器,你就可以使用端口过滤器了。如果集群中没有任何一个节点该端口可用的话,系统就会给出一个错误的提示信息。
2.Apache Mesos & Mesosphere Marathon
Mesos的目的就是建立一个高效可扩展的系统,并且这个系统能够支持很多各种各样的框架,不管是现在的还是未来的框架,它都能支持。这也是现今一个比较大的问题:类似Hadoop和MPI这些框架都是独立开的,这导致想要在框架之间做一些细粒度的分享是不可能的。[35]
因此Mesos的提出就是为了在底部添加一个轻量的资源共享层(resource-sharing layer),这个层使得各个框架能够适用一个统一的接口来访问集群资源。Mesos并不负责调度而是负责委派授权,毕竟很多框架都已经实现了复杂的调度。
取决于用户想要在集群上运行的作业类型,共有四种类型的框架可供使用[52]。其中有一些支持原生的Docker,比如说Marathon[39]。Docker容器的支持自从Mesos 0.20.0就已经被加入到Mesos中了[51]。
我们接下来将会重点关注如何在让Mesos和Marathon一起工作,毕竟Marathon主要是由Mesosphere维护[41],并且提供了很多关于调度的功能,比如说约束(constraints)[38],健康检查(health checks)[40],服务发现(service discovery)和负载均衡(load balancing)[42]。
Apache Mesos architecture using Marathon, © Adrian Mouat [49]
我们可以从图上看到,集群中一共出现了4个模块。ZooKeeper帮助Marathon查找Mesos master的地址[53],同时它具有多个实例可用,以此应付故障的发生。Marathon负责启动,监控,扩展容器。Mesos maser则给节点分配任务,同时如果某一个节点有空闲的CPU/RAM,它就会通知Marathon。Mesos slave运行容器,并且报告当前可用的资源。
约束(Constraints)
约束使得操作人员能够操控应用在哪些节点上运行,它主要由三个部分组成:一个字段名(field name)(可以是slavve的hostname或者任何Mesos slave属性),一个操作符和一个可选的值。5个操作符如下:
操作符:角色(role)
- UNIQUE:使得属性唯一,比如说越苏[“hostname”,”UNIQUE”]使得每个host上只有一个应用在运行。
- CLUSTER:使得运行应用的slaves必须共享同一个特定属性。比如说约束 [“rack id”, “CLUSTER”, “rack-1”] 强制应用必须运行在rack-1上,或者处于挂起状态知道rack-1有了空余的CPU/RAM。
- GROUP_BY:根据某个特性的属性,将应用平均分配到节点上,比如说特定的host或者rack。
- LIKE:使得应用只运行在拥有特定属性的slaves上。尽管只有CLUSTER可用,但由于参数是一个正则表达式,因此很多的值都能够被匹配到。
- UNLIKE:和LIKE相反。
健康检查(Health checks)
健康检查是应用依赖的,需要被手动实现。这是因为只有开发者知道他们自己的应用如何判断健康状态。(这是一个Swarm和Mesos之间的不同点)
Mesos提供了很多选项来声明每个健康检查之间需要等待多少秒,或者多少次连续的健康检查失败后,这个不健康的任务需要被终结。
服务发现和负载均衡(Service discovery and load balancing)
为了能够发送数据到正在运行的应用,我们需要服务发现。Apache Mesos提供了基于DNS的服务发现,称之为Mesos-DNS[44],它能够在多个框架(不仅仅是Marathon)组成的集群中很好的工作。
如果一个集群只由运行容器的节点组成,Marathon足以承当起管理的任务。在这种情况下,主机可以运行一个TCP的代理,将静态服务端口的连接转发到独立的应用实例上。Marathon确保所有动态分配的服务端口都是唯一的,这种方式比手动来做好的多,毕竟多个拥有同样镜像的容器需要同一个端口,而这些容器可以运行在同一个主机上。
Marathon提供了两个TCP/HTTP代理。一个简单的shell脚本[37]还有一个更复杂的脚本,称之为marathon-ld,它拥有更多的功能[43]。
3.Google Kubernetes
Kubernetes是一个Docker容器的编排系统,它使用label和pod的概念来将容器换分为逻辑单元。Pods是同地协作(co-located)容器的集合,这些容器被共同部署和调度,形成了一个服务[28],这是Kubernetes和其他两个框架的主要区别。相比于基于相似度的容器调度方式(就像Swarm和Mesos),这个方法简化了对集群的管理.
Kubernetes调度器的任务就是寻找那些PodSpec.NodeName为空的pods,然后通过对它们赋值来调度对应集群中的容器[32]。相比于Swarm和Mesos,Kubernetes允许开发者通过定义PodSpec.NodeName来绕过调度器[29]。调度器使用谓词(predicates)[29]和优先级(priorites)[30]来决定一个pod应该运行在哪一个节点上。通过使用一个新的调度策略配置可以覆盖掉这些参数的默认值[33]。
命令行参数plicy-config-file可以指定一个JSON文件(见附录A)来描述哪些predicates和priorities在启动Kubernetes时会被使用,通过这个参数,调度就能够使用管理者定义的策略了。
Kubernetes architecture (containers in grey, pods in color), © Google Inc. [31]
谓词(Predicates)
谓词是强制性的规则,它能够用来调度集群上一个新的pod。如果没有任何机器满足该谓词,则该pod会处于挂起状态,知道有机器能够满足条件。可用的谓词如下所示:
- Predicate:节点的需求
- PodFitPorts:没有任何端口冲突
- PodFitsResurce:有足够的资源运行pod
- NoDiskConflict:有足够的空间来满足pod和链接的数据卷
- MatchNodeSelector:能够匹配pod中的选择器查找参数。
- HostName:能够匹配pod中的host参数
优先级(Priorities)
如果调度器发现有多个机器满足谓词的条件,那么优先级就可以用来判别哪一个才是最适合运行pod的机器。优先级是一个键值对,key表示优先级的名字,value就是该优先级的权重。可用的优先级如下:
- Priority:寻找最佳节点
- LeastRequestdPriority:计算pods需要的CPU和内存在当前节点可用资源的百分比,具有最小百分比的节点就是最优的。
- BalanceResourceAllocation:拥有类似内存和CPU使用的节点。
- ServicesSpreadingPriority:优先选择拥有不同pods的节点。
- EqualPriority:给所有集群的节点同样的优先级,仅仅是为了做测试。
结论
以上三种框架提供了不同的功能和归来来自定义调度器的逻辑。从这节来看,显而易见,由于Swarm的原生API,Swarm是三个中最容易使用的。
以Docker的方式来运行容器[15]意味着一个容器是短暂存在的,并且每一个容器只运行一个进程。根据这条原则,多个容器提供一个服务或者代表一个应用是极度正常的。
因此编排和调度容器成为了最应当解决的问题,这也解释了为什么,即便这项技术不是很成熟,但仍有那么多的调度器被开发出来,并且提供了不同的功能和选项。