做者 | 阿里云容器技术专家 莫源
本文整理自莫源于 8 月 31 日 K8s & cloudnative meetup 深圳场的演讲内容。关注“阿里巴巴云原生”公众号,回复关键词“资料”,便可得到 2019 整年 meetup 活动 PPT 合集及 K8s 最全知识图谱。segmentfault
导读:Serverless 和 Autoscaling 是近些年来广大开发者很是关心的内容。有人说 Serverless 是容器 2.0,终有一天容器会和 Serverless 进行一场决战,分出胜负。实际上,容器和 Serverless 是能够共存而且互补的,特别是在 Autoscaling 相关的场景下,Serverless 能够与容器完美兼容,弥补容器场景在使用简单、速度、成本的缺欠,在本文中将会为你们介绍容器在弹性场景下的原理、方案与挑战,以及 Serverless 是如何帮助容器解决这些问题的。
当咱们在谈论"弹性伸缩"的时候,咱们在谈论什么?"弹性伸缩"对于团队中不一样的角色有不一样的意义,而这正是弹性伸缩的魅力所在。架构
这张图是阐述弹性伸缩问题时常常引用的一张图,表示的是集群的实际资源容量和应用所需容量之间的关系。并发
首先,咱们先看左侧第一块黄色栅格的区域,这个区域表示集群的容量没法知足业务的容量所需,在实际的场景中,一般会伴随出现因为资源不足而没法调度的 Pod 等现象。less
中间的栅格区域,集群的容量远高于实际资源所需的容量,此时会出现资源的浪费,实际的表现一般是节点的负载分配不均,部分节点上面无调度负载,而另一些节点的负载相对较高。运维
右侧栅格区域表示的是激增的峰值容量,咱们能够看到,到达峰值前的曲率是很是陡峭的,这种场景一般是因为流量激增、大批量任务等很是规容量规划内的场景,激增的峰值流量给运维同窗的反应时间很是短,一旦处理不当就有可能引起事故。高并发
弹性伸缩对于不一样角色的人员,有着不一样的意义:学习
弹性伸缩有多种不一样的组件和方案,选择适合本身业务需求的方案是落地执行前的第一步。大数据
Kubernetes 弹性伸缩的组件能够从两个维度进行解读:一个是伸缩方向,一个是伸缩对象。阿里云
从伸缩方向上,分为横向与纵向。从伸缩对象上,分为节点与 Pod。那么将这个象限进行展开,就变成以下 3 类组件:spa
其中 HPA 与 Cluster-Autoscaler 是开发者最常组合使用的弹性伸缩组件。HPA 负责容器的水平伸缩,Cluster-Autoscaler 负责节点的水平伸缩。不少的开发者会产生这样的疑问:为何弹性伸缩一个功能须要细化成这么多组件分开处理,难道不能够直接设置一个阈值,就实现集群的自动水位管理吗?
了解 Kubernetes 的调度方式能够帮助开发者更好的理解 Kubernetes 弹性伸缩的设计哲学。在 Kubernetes 中,调度的最小单元是一个 Pod,Pod 会根据调度策略被调度到知足条件的节点上,这些策略包括资源的匹配关系、亲和性与反亲和性等等,其中资源的匹配关系的计算是调度中的核心要素。
一般和资源相关的有以下四个概念:
在了解这四个基本概念和使用场景以后,咱们再来看下 Kubernetes 弹性伸缩的三大难题:
还记得在没有使用容器前,是如何作容量规划的吗?通常会按照应用来进行机器的分配,例如,应用 A 须要 2 台 4C8G 的机器,应用 B 须要 4 台 8C16G 的机器,应用 A 的机器与应用 B 的机器是独立的,相互不干扰。到了容器的场景中,大部分的开发者无需关心底层的资源了,那么这个时候容量规划哪里去了呢?
在 Kubernetes 中是经过 Request
和 Limit
的方式进行设置,Request
表示资源的申请值,Limit
表示资源的限制值。既然 Request
和 Limit
才是容量规划的对等概念,那么这就表明着资源的实际计算规则要根据 Request
和 Limit
才更加准确。而对于每一个节点预留资源阈值而言,颇有可能会形成小节点的预留没法知足调度,大节点的预留又调度不完的场景。
在一个 Kubernetes 集群中,一般不仅包含一种规格的机器。针对不一样的场景、不一样的需求,机器的配置、容量可能会有很是大的差别,那么集群伸缩时的百分比就具有很是大的迷惑性。
假设咱们的集群中存在 4C8G 的机器与 16C32G 两种不一样规格的机器,对于 10% 的资源预留而言,这两种规格是所表明的意义是彻底不一样的。特别是在缩容的场景下,一般为了保证缩容后的集群不处在震荡状态,咱们会一个节点一个节点来缩容节点,那么如何根据百分比来判断当前节点是处在缩容状态就尤其重要。此时若是大规格机器有较低的利用率被判断缩容,那么颇有可能会形成节点缩容后,容器从新调度后的争抢饥饿。若是添加判断条件,优先缩容小配置的节点,则有可能形成缩容后资源的大量冗余,最终集群中可能会只剩下全部的巨石
节点。
集群的资源利用率是否能够真的表明当前的集群状态呢?当一个 Pod 的资源利用率很低的时候,不表明就能够侵占他所申请的资源。在大部分的生产集群中,资源利用率都不会保持在一个很是高的水位,但从调度来说,资源的调度水位应该保持在一个比较高的水位。这样才能既保证集群的稳定可用,又不过于浪费资源。
若是没有设置 Request
与 Limit
,而集群的总体资源利用率很高,这意味着什么?这表示全部的 Pod 都在被以真实负载为单元进行调度,相互之间存在很是严重的争抢,并且简单的加入节点也丝毫没法解决问题,由于对于一个已调度的 Pod 而言,除了手动调度与驱逐,没有任何方式能够将这个 Pod 从高负载的节点中移走。那若是咱们设置了 Request
与 Limit
而节点的资源利用率又很是高的时候说明了什么呢?很惋惜这在大部分的场景下都是不可能的,由于不一样的应用不一样的负载在不一样的时刻资源的利用率也会有所差别,大几率的状况是集群尚未触发设置的阈值就已经没法调度 Pod 了。
在了解了 Kubernetes 弹性伸缩的三大问题后,咱们再来看下 Kubernetes 的解决办法是什么?
Kubernetes 的设计理念是将弹性伸缩分红调度层伸缩和资源层伸缩。调度层负责根据指标、阈值伸缩出调度单元,而资源层伸缩负责知足调度单元的资源需求。
在调度层一般是经过 HPA 的方式进行 Pod 的水平伸缩,HPA 的使用方式和咱们传统意义上理解的弹性伸缩是很是接近和相似的,经过设置判断的指标、判断的阈值来进行水平伸缩。
在资源层目前主流的方案是经过 cluster-autoscaler 进行节点的水平伸缩。当出现 Pod 因为资源不足形成没法调度时,cluster-autoscaler 会尝试从配置伸缩组中,选择一个能够知足调度需求的组,并自动向组内加入实例,当实例启动后注册到 Kubernetes 后,kube-scheduler 会从新触发 Pod 的调度,将以前没法调度的 Pod 调度到新生成的节点上,从而完成全链路的扩容。
一样在缩容时,调度层会现根据资源的利用率与设置的阈值比较,实现 Pod 水平的缩容。当节点上 Pod 的调度资源下降到资源层缩容阈值的时候,此时 Cluster-Autoscaler 会进行低调度百分比的节点的排水,排水完成后会进行节点的缩容,完成整个链路的收缩。
这张图是一个很是经典的弹性伸缩的案例,能够表明大多数的在线业务的场景。应用的初始架构是一个 Deployment,下面有两个 Pod,这个应用的接入层是通 过Ingress Controller 的方式进行对外暴露的,咱们设置应用的伸缩策略为:单个 Pod 的 QPS 到达 100,则进行扩容,最小为 2 个 Pod,最大为 10 个 Pod。
HPA controller 会不断轮训 alibaba-cloud-metrics-adapter,来获取 Ingress Gateway 当前路由的 QPS 指标。当 Ingress Gateway 的流量到达 QPS 阈值时,HPA controller 会触发 Deployment 的 Pod 数目变化;当 Pod 的申请容量超过集群的总量后,cluster-autoscaler 会选择合适的伸缩组,弹出相应的 Node,承载以前未调度的 Pod。
这样一个经典的弹性伸缩案例就解析完毕了,那么在实际的开发过程当中,会遇到哪些问题呢?
首先是扩容时延的问题,社区标准模式是经过建立、释放 ECS 的方式,扩容的时延在 2min-2.5min 左右,而阿里云独立的极速模式是经过建立、停机、启动的方式进行实现,停机时只收取存储的费用,不收取计算的费用。能够经过很是低廉的价格得到 50% 以上的弹性效率。
此外复杂度也是 cluster-autoscaler 绕不过的问题,想要用好 cluster-autoscaler,须要深刻的了解 cluster-autoscaler 的一些内部机制,不然极有可能形成没法弹出或者没法缩容的场景。
对于大多数的开发者而言,cluster-autoscaler 的工做原理是黑盒的,并且 cluster-autoscaler 目前最好的问题排查方式依然是查看日志。一旦 cluster-autoscaler 出现运行异常后者因为开发者配置错误致使没法如预期的伸缩,那么 80% 以上的开发者是很难本身进行纠错的。
阿里云容器服务团队开发了一款 kubectl plugin,能够提供 cluster-autoscaler 更深层次的可观测性,能够查看当前 cluster-autoscaler 所在的伸缩阶段以及自动弹性伸缩纠错等能力。
虽然目前遇到的几个核心的问题,都不是压死骆驼的最后一棵稻草。可是咱们一直在思考,是否有其余的方式可让弹性伸缩使用起来更简单、更高效?
资源层伸缩的核心问题在于学习成本较高、排错困难、时效性差。当回过头来看 Serverless 的时候,咱们能够发现这些问题刚好是 Serverless 的特色与优点,那么是否有办法让 Serverless 成为 Kubernetes 资源层的弹性方案呢?
阿里云容器服务团队开发了 virtual-kubelet-autoscaler,一个在 Kubernetes 中实现 serverless autoscaling 的组件。
当出现了没法调度的 Pod 的时候,virtual-kubelet 负责承载真实的负载,能够理解为一个虚拟节点,拥有无限大的 capacity。当 Pod 调度到 virtual-kubelet 上时,会将 Pod 经过轻量级实例 ECI 进行启动。目前 ECI 的启动时间在 30s 以内,程序从调度开始到运行通常会在 1 分钟内拉起。
与 cluster-autoscaler 相似,virtual-kubelet-autoscaler 也须要使用模拟调度的机制来判断 Pod 是否能够被真实处理和承载,可是相比 cluster-autoscaler 而言,存在以下差别:
virtual-kubelet-autoscaler 并非用来替代 cluster-autoscaler 的,virtual-kubelet-autoscaler 的优点在于使用简单、高弹性高并发,按量按需计费。可是与此同时也牺牲了部分的兼容性,目前对 cluster-pi、coredns 等机制支持的还并不完善,只需少量的配置 virtual-kubelet-autoscaler 是能够和 cluster-autoscaler 兼容的。virtual-kubelet-autoscaler 特别适合的场景是大数据离线任务、CI/CD 做业、突发型在线负载等。
serverless autoscaling 已经逐渐成为 Kubernetes 弹性伸缩的重要组成部分,当 serverless autoscaling 兼容性基本补齐的时候,serverless 使用简单、无需运维、成本节约的特性会与 Kubernetes 造成完美互补,实现 Kubernetes 弹性伸缩的新飞跃。
搜索「阿里巴巴云原生公众号」获取更多K8s容器技术内容