做者<br />阿里云容器平台技术专家 王程<br />阿里云容器平台技术专家 张晓宇(衷源)java
<a name="Ga22a"></a>node
不知道你们有没有过这样的经历,当咱们拥有了一套 Kubernetes 集群,而后开始部署应用的时候,咱们应该给容器分配多少资源呢?很难说。因为 Kubernetes 本身的机制,咱们能够理解容器的资源实质上是一个静态的配置。若是我发发现资源不足,为了分配给容器更多资源,咱们须要重建 Pod。若是分配冗余的资源,那么咱们的 worker node 节点彷佛又部署不了多少容器。试问,咱们能作到容器资源的按需分配吗?这个问题的答案,咱们能够在本次分享中和你们一块儿进行探讨。git
首先容许咱们根据咱们的实际状况抛出咱们实际生产环境的挑战。或许你们还记的,2018 的天猫双 11,一天的总成交额达到了 2135 亿。由此一斑可窥全豹,可以支撑如此庞大规模的交易量背后的系统,其应用种类和数量应该是怎样的一种规模。在这种规模下,咱们经常听到的容器调度,如:容器编排,负载均衡,集群扩缩容,集群升级,应用发布,应用灰度等等这些词,在被超大规模集群这个词修饰后,都再也不是件容易处理的事情。规模自己也就是咱们最大的挑战。如何运营和管理好这么一个庞大的系统,并遵循业界 dev-ops 宣传的那样效果,犹如让大象去跳舞。可是马老师说过,大象就该干大象该干的事情,为何要去跳舞呢。github
<a name="1"></a>web
大象是否能够跳舞,带着这个问题,咱们须要从淘宝天猫等 APP 背后系统提及。这套互联网系统应用部署大体可分为三个阶段,传统部署,虚拟机部署和容器部署。相比于传统部署,虚拟机部署有了更好的隔离性和安全性,可是在性能少不可避免的产生了大量损耗。而容器部署又在虚拟机部署实现隔离和安全的背景下,提出了更轻量化的解决方案。咱们的系统也是沿着这么一条主航道上运行的。假设底层系统比如一艘巨轮,面对巨量的集装箱---容器,咱们须要一个优秀的船长,对它们进行调度编排,让系统这艘大船能够避开层层险阻,操做难度下降,且具有更多灵活性,最终达成航行的目的。算法
<a name="2"></a>数据库
在开始之初,想到容器化和 Kubernetes 的各类美好场景,咱们理想中的容器编排效果应该是这样的:api
然而理想很丰满,现实很骨感。迎接咱们的是杂乱和形态万千的窘迫。杂乱是由于:做为一个异军突起的新型技术栈,不少配套工具和工做流的建设处于初级阶段。Demo 版本中运行良好的工具,在真实场景下大规模铺开,各类隐藏的问题就会暴露无遗,层出不穷。从开发到运维,全部的工做人员都在各类被动地疲于奔命。另外,“大规模铺开”还意味着,要直接面对形态万千的生产环境:异构配置的机器,复杂的需求,甚至是适配用户的既往的使用习惯等等。安全
<br />除了让人心力交瘁的混乱,系统还面临着应用容器的各类崩溃问题:内存不足致使的 OOM, CPU quota 分配太少致使的,进程被 throttle,还有带宽不足,响应时延大幅上升...甚至是交易量在面对访问高峰时候因为系统不给力致使的断崖式下跌等等。这些都使咱们在大规模商用 Kubernetes 场景中积累很是多的经验。网络
<a name="3"></a>
<a name="4"></a>
问题总要进行面对的。正如某位高人说过:若是感受哪里不太对,那么确定有些地方出问题了。因而咱们就要剖析,问题究竟出在哪里。针对于内存的 OOM,CPU 资源被 throttle,咱们能够推断咱们给与容器分配的初始资源不足。
资源不足就势必形成整个应用服务稳定性降低的问题。例如上图的场景:虽然是同一种应用的副本,或许是因为负载均衡不够强大,或者是因为应用自身的缘由,甚至是因为机器自己是异构的,相同数值的资源,可能对于同一种应用的不一样副本并具备相等的价值和意义。在数值上他们看似分配了相同的资源,然而在实际负载工做时,极有可能出现的现象是肥瘦不均的。
<br />而在资源 overcommit 的场景下,应用在整个节点资源不足,或是在所在的 CPU share pool 资源不足时,也会出现严重的资源竞争关系。资源竞争是对应用稳定性最大的威胁之一。因此咱们要尽力在生产环境中清除全部的威胁。<br />咱们都知道稳定性是件很重要的事情,尤为对于掌控上百万容器生杀大权的一线研发人员。或许不经心的一个操做就有可能形成影响面巨大的生产事故。所以,咱们也按照通常流程也作了系统预防和兜底工做。在预防维度,咱们能够进行全链路的压力测试,而且提早经过科学的手段预判应用须要的副本数和资源量。若是无法准确预算资源,那就只采用冗余分配资源的方式了。在兜底维度,咱们能够在大规模访问流量抵达后,对不紧要的业务作服务降级并同时对主要应用进行临时扩容。可是对于陡然增长几分钟的突增流量,这么多组合拳的花费不菲,彷佛有些不划算。或许咱们能够提出一些解决方案,达到咱们的预期。
<a name="5"></a>
回顾一下咱们的应用部署状况:节点上的容器通常分属多种应用,这些应用自己不必定,也通常不会同时处于访问的高峰。对于混合部署应用的宿主机,若是能都错峰分配上面运行容器的资源或许更科学。<br />
应用的资源需求可能就像月亮同样有阴晴圆缺,有周期变化。例如在线业务,尤为是交易业务,它们在资源使用上呈现必定的周期性,例如:在凌晨、上午时,它的使用量并非很高而在午间、下午时会比较高。打个比方:对于 A 应用的重要时刻,对于 B 应用可能不那么重要,适当打压B应用,腾挪出资源给A应用,这是个不错的选择。这听起来有点像是分时复用的感受。可是若是咱们按照流量峰值时的需求配置资源就会产生大量的浪费。
<br />除了对于实时性要求很高的在线应用外,咱们还有离线应用和实时计算应用等:离线计算对于 CPU 、Memory 或网络资源的使用以及时间不那么敏感,因此在任什么时候间段它均可以运行。实时计算,可能对于时间敏感性就会很高。早期,咱们业务是在不一样的节点按照应用的类型独立进行部署。从上面这张图来看,若是它们进行分时复用资源,针对实时性这个需求层面,咱们会发现它实际的最大使用量不是 2+2+1=5,而是某一时刻重要紧急应用需求量的最大值,也就是 3 。若是咱们可以数据监测到每一个应用的真实使用量,给它分配合理值,那么就能产生资源利用率提高的实际效果。
对于电商应用,对于采用了重量级 java 框架和相关技术栈的 web 应用,短期内 HPA 或者 VPA 都不是件容易的事情。先说 HPA,咱们或许能够秒级拉起了 Pod,建立新的容器,然而拉起的容器是否真的可用呢。从建立到可用,可能须要比较久的时间,对于大促和抢购秒杀-这种访问量“洪峰”可能仅维持几分钟或者十几分钟的实际场景,若是咱们等到 HPA 的副本所有可用,可能市场活动早已经结束了。至于社区目前的 VPA 场景,删掉旧 Pod,建立新 Pod,这样的逻辑更难接受。因此综合考虑,咱们须要一个更实际的解决方案弥补 HPA 和 VPA 的在这一单机资源调度的空缺。
<a name="6"></a>
<a name="7"></a>
咱们首先要对解决方案设定一个能够交付的标准:那就是 “既要稳定性,也要利用率,还要自动化实施,固然若是可以智能化那就更好”。<br />而后再交付标准进行细化:
上图是咱们最初的工具流程设计:当一个应用面临很高的业务访问需求时,体如今 CPU、Memory 或其余资源类型需求量变大,咱们根据 Data Collector 采集的实时基础数据,利用 Data Aggregator 生成某个容器或整个应用的画像,再将画像反馈给 Policy engine。 Policy engine 会瞬时快速修改 容器 Cgroup 文件目录下的的参数。咱们最先的架构和咱们的想法同样朴实,在 kubelet 进行了侵入式的修改。虽然咱们只是加了几个接口,可是这种方式确实不够优雅。每次 kubenrnetes 升级,对于 Policy engine 相关组件升级也有必定的挑战。
<br />为了作到快速迭代并和 Kubelet 解耦,咱们对于实现方式进行了新的演进。那就是将关键应用容器化。这样能够达到如下功效:
固然在后续演进中,咱们也在尝试和 HPA,VPA 进行打通,毕竟这些和 Policy engine 是存在着互补的关系。所以咱们架构进一步演进成以下情形。当 Policy engine 在处理一些更多复杂场景搞到无力时,上报事件让中心端作出更全局的决策。水平扩容或是垂直增长资源。
下面咱们具体讨论一下 Policy engine 的设计。Policy engine 是单机节点上进行智能调度并执行 Pod 资源调整的核心组件。它主要包括 api server,指挥中心 command center 和执行层 executor。其中 api server 用于服务外界对于 policy engine 运行状态的查询和设置的请求;command center 根据实时的容器画像和物理机自己的负载以及资源使用状况,做出 Pod 资源调整的决策。Executor 再根据 command center 的决策,对容器的资源限制进行调整。同时,executor 也把每次调整的 revision info 持久化,以便发生故障时能够回滚。
指挥中心按期从 data aggregator 获取容器的实时画像,包括聚合的统计数据和预测数据,首先判断节点状态,例如节点磁盘异常,或者网络不通,表示该节点已经发生异常,须要保护现场,再也不对Pod进行资源调整,以避免形成系统震荡,影响运维和调试。若是节点状态正常,指挥中心会策略规则,对容器数据进行再次过滤。好比容器 cpu 率飙高,或者容器的响应时间超过安全阈值。若是条件知足,则对知足条件的容器集合给出资源调整建议,传递给executor。
在架构设计上,咱们遵循了如下原则:
在资源调整方面,Cgroup 支持咱们对各个容器的 CPU、内存、网络和磁盘 IO 带宽资源进行,目前咱们主要对容器的 CPU 资源进行调整,同时在测试中探索在时分复用的场景下动态调整 memory limit 和 swap usage 而避免 OOM 的可行性;在将来咱们将支持对容器的网络和磁盘 IO 的动态调整。 <a name="9"></a>
<a name="d7WS0"></a>
<br />上图展现了咱们在测试集群获得的一些实验结果。咱们把高优先级的在线应用和低优先级的离线应用混合部署在测试集群里。SLO 是 250ms,咱们但愿在线应用的 latency 的 95 百分位值低于阈值 250ms。在实验结果中能够看到,在大约90s前,在线应用的负载很低;latency 的均值和百分位都在 250ms 如下。到了 90s后,咱们给在线应用加压,流量增长,负载也升高,致使在线应用 latency 的 95 百分位值超过了 SLO。在大约 150s 左右,咱们的小步快跑控制策略被触发,渐进式地 throttle 与在线应用发生资源竞争的离线应用。到了大约 200s 左右,在线应用的性能恢复正常,latency 的 95 百分位回落到 SLO 如下。这说明了咱们的控制策略的有效性。
<a name="10"></a>
下面咱们总结一下在整个项目的进行过程当中,咱们收获的一些经验和教训,但愿这些经验教训可以对遇到相似问题和场景的人有所帮助。
<a name="G7TgI"></a>
总结起来,咱们的工做主要实现了如下几方面的收益:
展望将来,咱们但愿在如下几个方面增强和扩展咱们的工做:
<a name="RrmTn"></a>
若是你们对于咱们的项目代码感兴趣的话,预计 2019 年 9 月份,咱们的工做也将出如今阿里巴巴开源项目OpenKruise (https://github.com/openkruise)中,敬请期待!