如果你有看笔者最近的所有文章,可能脑袋里反反复复都是Kubernetes,Kubernetes,Kubernetes,即便你不是全职在科技行业,我相信你肯定遇到过Kubernetes这个Buzzword。Kubernetes并不是空穴来风,如果对Kubernetes这样的容器PASS平台进行抽象,那么映入你眼帘的是一幅编排系统的模型,而编排系统并不是Kubernetes首创,笔者在正式介绍Kubernetes之前,想通过这篇文章,来讨论一个更加通用的模式:编排系统。
Kubernetes本身就是一套实现良好的编排系统,而说到编排系统,你可能会问,什么样的系统叫编排系统,或者说编排系统有哪些基础的组件?这些组件之前是如何交互最终形成大规模应用部署的能力?如果我们要设计这样一套系统,需要考虑哪些要点,以及要做哪些取舍?带着这些问题,我们正式进入今天的文章。
我们先来介绍一下没有容器平台Docker和容器编排平台Kubernetes之前的系统部署和运维是怎么样的,以期让大家能更好的理解编排系统具体要解决哪些问题。时间回到2002年,我们的主人公Julia是一家顶级电商公司的系统管理员,她的主要职责就是确保电商平台24小时正常运行,那么她是如何做到这一点的呢?和同时代的所有系统管理员类似,Julia负责的所有应用都是直接部署到物理机上,一个极度简化后的系统部署架构图如下所示:
如上图所示,需要运维的系统包含了多个应用和组件,让情况更糟的是,每个系统运行的操作系统和硬件都不太一样,并且因为这些应用是不同的团队在不同的时间构建,采用的技术完全不一样,因此给Julia的运维工作增加了极大的复杂度。
造成复杂度的不光是异构系统的维护,还需要大量的复杂的流程来确保无论是硬件还是软件进行版本更新,都不会造成系统停机,但是不造成系统停机在哪个年代,几乎是一种奢望,因此你会看到在重大的促销季节来临之前,开发和运维团队夜以继日的工作,以期能把最新版本和更加强大的硬件服务器给部署上去,而这项工作,只能在晚上进行。
如果你没有类似的经历,那么就要恭喜你了。随着技术的发展,虚拟化逐步成熟并统治了企业级部署方案,企业级的虚拟化套件可以帮助Julia在一台性能强劲的机器上创建多个虚拟机,这些虚拟机之间相互隔离,共同分享宿主机提供的硬件资源。
由于采用这种虚拟化技术,部署在数据中心的应用程序不需要单独的物理机,资源的使用率得到了优化和提升,企业在基础设施上的投入也相应的得到了优化,Julia由于引入了这些虚拟化技术,帮助企业节省了不少的硬件成本,多年都是公司的优秀员工。
但是科技行业有个固有的模式,没有银弹,而这个也适用在虚拟化技术上,随着微服务技术的出现,基于虚拟机这样的部署方式和微服务数量之间的矛盾日益显著,大概早2010年左右,出现了Docker平台,第一次将容器技术推到了聚光灯下,这种轻量级的“虚拟化”技术允许在同一台宿主机上,运行密度更高的相互隔离的应用程序,并且让每个应用的实例都觉得自己独享了和这台机器,资源的使用率得到进一步的提升,瞬间就风靡科技行业。
而在容器技术逐渐被行业所接受,并且大规模的被用到了以互联网企业为代表的高科技行业,痛点从打包部署逐步演进到容器的调度和编排,因此谷歌和红帽公司推出了Kubernetes平台,一套设计完美的编排系统来自动化应用程序的部署和管理工作,特别是基于容器的系统。读到这里,你肯定会好奇的问题,到底什么是编排系统,请继续阅读。
【什么是编排系统?】
根据笔者的理解,编排是一套提供应用自动化部署,扩容和管理的软件系统。在这套系统中,应用程序不再直接“赤裸裸的”运行在物理机上或者虚拟机上,而是运行在某种容器总,如下表所示,笔者总结了当今和历史上的主流部署方式的差异:
如上边的表格所示,编排系统自动化了应用程序的部署,扩展和运维工作。类如Kubernetes这样典型的编排系统主要由7个核心组件组成(7这个数字真的很特殊啊,如果你还记得上篇文章的内容,Linux操作系统提供的命名空间的类型也是7种),接下来我们来详细介绍一下组成编排系统的这7个核心组件,它们是:
- 任务task
- 多个任务组成job
- 调度器
- 管理节点
- 工作节点
- 集群
- 命令行工具CLI
上边罗列的这7个组件构成了一个基本的编排系统,我们先从Task任务开始说起。
【Task任务】
Task任务是编排系统上最小的工作集合,任务一般运行在容器中,当任务运行起来后,就是我们前边多篇文章说的容器进程。一个运行起来的任务可以是一个反向代理实例,也可以是我们自己编写的SpringCloud的API服务,甚至是一台关系型的数据库,如MYSQL。
由于任务是工作单元,因此任务需要配置:
1,内存,磁盘和CPU的资源,来确保任务能顺利和按时完成。
2,如果任务在执行的过程运行失败,我们需要提供明确的容错规则,比如说restart policy。
3,需要给任务提供模板,模板中会包含任务运行的镜像,以及端口号,配置等信息。
如果你了解Kubernetes,你就能对应到具体的对象Pod,但是因为笔者到现在为止还没有介绍POD的概念,因此大家稍安勿躁,虽然说任务的配置不只上边这些项目,但是这些都是驱动任务能够运行起来,满足业务需求的核心配置。好了,了解了任务之后,我们来看看一组任务组成的概念。
【Job-将多个任务组合在一起】
分布式系统为我们提供了高可靠,高性能和极大的提升了系统的吞吐量,但是天下没有免费的午餐,这些炫酷的特性都是有成本的。比如说分布式系统中固有的顽疾:网络不可靠的问题,这就意味着我们无法保证任何一个组件能够长时间稳定的运行。解决这个问题的办法也很简单,一个不行,来两个,所以我们一般会通过部署多个应用的实例来提供更高的可靠性。
Job就是这种“一个不行,部署多个”思想的具体实现,我们通过启动多个task来形成一个逻辑上的Job统一对外提供服务,举个例子,我们可以部署多个Nginx的实例,共同组成一个多节点的集群对外提供服务;或者我们将一个服务实例和一个tomcat实例组合在一起,统一对外提供服务。job和任务比起来,抽象层次更高:
1,job是由一个一个的task组成。
2,job扮演了调度的最小单位,如果你了解Kubernetes,你会马上联想到对应的概念。
接着我们来看看调度器,调度器是大规模分布式系统核心中的和核心。
【调度器Scheduler】
调度器的工作其实很直接,为用户提交的任务确定运行的工作节点(笔者在剩余部分将不区分job和task,统一以任务指代),任务在工作节点上以独立的进程运行,并且受到管理器的管理和监控。调度器的实现可以很简单,比如我们基于经典的round-robin模式,来一个任务就分配给一个工作节点,下个任务分配给下个节点,很明显这种方式是有问题的,无差别对待的结果就是有些机器上运行了很多耗费资源的任务,而有些机器的CPU和内存使用率接近于0.
因此编排系统的调度器实现方式一般都非常复杂,要考虑资源的平衡,集群的稳定性,任务的亲和度,污点规则(比如你就不应该把任务调度到管理节点上),硬件约束(比如某个任务需要GPU来提供高性能计算)等因素,来提供最佳的一种资源分配。整体上来看,调度器提供如下的能力:
1,为即将要运行的任务选择工作节点。
2,基于已经分配的任务历史记录,管理每台工作节点的负载情况。
3,基于预订的调度算法,进行任务分配的计算工作。
【管理节点】
管理节点是编排系统的大脑,负责接收用户的任务提交,并指挥调度器选择合适的运行节点,将任务运行起来。另外管理节点也会不断的收集任务的运行情况,如果发现某个任务变节,或者运行失败,会杀掉这个任务,然后重新创建一个实例来取代它。管理节点主要的工作如下:
1,接受用户提交的任务请求
2,和调度器配合将用户的任务调度到某个工作节点上,并运行起来
3,持续的监控运行起来的任务,在任务失败的情况下,重启任务来确保系统提供符合SLA的服务。
【工作节点】
工作节点顾名思义,就是我们常说的大头兵,冲在最前线,干最苦的活,还不被人理解的(不被人理解主要是因为沟通不及时,因为你不汇报实际的情况,没有人知道这个任务是不是运行的很辛苦:)这些计算节点。工作节点单纯的就是接受任务,将任务运行起来,如果任务运行失败,努力尝试重启任务,确保目标达成。工作节点非常非常重要的一个职责就是收集任务和任务运行机器环境的监控参数,汇报给管理节点,来支持管理节点做准确的决策。工作节点的主要职责包括但不限于:
1,运行容器进程,比如Docker容器实例。
2,从管理节点接受任务
3,提供监控指标信息给管理节点来提升管理和调度的准确性。
4,维护应用运行的状态,如果出现失败,需要努力重试。
【集群】
集群这个概念在编排系统中指上边描述的所有组件的统称,一个编排系统可以运行在一台机器上,那么所有的组件都会在部署在一台机器上。但是如果考虑的稳定性,集群一般需要多台机器来提供可靠性。
【CLI命令行工具】
CLI工具给用户提供了一个医用的操作界面,用于可以通过这个操作界面来完成:
1,启动和停止任务
2,获取任务的实时运行状态
3,监控任务的运行状态
4,启动管理节点
5,启动工作节点
以上就是组成一个典型编排系统的核心组件和介绍,为了帮助读者更好的理解这些概念,笔者准备了下边这张图:
所有的编排系统的架构和上边这张图类似,如果你还记得笔者在介绍Kubernetes文章中的图例的话, 就马上能意识这这一点。今天我们不说Kubernetes,我们来列举Kubernetes的父亲,谷歌公司的Borg系统,如下图所示。Borg系统的管理节点叫BorgMaster,工作节点叫Borglet(这下终于知道kubelets这个名字是怎么来的吧),以及其他的一些组件,这些组件和前边的图比起来,很类似:
通过上图可以看到,最底层的组件就是我们说的工作节点,在Borg系统中叫Borglets, 而在Kubernetes中虽然也叫工作节点,但是每个工作节点上都会运行一个deamon进程,叫Kublets,主要负责管理和运行调度到这个节点上的任务的运行和监控工作。
中间是管理节点Borgmaster,承担了整个集群大脑的职责,通过协调调度器为用户提交的计算和处理任务确定工作节点。
而Kubernetes的诞生可以说深度受到Borg的影响,我们在Kubernetes中把管理节点叫Control panel,工作节点的概念可以说完全一致。Kubernetes中的调度器叫Scheduler,通过下图大家可以对比一下:笔者介绍的编排系统的抽象模型,Borg系统和Kubernetes的架构相似度:
有意思的是,Hashicorp公司开发的Nomad中间件的架构和我们的抽象模式也和类似,在Nomad的架构中,管理节点叫server,工作节点叫cient,虽然下图没有把所有的组件画出来,但是笔者想告诉大家的是,在Nomad中,也有调度器,任务,集群等等的相同该你那,如下图所示:
注:Nomad是一个集群管理器和调度器,专为微服务和批量处理工作流设计。Nomad是一个分布式系统,提供高可用,可扩展能力,可以很容易扩展到跨数据中心和区域的数千个节点。Nomad提供一个常规工作流跨基础设施部署应用。开发者使用一个声明式作业规范来定义应用该如何部署,资源有什么要求(CPU,内存,硬盘)。Nomad 接收这些作业,查找可用的资源来运行应用。调度算法确保所有的约束都满足,尽量在一个主机部署尽可能多的应用,优化资源利用。此外,Nomad 支持在所有主流操作系统运行虚拟化,容器化或者是独立的应用,灵活的支持广泛的工作流负载。
好了,今天这篇文章就到此为止了,下篇文章我们来试图自己写一个基本的Kubernetes平台,带着大家看看,一个基本的编排系统的代码结构具体长啥样,敬请期待!
注:虚拟人物Julia是我们家大女儿的名字,因为疫情的原因,孩子已经有两年暑假都在家里,下图是Julia和妹妹在暑假里开的餐馆,期待疫情早日过去,满足孩子很多旅行的心愿。