小编序:
在上周发布的《从“鸿沟理论”看云原生,哪些技术可以跨越鸿沟?》一文中,灵雀云CTO陈恺表示:Kubernetes在云计算领域已经成为既定标准,进入主流市场,最新版本主要关注在稳定性、可扩展性方面,在开发人员中变得很是流行。Kubernetes会愈来愈多往下管理全部基础设施,往上管理全部种类的应用。咱们会看到,愈来愈多的周边技术向它靠拢,在其之上催化出一个庞大的云原生技术生态。html
那么,如今最新最流行的 Kubernetes 架构是什么样子呢?本文给你们介绍一下 Kubernetes 总体架构,并深刻探讨其中 2 个比较关键的问题。算法
Kubernetes 架构解析数据库
首先,Kubernetes 的官方架构图是这样的:缓存
这个架构图看起来会比较复杂,很难看懂,我把这个官方的架构图从新简化了一下,就会很是容易理解了:服务器
ETCD :是用来存储全部 Kubernetes 的集群状态的,它除了具有状态存储的功能,还有事件监听和订阅、Leader选举的功能,所谓事件监听和订阅,各个其余组件通讯,都并非互相调用 API 来完成的,而是把状态写入 ETCD(至关于写入一个消息),其余组件经过监听 ETCD 的状态的的变化(至关于订阅消息),而后作后续的处理,而后再一次把更新的数据写入 ETCD。所谓 Leader 选举,其它一些组件好比 Scheduler,为了作实现高可用,经过 ETCD 从多个(一般是3个)实例里面选举出来一个作Master,其余都是Standby。架构
API Server:刚才说了 ETCD 是整个系统的最核心,全部组件之间通讯都须要经过 ETCD,实际上,他们并非直接访问 ETCD,而是访问一个代理,这个代理是经过标准的RESTFul API,从新封装了对 ETCD 接口调用,除此以外,这个代理还实现了一些附加功能,好比身份的认证、缓存等。这个代理就是 API Server。并发
Controller Manager:是实现任务调度的,关于任务调度能够参考以前的文章,简单说,直接请求 Kubernetes 作调度的都是任务,好比 Deployment 、Deamon Set 或者 Job,每个任务请求发送给Kubernetes以后,都是由Controller Manager来处理的,每个任务类型对应一个Controller Manager,好比 Deployment对应一个叫作 Deployment Controller,DaemonSet 对应一个 DaemonSet Controller。负载均衡
Scheduler:是用来作资源调度的,Controller Manager会把任务对资源要求,其实就是Pod,写入到ETCD里面,Scheduler监听到有新的资源须要调度(新的Pod),就会根据整个集群的状态,给Pod分配到具体的节点上。异步
Kubelet:是一个Agent,运行在每个节点上,它会监听ETCD中的Pod信息,发现有分配给它所在节点的Pod须要运行,就在节点上运行相应的Pod,而且把状态更新回到ETCD。分布式
Kubectl: 是一个命令行工具,它会调用 API Server发送请求写入状态到ETCD,或者查询ETCD的状态。
若是还以为不清楚,咱们就用部署服务的例子来解释一下整个过程。假设要运行一个多实例的Nginx,在Kubernetes内部,整个流程是这样的:
1.经过kubectl命令行,建立一个包含Nginx的Deployment对象,kubectl会调用 API Server 往ETCD里面写入一个 Deployment对象。
2.Deployment Controller 监听到有新的 Deployment对象被写入,就获取到对象信息,根据对象信息来作任务调度,建立对应的 Replica Set 对象。
3.Replica Set Controller监听到有新的对象被建立,也读取到对象信息来作任务调度,建立对应的Pod来。
4.Scheduler 监听到有新的 Pod 被建立,读取到Pod对象信息,根据集群状态将Pod调度到某一个节点上,而后更新Pod(内部操做是将Pod和节点绑定)。
5.Kubelet 监听到当前的节点被指定了新的Pod,就根据对象信息运行Pod。
这就是Kubernetes内部如何实现整个 Deployment 被建立的过程。这个过程只是为了向你们解释每个组件的职责,以及他们之间是如何相互协做的,忽略掉了一些繁琐的细节。
目前为止,咱们有已经研究过几个很是有表明性的调度系统:Hadoop MRv一、YARN、Mesos和Kubernetes。当时学习完这些调度系统的架构后,脑子里面造成2个大大的疑问:
1.Kubernetes是二次调度的架构么?和Mesos相比它的扩展性如何?
2.为何全部调度系统都是没法横向扩展的?
后面咱们就针对这两个问题深刻讨论一下。
Kubernetes 是不是二层调度?
在 Google 的一篇关于内部 Omega 调度系统的论文中,将调度系统分红三类:单体、二层调度和共享状态三种,按照它的分类方法,一般Google的 Borg被分到单体这一类,Mesos被当作二层调度,而Google本身的Omega被当作第三类“共享状态”。
论文的做者实际上以前也是Mesos的设计者之一,后来去了Google设计新的 Omega 系统,并发表了论文,论文的主要目的是提出一种全新的“Shard State”的模式,来同时解决调度系统的性能和扩展性问题。但我以为 Shared State 模型太过理想化,根据这个模型开发的Omega系统,彷佛在Google内部并无被大规模使用,也没有任何一个大规模使用的调度系统采用 Shared State 模型。
由于Kubernetes的大部分设计是延续 Borg的,并且Kubernetes的核心组件(Controller Manager和Scheduler)缺省也都是绑定部署在一块儿,状态也都是存储在ETCD里面的,因此一般你们会把Kubernetes也当作“单体”调度系统,实际上我并不赞同。
我认为 Kubernetes 的调度模型也彻底是二层调度的,和 Mesos 同样,任务调度和资源的调度是彻底分离的,Controller Manager承担任务调度的职责,而Scheduler则承担资源调度的职责。
实际上Kubernetes和Mesos调度的最大区别在于资源调度请求的方式:
主动 Push 方式。是 Mesos 采用的方式,就是 Mesos 的资源调度组件(Mesos Master)主动推送资源 Offer 给 Framework,Framework 不能主动请求资源,只能根据 Offer 的信息来决定接受或者拒绝。
被动 Pull 方式。是 Kubernetes 的方式,资源调度组件 Scheduler 被动的响应 Controller Manager的资源请求。
这两种方式带来的不一样,我主要从一下 5 个方面来分析。另外注意,我所比较二者的优劣,都是从理论上作的分析,工程实现上会有差别,一些指标我也并无实际测试过。
1.资源利用率:Kubernetes 胜出
理论上,Kubernetes 应该能实现更加高效的集群资源利用率,缘由资源调度的职责彻底是由Scheduler一个组件来完成的,它有充足的信息可以从全局来调配资源,而后而Mesos 却作不到,由于资源调度的职责被切分到Framework和Mesos Master两个组件上,Framework 在挑选 Offer 的时候,彻底没有其余 Framework 工做负载的信息,因此也不可能作出最优的决策。
举个例子,好比咱们但愿把对耗费 CPU的工做负载和耗费内存的工做负载尽量调度到同一台主机上,在Mesos里面不太容易作到,由于他们分属不一样的 Framework。
2.扩展性:Mesos胜出
从理论上讲,Mesos 的扩展性要更好一点。缘由是Mesos的资源调度方式更容易让已经存在的任务调度迁移上来。举个例子,假设已经有了一个任务调度系统,好比 Spark,如今要迁移到集群调度平台上,理论上它迁移到 Mesos 比 Kubernetes 上更加容易。
若是迁移到 Mesos ,没有改变原来的工做流程和逻辑,原来的逻辑是:来了一个做业请求,调度系统把任务拆分红小的任务,而后从资源池里面挑选一个节点来运行任务,而且记录挑选的节点 IP 和端口号,用来跟踪任务的状态。迁移到 Mesos 以后,仍是同样的逻辑,惟一须要变化的是那个资源池,原来是本身管理的资源池,如今变成 Mesos 提供的Offer 列表。
若是迁移到 Kubernetes,则须要修改原来的基本逻辑来适配 Kubernetes,资源的调度彻底须要调用外部的组件来完成,而且这个变成异步的。
3.灵活的任务调度策略:Mesos 胜出
Mesos 对各类任务的调度策略也支持的更好。举个例子,若是某一个做业,须要 All or Nothing 的策略,Mesos 是可以实现的,可是 Kubernetes 彻底没法支持。因此All or Nothing 的意思是,价格整个做业若是须要运行 10 个任务,这 10个任务须要可以所有有资源开始执行,不然就一个都不执行。
4.性能:Mesos 胜出
Mesos 的性能应该更好,由于资源调度组件,也就是 Mesos Master 把一部分资源调度的工做甩给 Framework了,承担的调度工做更加简单,从数据来看也是这样,在多年以前 Twitter 本身的 Mesos 集群就可以管理超过 8万个节点,而 Kubernetes 1.3 只能支持 5千个节点。
5.调度延迟:Kubernetes 胜出
Kubernetes调度延迟会更好。由于Mesos的轮流给Framework提供Offer机制,致使会浪费不少时间在给不须要资源的 Framework 提供Offer。
为何不支持横向扩展?
可能你们已经注意到了,几乎全部的集群调度系统都没法横向扩展(Scale Out),好比早期的 Hadoop MRv1 的管理节点是单节点,管理的集群上限是 5000 台机器,YARN 资源管理节点同时也只能有一个节点工做,其余都是备份节点,可以管理的机器的上限1万个节点,Mesos经过优化,一个集群可以管理 8 万个节点,Kubernetes 目前的 1.13 版本,集群管理节点的上限是 5000 个节点。
全部的集群调度系统的架构都是没法横向扩展的,若是须要管理更多的服务器,惟一的办法就是建立多个集群。集群调度系统的架构看起来都是这个样子的:
中间的 Scheduler(资源调度器)是最核心的组件,虽然一般是由多个(一般是3个)实例组成,可是都是单活的,也就是说只有一个节点工做,其余节点都处于 Standby 的状态。为何会这样呢?看起来不符合互联网应用的架构设计原则,如今大部分互联网的应用经过一些分布式的技术,可以很容易的实现横向扩展,好比电商应用,促销时,经过往集群里面添加服务器,就可以提高服务的吞吐量。若是是按照互联网应用的架构,看起来应该是这样的:
Scheduler 应该是能够多活的,有任意多的实例一块儿对外提供服务,不管是资源的消费者,仍是资源的提供者在访问 Scheduler 的时候,都须要通过一个负载均衡的组件或者设备,负责把请求分配给某一个 Scheduler 实例。为何这种架构在集群调度系统里面变得不可行么?为了理解这件事情,咱们先经过一个互联网应用的架构的例子,来探讨一下具有横向扩展须要哪些前提条件。
横向扩展架构的前提条件
假设咱们要实现这样一个电商系统:
1.这是一个二手书的交易平台,有很是多的卖家在平台上提供二手书,咱们暂且把每一本二手书叫作库存;
2.卖家的每个二手书库存,根据书的条码,均可以找到图书目录中一本书,咱们把这本书叫作商品;
3.卖家在录入二手书库存的时候,除了录入是属于哪个商品,同时还须要录入其余信息,好比新旧程度、价钱、发货地址等等。
4.买家浏览图书目录,选中一本书,而后下单,订单系统根据买家的要求(价格偏好、送货地址等),用算法从这本书背后的全部二手书库存中,匹配一本符合要求的书完成匹配,咱们把这个过程叫订单匹配好了。
这样一个系统,从模型上看这个电商系统和集群调度系统没啥区别,这个里面有资源提供者(卖家),提供某种资源(二手书),组成一个资源池(全部二手书),也有资源消费者(买家),提交本身对资源的需求,而后资源调度器(订单系统)根据算法自动匹配一个资源(一本二手书)。
可是很显然,这个电商系统是能够设计成横向扩展架构的,为何呢?这个电商系统和集群调度系统的区别到底在什么地方? 在回答这个问题以前,咱们先来回答另一个问题:这个电商系统横向扩展的节点数是否有上限,上限是多少,这个上限是有什么因素决定的?
系统理论上的并发数量决定了横向扩展的节点数
假设系统架构设计的时候,不考虑任何物理限制(好比机器的资源大小,带宽等),可以并发处理 1000个请求,那么很显然,横向扩展的节点数量上限就是1000,由于就算部署了 1001个节点,在任什么时候候都有一个节点是处于空闲状态,部署更多的节点已经彻底没法提升系统的性能。咱们下面须要想清楚的问题其实就变成了:系统理论上可以并发处理请求的数量是多少,是有什么因素决定的。
系统的并发数量是由“独立资源池”的数量决定的
“独立资源池”是我本身造出来的一个词,由于实在想不到更加合适的。仍是以上面的电商系统为例,这个订单系统的理论上可以处理的并发请求(订购商品请求)数量是由什么来决定的呢?先看下面的图:
在订单系统在匹配需求的时候,实际上应该是这样运行的,在订单请求来了以后,根据订单请求中的购买的商品来排队,购买同一个商品的请求被放在一个队列里面,而后订单的调度系统开始从队列里面依次处理请求,每次作订单匹配的时候,都需根据当前商品的全部库存,从里面挑选一个最佳匹配的库存。
虽然在实现这个系统的时候,这个队列不见得是一个消息队列,可能会是一个关系型数据库的锁,好比一个购买《乔布斯传》的订单,系统在处理的时候须要先从全部库存里面查询出《乔布斯传》的库存,将库存记录锁住,并作订单匹配且更新库存(将生成订单的库存商品设置为”不可用”状态)以后,才会将数据库锁释放,这时候全部后续购买《乔布斯传》的订单请求都在队列中等待。
也有些系统在实现的时候采用“乐观锁”,就是每次订单处理时,并不会在一开始就锁住库存信息,而是在最后一步更新库存的时候才会锁住,若是发生两个订单匹配到了同一个库存物品,那么其中一个订单处理就须要彻底放弃而后重试。这两种实现方式不太同样,可是本质都是相同的。
因此从上面的讨论能够看出来,之因此全部购买《乔布斯传》的订单须要排队处理,是由于每一次作订单匹配的时候,须要《乔布斯传》这个商品的全部库存信息,而且最后会修改(占用)一部分库存信息的状态。在该订单匹配的场景里面,咱们就把《乔布斯传》的全部库存信息叫作一个“独立资源池”,订单匹配这个“调度系统”的最大并发数量就彻底取决于独立资源池的数量,也就是商品的数量。咱们假设一下,若是这个二手书的商城只卖《乔布斯传》一本书,那么最后全部的请求都须要排队,这个系统也几乎是没法横向扩展的。
集群调度系统的“独立资源池”数量是 1
咱们再来看一下集群调度系统,每一台服务器节点都是一个资源,每当资源消费者请求资源的时候,调度系统用来作调度算法的“独立资源池”是多大呢?答案应该是整个集群的资源组成的资源池,没有办法在切分了,由于:
1.调度系统的职责就是要在全局内找到最优的资源匹配。
2.另外,哪怕不须要找到最优的资源匹配,资源调度器对每一次资源请求,也没办法判断应该从哪一部分资源池中挑选资源。
正是由于这个缘由,“独立资源池”数量是 1,因此集群调度系统没法作到横向扩展。