[云原生] Kubernetes 应用接入最佳实践
应用接入最佳实践
目标:稳定性,可用性,性能,安全
从多维度思考高可用的问题
迁移对应用造成的影响
Java
- Concurrent GC Thread
- Heap Size
- 线程数不可控
Node.js
- 多线程模式启动的Thread数量过多,导致OOM kill.
Metric-server
Metrics-Server是集群核心监控数据的聚合器,,可以通过 Metrics API 的形式获取 Metrics 数据,不过仅仅是获取指标的最新值,不对旧值进行存储,且不负责将指标转发到第三方目标。Metrics Server 还可以与 Kubectl 工具结合使用,提供 kubectl top 命令来展示集群中的指标数据。
Aggregated APIServer
对apiserver的访问实际上就是rest调用,后端的URI就是哪一个api的哪一个版本的哪一个对象。然后就可以对这些URI注册不同的handler,转发到不同的api。
把多个apiserver聚合成一个,对用户来说只是访问了一个apiserver地址,后面可能有多个apiserver,服务于不同的api。原生的apiserver,服务于原生对象如node,pod等。
API Aggregation 允许在不修改 Kubernetes 核心代码的同时扩展 Kubernetes API,即将第三方服务注册到 Kubernetes API 中,这样就可以通过 Kubernetes API 来访问外部服务。
另外一种扩展 Kubernetes API 的方法是使用 CustomResourceDefinition (CRD)。
自动扩容缩容
应用扩容是指在应用接收到的并发请求已经处于其处理请求极限边界的情形下,扩展处理能力而确保应用高可用的技术手段。
- Horizontal Scaling
所谓横向伸缩是指通过增加应用实例数量分担负载的方式来提升应用整体处理能力的方式。 - Vertical Scaling
所谓纵向伸缩是指通过增加单个应用实例资源以提升单个实例处理能力,进而提升应用整体处理能力的方式。
云原生下应用扩缩容都有哪些方式呢?
- HPA:Horizatontal Pod Autoscaler,规定资源使用值,最小和最大实例数量
- VPA:Vertical Pod Autoscaler,规定最小和最大资源使用量。没有生产可用,暂不介绍。
- Cluster Autoscaler:pod处于pending状态集群扩容
HPA
- HPA 是 Kubernetes 的一种资源对象,能够根据某些指标对在 statefulSet、 replica Set、 deployment 等集合中的Pod 数量进行横向动态伸缩,使运行在上面的服务对指标的变化有一定的自适应能力。
- 因节点计算资源固定,当 Pod 调度完成并运行以后,动态调整计算资源变得较为困难,因此横向扩展具有更大优势,HPA 是扩展应用能力的第一选择。
- 多个冲突的 HPA 同时创建到同一个应用的时候会有无法预期的行为,因此需要小心维护 HPA规则。
- HPA 依赖于 Metrics-Server。
HPA v2版本的配置如下所示。
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
# HPA 的伸缩对象描述,HPA会动态修改该对象的Pod 数量
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
# HPA 的最小 Pod 数量和最大 Pod 数量
minReplicas: 1
maxReplicas: 10
# 监控的指标数组,支持多种类型的指标共存
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
HPA 的指标类型即metrics
对于按 Pod 统计的资源指标(如 CPU),控制器从资源指标 API 中获取每一个HPA指定的 Pod 的度量值,如果设置了目标使用率,控制器获取每个 Pod 中的容器资源使用情况,并计算资源使用率。如果设置了 target 值,将直接使用原始数据(不再计算百分比)。
如果 Pod 使用自定义指标,控制器机制与资源指标类似,区别在于自定义指标只使用原始值,而不是使用率。
如果 Pod 使用对象指标和外部指标(每个指标描述一个对象信息)比如QPS,这个指标将直接根据目标设定值相比较,并生成一个上面提到的扩缩比例。在 autoscaling/v2beta2 版本 API中,这个指标也可以根据 Pod 数量平分后再计算。
# Resource 类型的指标
- type: Resource
resource:
name: cpu
# Utilization 类型的目标值,Resource 类型的指标只支持 Utilization 和 AverageValue 类型的目标值
target:
type: Utilization
averageUtilization: 50
# Pods 类型的指标
- type: Pods
pods:
metric:
name: packets-per-second
# AverageValue 类型的目标值,Pods 指标类型下只支持 AverageValue 类型的目标值
target:
type: AverageValue
averageValue: 1k
HPA算法细节
按照 Pod 中所有 container 的资源使用平均值来计算,期望副本数=当前副本数 *(当前指标 / 期望指标)
当前度量值为 200m,目标设定值为 100m,那么由于 200.0/100.0== 2.0,副本数量将会翻倍。
如果当前指标为 50m,副本数量将会减半,因为 50.0/100.0== 0.5。
如果计算出的扩缩比例接近1.0(根据–horizontal-pod-autoscaler-tolerance 参数全局配置的容忍值,默认为 0.1),将会放弃本次扩缩。
滚动升级时的扩缩容
当你为一个 Deployment 配置自动扩缩时,你要为每个 Deployment 绑定一个HPA。
HPA管理 Deployment 的 replicas字段。
Deployment Controller 负责设置下层 ReplicaSet 的 replicas 字段,以便确保在上线及后续过程副本个数合适。
扩缩策略
扩容时:假如一个应用平时的cpu使用率是20%,突然使用率达到了100%,根据HPA的算法会使pod数量翻5倍。对基础架构和业务都有压力。
缩容时:假如pod数量是10个,需要缩容80%,如果一次性全部缩掉,业务可能会有影响。
这种情况就可以使用HPA的高级策略。
在 spec 字段的 behavior 部分可以指定一个或多个扩缩策略。
- 快速扩容
扩容时,立即新增当前9倍数量的副本数,即立即扩容到当前10倍的 Pod 数量,当然也不能超过 maxReplicas 的限制。
pod数量:1-> 10 -> 100 -> 1000
没有缩容策略,即默认负载减小后5分钟开始缩容。 - 缓慢缩容
缩容时,60秒内只能缩5个pod或者60秒内只能缩容10%的pod
这样就能保证业务的缩容是平滑的,数据指标下降也是平滑的,不是突然的下降。防止突然的流量高峰导致部分请求失败。
behavior:
scaleUp:
policies:
- type: Percent
value: 900
periodSeconds: 5
behavior:
scaleDown:
policies:
- type: Pods
value: 5
periodSeconds: 60
- type: Percent
value: 10
periodSeconds: 60
扩缩容灵敏度(冷却/延迟支持)
当使用HPA管理一组副本扩缩时,有可能因为指标动态的变化造成频繁的扩缩容,这种情况称为抖动 (Thrashing)。通过horizontal-pod-autoscaler-downscale-stabilization 设置缩容冷却时间窗口长度。默认值是5分钟
- 延长扩容时间窗口
某些大数据处理的业务,可能短时间内业务堆积,扩容出很多的pod,造成资源的浪费。我们希望可以快速扩容但是又不那么灵敏,因为即使不扩容业务也可以很快处理掉。
扩容时,需要先等待 5 分钟的时间窗口,如果5分钟内负载降下来了就不再扩容,如果负载持续超过扩容阀值才扩容,每次扩容新增 20 个 Pod。通过stabilizationWindowSeconds 设置扩容时间窗口
自定义指标
Kubernetes 默认提供 CPU 和内存作为 HPA 弹性伸缩的指标,如果有更复杂的场景需求,比如基于业务单副本 QPS 大小来进行自动扩缩容,可以安装 prometheus-adapter 来实现基于自定义指标的 Pod 扩缩容。
HPA的缺点
- 如果创建了2个hpa对象,指定了同样的target后,这2个规则会冲突,可能会出问题。
- 基于指标的弹性有滞后效应,因为弹性控制器操作的链路过长。
- 从应用负载超出國值到 HPA 完成扩容之间的时间差包括:
1). 应用指标数据已经超出阈值
2). HPA 定期执行指标收集滞后效应;
3). HPA 控制 Deployment 进行扩容的时间;
4). Pod 调度,运行时启动挂载存储和网络的时间;
5). 应用启动到服务就绪的时间。 - 很可能在突发流量出现时,还没完成弹性扩容,服务实例已经被流量击垮。
VPA
VPA全称Vertical Pod Autoscaler,即垂直Pod自动扩缩容,它根据容器资源使用率自动设置CPU和内存的requests,从而允许在节点上进行适当的调度,以便为每个Pod提供适当的资源。它既可以缩小过度请求资源的容器,也可以根据其使用情况随时提升资源不足的容量。
使用VPA的意义:
- Pod资源用其所需,提升集群节点使用效率;
- 不必运行基准测试任务来确定CPU和内存请求的合适指;
- VPA可以随时调整CPU和内存请求,无需认为操作,因此可以减少维护时间。
VPA不会改变Pod的资源limits值。
- VPA Recommender
- 监视资源利用率并计算目标值
- 查看指标历史记录、OOM 事件和 VPA 部署规范并建议公平请求。根据定义的限制请求比例提高/降低限制
- VPA Updater
- 驱逐那些需要新资源限制的 pod
- 如果定义了“updateMode: Auto”,则实现 Recommender 推荐的任何内容
- VPA Admission Controller
- 每当 VPA 更新程序驱逐并重新启动 pod 时,在新 pod 启动之前更改 CPU 和内存设置(使用 webhook)
- 当 Vertical Pod Autoscaler 设置为 updateMode 为“Auto”时,如果需要更改 pod 的资源请求,则驱逐 pod。由于 Kubernetes 的设计,修改正在运行的 Pod 的资源请求的唯一方法是重新创建 Pod
- History Storage
- 是一个存储组件(如Prometheus),使用来自API Server的利用率信息与OOM(与推荐器相同的数据)并将其持久存储。
VPA工作原理
- 用户配置 VPA
- VPA Recommender 从指标服务器读取 VPA 配置和资源利用率指标
- VPA Recommender 提供 pod 资源推荐
- VPA 更新程序读取 pod 资源建议
- VPA 更新程序启动 pod 终止
- 部署意识到 pod 已终止,并将重新创建 pod 以匹配其副本配置
- 当 Pod 处于重新创建过程中时,VPA 准入控制器获取 Pod 资源推荐。由于 Kubernetes 不支持动态更改正在运行的 pod 的资源限制,因此 VPA 无法使用新的限制更新现有 pod。它终止使用过时限制的 pod。当 pod 的控制器从 Kubernetes API 服务请求替换时,VPA 准入控制器将更新的资源请求和限制值注入到新的 pod 的规范中
- 最后,VPA 准入控制器将建议覆盖到 pod。
我们也可以在推荐模式(dry run)下运行 VPA。在这种模式下,VPA Recommender 将使用其建议值更新工作负载的 Vertical Pod Autoscaler 资源的状态字段,但不会终止 pod 或更改 pod API 请求
Recommender设计理念
Recommender 是 VPA 的主要组成部分。它负责计算推荐资源。在启动时,Recommender 从 History Storage 中获取所有 Pod 的历史资源利用率(无论它们是否使用 VPA)以及 Pod OOM 事件的历史记录。它聚合这些数据并将其保存在内存中。
推荐模型 (MVP) 假设内存和 CPU 利用率是独立的随机变量,其分布等于过去 N 天观察到的变量(推荐值为 N=8 以捕获每周峰值)。未来更高级的模型可能会尝试检测趋势、周期性和其他与时间相关的模式。
- 对于 CPU,目标是将容器使用率超过请求的高百分比(例如 95%)时的时间部分保持在某个阈值(例如 1% 的时间)以下。在此模型中,“CPU 使用率”被定义为在短时间间隔内测量的平均使用率。测量间隔越短,针对尖峰、延迟敏感的工作负载的建议质量就越高。最小合理分辨率为 1/min,推荐为 1/sec。
- 对于内存,目标是将特定时间窗口内容器使用率超过请求的概率保持在某个阈值以下(例如,24 小时内低于 1%)。窗口必须很长(≥ 24 小时)以确保由 OOM 引起的驱逐不会明显影响 (a) 服务应用程序的可用性 (b) 批处理计算的进度(更高级的模型可以允许用户指定 SLO 来控制它)。
VPA Recommender只管推荐。VPA Recommender 监视所有 Pod,不断为它们计算新的推荐资源,并将推荐值存储在 VPA 对象中。
VPA Recommender的推荐算法深受Google Borg Autopilot的moving window推荐器的启发,moving window推荐器的原理可以看下Autopilot论文。Vertical Pod Autoscaler的推荐器vpa-recommend为每个vpa对象的每个container创建存储cpu和memory使用值的decay histogram对象,定期从prometheus中拉取所有pod的资源使用情况,将container的usage写入histogram中。decay histogram的桶的大小是按照指数增长的,cpu第一个桶的大小(firstBucketSize)是0.01,memory是1e7,指数值ratio是1.05,第一个桶存储[0..firstBucketSize)的使用值的权重,则第n个桶的起始值是
value(n)=firstBucketSize∗(1+ratio+ratio2+...+ratio(n−1))=firstBucketSize∗(ration−1)(ratio−1)
每个使用值权重写入到histogram中的桶的位置是
index=floor(logratio(value∗(ratio−1)firstBucketSize+1))
value代表当前的usage值,写入cpu histogram权重值是
weight=Max(cpuRequestCores,minSampleWeight)∗2(time−begin)/CPUHistogramDecayHalfLife
其中minSampleWeight=0.1,begin是记录的第一个使用值的时间,time是当前usage的时间,默认CPUHistogramDecayHalfLife=24h。
每次推荐的预测值即为0.9*totalWeight对应的桶的初始值,计算过程:从weight不为0的最小的桶开始将weight值相加,当结果大于等于0.9倍的totalWeight后,取这个桶的初始值作为推荐值。对于memory资源,vpa-recommend还会watch集群的oom事件,对于发生oom的pod,会自动增加预测值。
主要流程
Recommender启动时,首先会加载历史数据到内存中去。历史数据来源包括两部分(二选一):
一是 VPA checkpoint,借助 ClusterStateFeeder 接口的 InitFromCheckpoints 方法将历史的 VPA checkpoint 加载至内存。
二是 Prometheus,需要配置 Prometheus 采集 k8s metrics-server 上报的 cpu、memory 资源使用情况,再通过 prometheus client 借助 ClusterStateFeeder 接口的 InitFromHistoryProvider 方法加载至内存。除此之外,历史 Pod 的标签也需要从 prometheus 采集。
加载历史数据后,Recommender 由一个定时器启动(默认周期为 1min),分别执行 RunOnce 与健康检查两个步骤。 其中我们主要关注 RunOnce 方法,在一个运行周期内,Recommender 分别执行了如下 6 个步骤:
- LoadVPAs:将 VPA 对象加载至 ClusterState (Recommender的一个核心结构体)
- LoadPods:LoadPods 将 Pods 对象加载至 ClusterState
- LoadRealTimeMetrics:将 metrics server 聚合后的容器资源使用情况加载至 ClusterState
- UpdateVPAs:以半衰指数直方图为输入,计算 cpu/memory 资源推荐值,并更新到 k8s 集群中 VPA 的 status 字段
- MaintainCheckpoints:将推荐模型使用的半衰指数直方图备份至 VPA checkpoint
- GarbageCollect:回收内存中无用数据,保证 Recommender 不占用过多的内存和 VPA 推荐值的新鲜度
Recommender中有一个核心结构图ClusterState。通过 LoadVPAs 函数获取了所有vpa信息;通过LoadPods获取的所有pod和container信息;通过 LoadRealTimeMetrics 获取所有容器的当前cpu,mem使用量。
然后再通过UpdateVPAs算出 容器的推荐cpu, mem值。
VPA 在许多应用程序中都很有用,但VPA的成熟度还不足
- 更新正在运行的Pod资源配置是VPA的一项试验性功能,会导致Pod的重建和重启,而且有可能被调度到其他的节点上。
- VPA不会驱逐没有在副本控制器管理下的Pod。目前对于这类Pod,Auto模式等同于Initial模式。
- 目前VPA不能和监控CPU和内存度量的Horizontal Pod Autoscaler(HPA)同时运行,除非HPA只监控其他定制化的或者外部的资源度量。
- VPA使用admission webhook作为其准入控制器。如果集群中有其他的 admissionwebhook,需要确保它们不会与VPA发生冲突。准入控制器的执行顺序定义在APIServer的配置参数中。
- VPA会处理出现的绝大多数OOM(Out Of Memory)的事件,但不保证所有的场景下都有效。
- VPA的性能还没有在大型集群中测试过。
- VPA对Pod 资源requests的修改值可能超过实际的资源上限,例如节点资源上限、空闲资源或资源配额,从而造成Pod处于Pending 状态无法被调度。同时使用集群自动伸缩(ClusterAutoscaler)可以一定程度上解决这个问题。
- 多个VPA同时匹配同一个Pod会造成未定义的行为。
工业界弹性方案
通过弹性能够增效降本,实现企业的成本控制。但成本优化本身有几大矛盾。
第一对是业务稳定性跟资源利用率间的矛盾,很多业务没有人维护,其稳定性全靠冗余来支撑,而冗余天然跟资源利用率就是一个互斥的话题,没有冗余,业务的稳定性就会降低;
第二对是业务投入与技术投入的矛盾,假设部门只有一个 hire count,到底应该去做业务,还是去把架构搞得可弹性,这其实也是一个不可调和的矛盾;
第三对是企业内不同角色 / 组织的矛盾,不同团队由于各自目标,最终演化成了业务团队与资源团队之间的矛盾。
虽然这些矛盾是本质性的,无法完全解决,但企业可以通过技术手段和组织手段减少这些矛盾。组织手段可以分为两个方面:一方面可以通过奖励进行驱动,拿腾讯举例,只要能够观测到每个业务真实的成本和资源利用率,接下来也不需要强制做什么,只需要挂一个红黑榜,给资源利用率前十名的团队奖励,请排名后三位的团队回复邮件,解释说明资源利用率低的原因。
另一方面,企业还可以设置一些增强手段——建立成本文化,即FinOps,意思是代码设计之初就需要考虑冗余和架构,企业要监控性能、稳定性等数据的指标。它让每个团队都可以像监控业务可用性一样监控业务成本,像优化业务可用性一样去持续优化成本。
腾讯内部推出了一个云原生弹性成熟度模型,用来监控业务组件在过去一周中是否发生过伸缩。如果监测到从来没有发生过伸缩业务,就让它能够伸缩,有这个能力之后,再去推动弹性改造,最终,通过这种方式资源利用率提升了 30%-40%。
实际上,资源利用率提升的本质是消除浪费,一种方式是推动业务按需使用,即为弹性伸缩,用多少申请多少,不用就把它缩下去;第二种是业务不用动,平台方进行资源腾挪复用,即为在离线混部。
Crane
Crane(Cloud Resource Analytics and Economics)是一个基于 FinOps 的云资源分析与成本优化平台。它的愿景是在保证客户应用运行质量的前提下实现极致的降本。
开源项目:https://github.com/gocrane
Main Features
- 成本可视化和优化评估
- 提供一组 Exporter 计算集群云资源的计费和账单数据并存储到你的监控系统,比如 Prometheus。
- 多维度的成本洞察,优化评估。通过 Cloud Provider 支持多云计费。
- 推荐框架
提供了一个可扩展的推荐框架以支持多种云资源的分析,内置了多种推荐器:资源推荐,副本推荐,闲置资源推荐。 - 基于预测的水平弹性器
EffectiveHorizontalPodAutoscaler 支持了预测驱动的弹性。它基于社区 HPA 做底层的弹性控制,支持更丰富的弹性触发策略(预测,观测,周期),让弹性更加高效,并保障了服务的质量。了解更多。 - 负载感知的调度器
动态调度器根据实际的节点利用率构建了一个简单但高效的模型,并过滤掉那些负载高的节点来平衡集群。了解更多。 - 基于 QoS 的混部
架构
Crane 的整体架构如下:
- Craned
Craned 是 Crane 的最核心组件,它管理了 CRDs 的生命周期以及API。Craned 通过 Deployment 方式部署且由两个容器组成:- Craned: 运行了 Operators 用来管理 CRDs,向 Dashboard 提供了 WebApi,Predictors 提供了 TimeSeries API,提供预测能力,集合VPA的算法和DSP(周期性预测),集中资源预测,驱动在整个生态系统。通过预测,回收接下来一小时、一天的空闲资源(通过Extend-resource实现,更新节点的 扩展资源)
- Dashboard: 基于 TDesign's Starter 脚手架研发的前端项目,提供了易于上手的产品功能
- Fadvisor
Fadvisor 提供一组 Exporter 计算集群云资源的计费和账单数据并存储到你的监控系统,比如 Prometheus。Fadvisor 通过 Cloud Provider 支持了多云计费的 API。 - Metric Adapter
Metric Adapter 实现了一个 Custom Metric Apiserver. Metric Adapter 读取 CRDs 信息并提供基于 Custom/External Metric API 的 HPA Metric 的数据。 - Crane Agent
Crane Agent 通过 DaemonSet 部署在集群的节点上。
资源预测模块 Predictors
定义一个CRD,TimeSeriesPrediction对象,按Node或者Pod汇聚;通过controller更新节点资源
成本展示
通过Dashboard展示CPU/Memory用量以及Cost使用情况,浪费识别。并提供一键更新资源占用的功能。
成本优化
常规优化手段包括弹性和混部。
- 弹性,即按作业负载动态调节实例副本数或单实例的资源上
- 负载高峰扩容以保证业务服务等级
- 负载低估缩容以回收资源减少浪费
- 混部
- 在负载低估运行离线作业以提升总体资源利用率
- TKE-Scheduler专为离线场景优化:gang-scheduling, all-or-nothing,10X性能提升
而社区原生的弹性能力也有不足
- EHPA(Effect HPA) 提供metric sever来驱动HPA,也就是通过预测值来做弹性,解决HPA滞后的问题。
- 引入AdvancedHorizontalPodAutoscaler对象,该对象关联TimeSeriesPrediction和 HPA
- Metric-Adapter通过External Metric Provider 方式注册到K8s集群中,并将 TimeSeriesPrediction.Status中的预测结果转化为External Metrics
- HPA-Controller 通过External Metric API从 Metric-Adapter 获取应用的预测指标,该指标驱动HPA行为
- EVPA(Effect VPA)
- Craned中的NodeResourceController 控制器基于预测结果将空闲资源更新至节点extend-resource。
- 引入SchedulingPolicy对象,为特定ProrityClass 定义资源转化策略。
- 基于Mutating Webhook,将特定PriorityClass的Pod中的资源请求转化为extend-resource,实现回收资源再利用。
- 资源回收再利用可以被认为是广义的实时动态VPA能力(InstanceVerticalPodAutoscaler)。
服务质量保证
- 配合PriorityClass为业务定级,通过QoSEnsurancePolicy定义做什么检测,做什么回避的action
- 针对节点水位和业务SLO进行多维度异常指标检测。
- 当检测指标出现异常时,按预定义优先级进行主动回避,确保高优任务SLO。
参考:
HPA总结
vpa recommender源码分析
Kubernetes-自动扩展器HPA、VPA、CA
IT成本优化漫谈
Crane-scheduler
腾讯大规模业务集群的云原生成本优化实践