内容简介:在这篇文章,我会详细介绍一下 Kubernetes 的架构,您在看这篇文章之前,需要提前了解一下 Kubernetes 是什么,以及一些基本概念。关于调度系统架构的文章,这是系列的第二篇,之前的第一篇请移步这这里阅读:《集群调度系统的演进》。在文章里面,通过介绍 Hadoop MRv1、YARN和Mesos调度系统,了解了调度系统的架构和演进过程,那么现在最新最流行的 Kubernetes 的架构是什么样子的呢?这篇文章就给大家介绍一下 Kubernetes 整体架构,并且会深入探讨其中 2 个比较深入的问
在这篇文章,我会详细介绍一下 Kubernetes 的架构,您在看这篇文章之前,需要提前了解一下 Kubernetes 是什么,以及一些基本概念。
关于调度系统架构的文章,这是系列的第二篇,之前的第一篇请移步这这里阅读:《集群调度系统的演进》。在文章里面,通过介绍 Hadoop MRv1、YARN和Mesos调度系统,了解了调度系统的架构和演进过程,那么现在最新最流行的 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 内部,整个流程是这样的:
-
通过 kubectl 命令行,创建一个包含 Nginx 的 Deployment 对象,kubectl 会调用 API Server 往 etcd 里面写入一个 Deployment 对象。
-
Deployment Controller 监听到有新的 Deployment 对象被写入,就获取到对象信息,根据对象信息来做任务调度,创建对应的 Replica Set 对象。
-
Replica Set Controller 监听到有新的对象被创建,也读取到对象信息来做任务调度,创建对应的 Pod 来。
-
Scheduler 监听到有新的 Pod 被创建,读取到 Pod 对象信息,根据集群状态将 Pod 调度到某一个节点上,然后更新 Pod(内部操作是将 Pod 和节点绑定)。
-
Kubelet 监听到当前的节点被指定了新的 Pod,就根据对象信息运行 Pod。
上面就是 Kubernetes 内部的是如何实现的整个 Deployment 被创建的过程。这个过程只是为了向大家解释每一个组件的职责,以及他们之间是如何相互协作的,忽略掉了很多繁琐的细节。
目前为止,我们有已经研究过了几个非常具有代表性的调度系统:Hadoop MRv1、YARN、Mesos 和 Kubernetes。我当时学习完这些调度系统的架构之后,在我脑子里面实际上有 2 个大大的疑问:
-
Kubernetes 是二次调度的架构么,和 Mesos 相比它的扩展性如何?
-
为什么所有调度系统都是无法横向扩展的?
后面我们就针对这两个问题深入讨论一下。
Kubernetes 是否是二层调度,和 Mesos 相比扩展性如何?
在 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 个方面来分析。另外注意,我所比较两者的优劣,都是从理论上做的分析,工程实现上会有差异,一些指标我也并没有实际测试过。
资源利用率: Kubernetes 胜出
理论上,Kubernetes 应该能实现更加高效的集群资源利用率,原因资源调度的职责完全是由 Scheduler 一个组件来完成的,它有充足的信息能够从全局来调配资源,然后 Mesos 缺却做不到,因为资源调度的职责被切分到 Framework 和 Mesos Master 两个组件上,Framework 在挑选 Offer 的时候,完全没有其他 Framework 的工作负载的信息,所以也不可能做出最优的决策。我们来举一个例子,比如我们希望把对耗费 CPU 的工作负载和耗费内存的工作负载竟可能调度到同一台主机上,在 Mesos 里面不太容易做到,因为他们是属于不同的 Framework。
扩展性: Mesos 胜出
从理论上讲,Mesos 的扩展性要更好一点。原因是 Mesos 的资源调度方式更容易让已经存在的任务调度迁移上来。我来举一个例子说明一下,假设已经有了一个任务调度系统,比如 Spark ,现在要迁移到集群调度平台上,理论上它迁移到 Mesos 比 Kubernetes 上更加容易。
如果迁移到 Mesos ,没有改变它原来的工作流程和逻辑,原来的逻辑是:来了一个作业请求,调度系统把任务拆分成小的任务,然后从资源池里面挑选一个节点来运行任务,并且记录挑选的节点 IP 和端口号,用来跟踪任务的状态。迁移到 Mesos 之后,还是一样的逻辑,唯一需要变化的是那个资源池,原来是自己管理的资源池,现在变成 Mesos 提供的 Offer 列表。
如果迁移到 Kubernetes,则需要修改原来的基本逻辑来适配 Kubernetes,资源的调度完全需要调用外部的组件来完成,并且这个变成异步的。 (如果你想深入快速学习Kubernetes, 可以报名参加我们组织的为期3天的Kubernetes实战培训 ,一线资深讲师带你从0开始上手Kubernetes。)
灵活的任务调度策略: Mesos 胜出
Mesos 对各种任务的调度策略也要支持的更好。举个例子,如果某一个作业,需要 All or Nothing 的策略,Mesos 是能够实现的,但是 Kubernetes 完全无法支持。所以为的 All or Nothing 的意思是,价格整个作业如果需要运行 10 个任务,这 10 个任务需要能够全部有资源开始执行,否则的话就一个都不执行。
性能: Mesos 胜出
Mesos 的性能应该更好,因为资源调度组件,也就是 Mesos Master 把一部分资源调度的工作甩给 Framework了,承担的调度工作更加简单,从数据来看也是这样,在多年之前 Twitter 自己的 Mesos 集群就能够管理超过 8万个节点,而 Kubernetes 1.3 只能支持 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 实例。为什么这种架构在集群调度系统里面变得不可行么?为了理解这件事情,我们先通过一个互联网应用的架构的例子,来探讨一下具备横向扩展需要哪些前提条件。
横向扩展架构的前提条件
假设我们要实现这样一个电商系统吧:
-
这是一个二手书的交易平台,有非常多的卖家在平台上提供二手书,我们暂且把每一本二手书叫做库存吧。
-
卖家的每一个二手书库存,根据书的条码,都可以找到图书目录中一本书,我们把这本书叫做商品吧。
-
卖家在录入二手书库存的时候,除了录入是属于哪一个商品,同时还需要录入其他信息,比如新旧程度、价钱、发货地址等等。
-
买家浏览图书目录,选中一本书,然后下单,订单系统根据买家的要求(价格偏好、送货地址等),用算法从这本书背后的所有二手书库存中,匹配一本符合要求的书完成匹配,我们把这个过程叫订单匹配好了。
这样一个系统,从模型上看这个电商系统和集群调度系统没啥区别,这个里面有资源提供者(卖家),提供某种资源(二手书),组成一个资源池(所有二手书),也有资源消费者(买家),提交自己对资源的需求,然后资源调度器(订单系统)根据算法自动匹配一个资源(一本二手书),但是很显然,这个电商系统是可以设计成横向扩展架构的,这是为什么呢,这个电商系统和集群调度系统的区别到底在什么地方?我想在回答这个问题之前,我们先来回答另外一个问题:这个电商系统横向扩展的节点数是否有上限,上限是多少,这个上限是有什么因素决定的?
系统理论上的并发数量决定了横向扩展的节点数
怎么来理解这个事情呢,假设系统架构设计的时候,不考虑任何物理限制(比如机器的资源大小,带宽等),能够并发处理 1000 个请求,那么很显然,横向扩展的节点数量上限就是 1000,应为就算部署了 1001 个节点,在任何时候都有一个节点是处于空闲状态,部署更多的节点已经完全无法提高系统的性能。我们下面需要想清楚的问题其实就变成了:系统理论上能够并发处理请求的数量是多少,是有什么因素决定的。
系统的并发数量是由“独立资源池”的数量决定的
“独立资源池”是我自己造出来的一个词,因为实在想不到更加合适的。还是以上面的电商系统为例,这个订单系统的理论上能够处理的并发请求(订购商品请求)数量是由什么来决定的呢?先看下面的图吧:
在订单系统在匹配需求的时候,实际上应该是这样运行的,在订单请求来了之后,根据订单请求中的购买的商品来排队,购买同一个商品的请求被放在一个队列里面,然后订单的调度系统开始从队列里面依次处理请求,每次做订单匹配的时候,都需根据当前商品的所有库存,从里面挑选一个最佳匹配的库存。虽然在实现这个系统的时候,这个队列不见得是一个消息队列,可能会是一个关系型数据库的锁,比如一个购买《乔布斯传》的一个订单,系统在处理的时候需要先要从所有库存里面查询出《乔布斯传》的库存,将库存记录锁住,并且做订单匹配并且更新库存(将生成订单的库存商品设置为”不可用”状态)之后,才会将数据库锁释放,这个时候实际上所有后续购买《乔布斯传》的订单请求都在队列中等待,也有些系统在实现的时候采用“乐观锁”,就是在每一次订单处理的时候,并不会在第一开始就锁住库存信息,而是在最后一步更新库存的时候才会锁住,如果发生两个订单匹配到了同一个库存物品,那么其中一个订单处理就需要完全放弃然后重试。这两种实现方式不太一样,但是本质都是相同的。
所以从上面的讨论可以看出来,之所以所有购买《乔布斯传》的订单需要排队处理,原因是因为每一次做订单匹配的时候,需要所有乔布斯传的这个商品的所有库存信息,并且最后会修改(占用)一部分库存信息的状态。在这个订单匹配的场景里面,我们就把乔布斯传的所有库存信息叫做一个“独立资源池”,订单匹配这个“调度系统”的最大并发数量就完全取决于独立资源池的数量,也就是商品的数量。我们假设一下,如果这个二手书的商城只卖《乔布斯传》一本书,那么最后所有的请求都需要排队,这个系统也几乎是无法横向扩展的。
集群调度系统的“独立资源池”数量是 1
我们再来看一下集群调度系统,每一台服务器节点都是一个资源,每当资源消费者请求资源的时候,调度系统用来做调度算法的“独立资源池”是多大呢?答案应该是整个集群的资源组成的资源池,没有办法在切分了,因为:
-
调度系统的职责就是要在全局内找到最优的资源匹配。
-
另外,就算不需要找到最优的资源匹配,资源调度器对每一次资源请求,也没办法判断应该从哪一部分资源池中挑选资源。
正是因为这个原因,“独立资源池”数量是 1,所以集群调度系统无法做到横向扩展。
本文转载自公众号: 深入浅出谈架构, 点击查看原文 。
Kubernetes入门与进阶实战培训
Kubernetes入门与进阶实战培训将于2019年6月14日在北京开课,3天时间带你系统掌握Kubernetes, 学习效果不好可以继续学习。 本次培训包括:Docker基础、容器技术、 Docker 镜像、数据共享与持久化、Docker三驾马车、Docker实践、Kubernetes基础、Pod基础与进阶、常用对象操作、服务发现、Helm、Kubernetes核心组件原理分析、Kubernetes服务质量保证、调度详解与应用场景、网络、基于Kubernetes的CI/CD、基于Kubernetes的配置管理等,点击下方图片或者点击阅读原文了解详情。
以上所述就是小编给大家介绍的《Kubernetes架构为什么是这样的?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 『互联网架构』软件架构-分布式架构(14)
- 『互联网架构』软件架构-电商系统架构(上)(69)
- 『互联网架构』软件架构-电商系统架构(中)(70)
- 『互联网架构』软件架构-电商系统架构(下)(71)
- 『互联网架构』软件架构-电商系统架构发展历程(68)
- 阿里P8架构师谈:淘宝技术架构从1.0到4.0的架构变迁!附架构资料
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
疯狂Java讲义
李刚 / 电子工业出版社 / 2008-10 / 99.00元
《疯狂Java讲义》2000年至今,Java语言一直是应用最广的开发语言,并拥有最广泛的开发人群。如今,Java已经不再简单地是一门语言,它更像一个完整的体系,一个系统的开发平台。更甚至,它被延伸成一种开源精神。 《疯狂Java讲义》深入介绍了Java编程的相关方面,全书内容覆盖了Java的基本语法结构、Java的面向对象特征、Java集合框架体系、Java泛型、异常处理、Java GUI编......一起来看看 《疯狂Java讲义》 这本书的介绍吧!