一、应用资源优化
资源优化是 FinOps 中常见的优化手段,基于 Kubernetes 应用的特点总结出云原生应用的资源优化模型:
图中五条线从上到下分别是:
- 节点容量:集群中所有节点的资源总量,对应集群的 Capacity
- 已分配:应用申请的资源总量,对应 Pod Request
- 周峰值:应用在过去一段时间内资源用量的峰值。周峰值可以预测未来一段时间内的资源使用,通过周峰值配置资源规格的安全性较高,普适性更强
- 日均峰值:应用在近一天内资源用量的峰值
- 均值:应用的平均资源用量,对应 Usage
其中资源的闲置分两类:
- Resource Slack:Capacity 和 Request 之间的差值
- Usage Slack:Request 和 Usage 之间的差值
Total Slack = Resource Slack + Usage Slack
资源优化的目标是 减少 Resource Slack 和 Usage Slack。模型中针对如何一步步减少浪费提供了四个步骤,从上到下分别是:
- 提升装箱率:提升装箱率能够让 Capacity 和 Request 更加接近。手段有很多,例如:动态调度器、腾讯云的云原生节点的节点放大功能等
- 业务规格调整减少资源锁定:根据周峰值资源用量调整业务规格使的 Request 可以减少到周峰值线。资源推荐和副本推荐可以帮助应用实现此目标。
- 业务规格调整+扩缩容兜底流量突发:在规格优化的基础上再通过 HPA 兜底突发流量使的 Request 可以减少到日均峰值线。此时 HPA 的目标利用率偏低,仅为应对突发流量,绝大多数时间内不发生自动弹性。弹性推荐可以扫描出适合做弹性的应用并提供HPA配置。
- 业务规格调整+扩缩容应对日常流量变化:在规格优化的基础上再通过 HPA 应用日常流量使的 Request 可以减少到均值。此时 HPA 的目标利用率等于应用的平均利用率。EHPA实现了基于预测的水平弹性,帮助更多应用实现智能弹性。
推荐规则
添加集群之后勾选了安装推荐规则之后,会在推荐规则里面创建4个推荐规则。
通过查看 Recommendationrules CRD可以查看到这四个资源。
目前 Crane 支持了以下 Recommender:
- 资源推荐: 通过 VPA 算法分析应用的真实用量推荐更合适的资源配置
- 副本数推荐: 通过 HPA 算法分析应用的真实用量推荐更合适的副本数量
- HPA 推荐: 扫描集群中的 Workload,针对适合适合水平弹性的 Workload 推荐 HPA 配置
- 闲置节点推荐: 扫描集群中的闲置节点
查看 workloads-rule
规则的yaml文件
apiVersion: analysis.crane.io/v1alpha1
kind: RecommendationRule
metadata:
labels:
analysis.crane.io/recommendation-rule-preinstall: "true"
name: workloads-rule
spec:
namespaceSelector: # 所有的namespace
any: true
recommenders: # 资源推荐器
- name: Replicas
- name: Resource
resourceSelectors: # 资源信息
- apiVersion: apps/v1
kind: Deployment
- apiVersion: apps/v1
kind: StatefulSet
runInterval: 24h # 24小时运行一次
在该示例中:
- 每隔24小时运行一次分析推荐,
runInterval
格式为时间间隔,比如: 1h,1m,设置为空表示只运行一次。 - 待分析的资源通过配置
resourceSelectors
数组设置,每个resourceSelector
通过 kind,apiVersion,name 选择 k8s 中的资源,当不指定 name 时表示在namespaceSelector
基础上的所有资源 namespaceSelector
定义了待分析资源的 namespace,any: true
表示选择所有 namespacerecommenders
定义了待分析的资源需要通过哪些 Recommender 进行分析。目前支持的类型:recommenders- 资源类型和
recommenders
需要可以匹配,比如 Resource 推荐默认只支持 Deployments 和 StatefulSets,每种 Recommender 支持哪些资源类型请参考 recommender 的文档
资源推荐
查看资源推荐
查看yaml,得知 kind:Recommendation
CRD 资源 。
根据现有规则,根据每一个命名空间下的服务会生成对应的副本数、资源、service的推荐规则的资源。
采纳命令
点击查看采纳命令,可以获取一个服务的
patchData=`kubectl get recommend workloads-rule-resource-4w5hg -n testing-mingzhu-1 -o jsonpath='{.status.recommendedInfo}'`;
kubectl patch Deployment iov-parking-mqtt -n testing-mingzhu-1 --patch "${patchData}"
查看 workloads-rule-resource-4w5hg
的 yaml
...
spec:
adoptionType: StatusAndAnnotation
completionStrategy:
completionStrategyType: Once
targetRef:
apiVersion: apps/v1
kind: Deployment
name: iov-parking-mqtt
namespace: testing-mingzhu-1
type: Resource
status:
action: Patch
conditions:
- lastTransitionTime: "2023-12-03T08:20:48Z"
message: Recommendation is ready
reason: RecommendationReady
status: "True"
type: Ready
currentInfo: '{"spec":{"template":{"spec":{"containers":[{"name":"iov-parking-mqtt","resources":{"requests":{"cpu":"300m","memory":"500Mi"}}}]}}}}'
lastUpdateTime: "2023-12-10T08:49:49Z"
recommendedInfo: '{"spec":{"template":{"spec":{"containers":[{"name":"iov-parking-mqtt","resources":{"requests":{"cpu":"114m","memory":"120586239"}}}]}}}}'
recommendedValue: |
resourceRequest:
containers:
- containerName: iov-parking-mqtt
target:
cpu: 114m
memory: "120586239"
targetRef: {}
命令的意思是获取 recommendedInfo
的推荐,通过kubectl的命令 patch Deployment 的信息。
资源优化
Kubernetes 用户在创建应用资源时常常是基于经验值来设置 request 和 limit。通过资源推荐的算法分析应用的真实用量推荐更合适的资源配置,您可以参考并采纳它提升集群的资源利用率。
优化前:
优化后:
命名空间成本分布:
优化前:
优化后(金额差距太多,可能和数据少了几天有关):
使用24小时颗粒度的图表来看,cpu,内存都有降低,资源分配的也更合理。
实现原理
资源推荐按以下步骤完成一次推荐过程:
- 通过监控数据,获取 Workload 过去一周的 CPU 和 Memory 历史用量。
- 基于历史用量通过 VPA Histogram 取 P99 百分位后再乘以放大系数
- OOM 保护:如果容器存在历史的 OOM 事件,则考虑 OOM 时的内存适量增大内存推荐结果
- 资源规格规整:按指定的容器规格对推荐结果向上取整
基本原理是基于历史的资源用量,将 Request 配置成略高于历史用量的最大值并且考虑 OOM,Pod 规格等因素。
VPA 算法
资源推荐的核心思想是基于历史资源用量推荐合理的资源配置,我们采用了社区 VPA Histogram 算法来实现。VPA 算法将历史的资源用量放到直方图中,找到资源用量的 P99 百分数,将百分数乘以放大系数作为推荐值。
VPA 算法的 output 是 cpu、内存指标的 P99 用量。为了给应用预留 buffer,推荐结果还会乘以放大系数。资源推荐支持两种方式配置放大系数:
- 扩大比例:推荐结果=P99用量 * (1 + 放大系数),对应配置:cpu-request-margin-fraction 和 mem-request-margin-fraction
- 目标峰值利用率:推荐结果=P99用量/目标峰值利用率,对应配置:cpu-target-utilization 和 mem-target-utilization
在您有应用的目标峰值利用率目标时,推荐使用目标峰值利用率方式放大推荐结果。
OOM 保护
Craned 运行了单独的组件 OOMRecorder ,它记录了集群中 container OOM 的事件,资源推荐会读取 OOM 事件获取 OOM 时刻的内存使用,将内存使用乘以放大系数后与 VPA 的内存推荐结果比较,取较大值
资源规格规整
在 Kubernetes Serverless 中,Pod 的 cpu、内存规格是预设的,资源推荐支持对推荐结果按预设的资源规格向上取整,例如,基于历史用量的 cpu 推荐值为0.125核,资源规格规整后向上取整后为 0.25核。用户也可以通过修改规格配置来满足自己环境的规格需求。
通过 Prometheus Metric 监控推荐结果
推荐资源的推荐结果会记录到 Metric:crane_analysis_resource_recommendation
如何验证推荐结果的准确性
用户可以通过以下 Prom query 得到 Workload Container 的资源用量,推荐值会略高于历史用量的最大值并且考虑 OOM,Pod 规格等因素。
以 crane-system 的 Deployment Craned 为例,用户可以将 container, namespace, pod 换成希望验证的推荐 TargetRef。
irate(container_cpu_usage_seconds_total{container!="POD",namespace="crane-system",pod=~"^craned.*$",cnotallow="craned"}[3m]) # cpu usage
container_memory_working_set_bytes{container!="POD",namespace="crane-system",pod=~"^craned.*$",cnotallow="craned"} # memory usage
二、动态调度器
kubernetes 的原生调度器只能通过资源请求来调度 pod,这很容易造成一系列负载不均的问题:
- 对于某些节点,实际负载与资源请求相差不大,这会导致很大概率出现稳定性问题。
- 对于其他节点来说,实际负载远小于资源请求,这将导致资源的巨大浪费。
为了解决这些问题,动态调度器根据实际的节点利用率构建了一个简单但高效的模型,并过滤掉那些负载高的节点来平衡集群。
架构
如上图,动态调度器依赖于Prometheus
和Node-exporter
收集和汇总指标数据,它由两个组件组成:
Node-annotator
目前是 Crane-scheduler-controller
的一个模块.
Node-annotator
定期从 Prometheus 拉取数据,并以注释的形式在节点上用时间戳标记它们。Dynamic plugin
直接从节点的注释中读取负载数据,过滤并基于简单的算法对候选节点进行评分。
安装 Crane-Schedule
Crane-scheduler 是一组基于scheduler framework的调度插件。
配置 Prometheus 规则
如果根据 FinOps 之 Crane 成本优化(一) 进行安装的,已经配置过了。
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: example-record
spec:
groups:
- name: cpu_mem_usage_active
interval: 30s
rules:
- record: cpu_usage_active
expr: 100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[30s])) * 100)
- record: mem_usage_active
expr: 100*(1-node_memory_MemAvailable_bytes/node_memory_MemTotal_bytes)
- name: cpu-usage-5m
interval: 5m
rules:
- record: cpu_usage_max_avg_1h
expr: max_over_time(cpu_usage_avg_5m[1h])
- record: cpu_usage_max_avg_1d
expr: max_over_time(cpu_usage_avg_5m[1d])
- name: cpu-usage-1m
interval: 1m
rules:
- record: cpu_usage_avg_5m
expr: avg_over_time(cpu_usage_active[5m])
- name: mem-usage-5m
interval: 5m
rules:
- record: mem_usage_max_avg_1h
expr: max_over_time(mem_usage_avg_5m[1h])
- record: mem_usage_max_avg_1d
expr: max_over_time(mem_usage_avg_5m[1d])
- name: mem-usage-1m
interval: 1m
rules:
- record: mem_usage_avg_5m
expr: avg_over_time(mem_usage_active[5m])
Prometheus 的采样间隔必须小于30秒,不然可能会导致规则无法正常生效。如:cpu_usage_active
。
安装 Crane-schedule 作为第二个调度器
有两种选择:
- 安装 Crane-scheduler 作为第二个调度器
- 用 Crane-scheduler 替换原生 Kube-scheduler
这里使用第一种方法。替换原生调度器需要经过谨慎测试,安装方法参考官网:用 Crane-scheduler 替换原生 Kube-scheduler
将下面的 global.prometheusAddr 替换成自己的 prometheus 的 地址,要加 http:// 协议。
helm repo add crane https://finops-helm.pkg.coding.net/gocrane/gocrane
helm install scheduler -n crane-system --create-namespace --set global.prometheusAddr="http://prometheus-k8s.monitoring:9090" crane/scheduler
检查是否安装成功
查看 crane-scheduler-controller Pod 的日志。
查看node节点的 annotations
默认调度策略文件
Crane-Schedule
默认的调度策略,配置文件如下:
# dynamic-scheduler-policy
# policy.yaml
apiVersion: scheduler.policy.crane.io/v1alpha1
kind: DynamicSchedulerPolicy
spec:
syncPolicy:
##cpu usage
- name: cpu_usage_avg_5m
period: 3m
- name: cpu_usage_max_avg_1h
period: 15m
- name: cpu_usage_max_avg_1d
period: 3h
##memory usage
- name: mem_usage_avg_5m
period: 3m
- name: mem_usage_max_avg_1h
period: 15m
- name: mem_usage_max_avg_1d
period: 3h
predicate:
##cpu usage
- name: cpu_usage_avg_5m
maxLimitPecent: 0.65
- name: cpu_usage_max_avg_1h
maxLimitPecent: 0.75
##memory usage
- name: mem_usage_avg_5m
maxLimitPecent: 0.65
- name: mem_usage_max_avg_1h
maxLimitPecent: 0.75
priority:
###score = sum(() * weight) / len, 0 <= score <= 10
##cpu usage
- name: cpu_usage_avg_5m
weight: 0.2
- name: cpu_usage_max_avg_1h
weight: 0.3
- name: cpu_usage_max_avg_1d
weight: 0.5
##memory usage
- name: mem_usage_avg_5m
weight: 0.2
- name: mem_usage_max_avg_1h
weight: 0.3
- name: mem_usage_max_avg_1d
weight: 0.5
hotValue:
- timeRange: 5m
count: 5
- timeRange: 1m
count: 2
动态调度器提供了一个默认值调度策略并支持用户自定义策略。默认策略依赖于以下指标:
cpu_usage_avg_5m
cpu_usage_max_avg_1h
cpu_usage_max_avg_1d
mem_usage_avg_5m
mem_usage_max_avg_1h
mem_usage_max_avg_1d
在调度的Filter
阶段,如果该节点的实际使用率大于上述任一指标的阈值,则该节点将被过滤。而在Score
阶段,最终得分是这些指标值的加权和。
默认调度文件
里面包含的就是调度器的配置文件,我们会在配置中启用 Dynamic 动态调度插件。
# scheduler-config
# scheduler-config.yaml
apiVersion: kubescheduler.config.k8s.io/v1beta2
kind: KubeSchedulerConfiguration
leaderElection:
leaderElect: true
profiles:
- schedulerName: crane-scheduler # 调度器名字
plugins:
filter:
enabled:
- name: Dynamic # 开启插件名字
score:
enabled:
- name: Dynamic # 调度器打分
weight: 3
pluginConfig:
- name: Dynamic
args: # 配置文件为上面的默认调度策略文件
policyConfigPath: /etc/kubernetes/policy.yaml
使用 Crane-Schedule 调度器
使用 schedulerName: crane-scheduler
指定调度器。
apiVersion: apps/v1
kind: Deployment
metadata:
name: cpu-stress
spec:
selector:
matchLabels:
app: cpu-stress
replicas: 1
template:
metadata:
labels:
app: cpu-stress
spec:
schedulerName: crane-scheduler
hostNetwork: true
tolerations:
- key: node.kubernetes.io/network-unavailable
operator: Exists
effect: NoSchedule
containers:
- name: stress
image: docker.io/gocrane/stress:latest
command: ["stress", "-c", "1"]
resources:
requests:
memory: "1Gi"
cpu: "1"
limits:
memory: "1Gi"
cpu: "1"
部署到k8s集群
$ kubectl apply -f test-crane-schedule.yaml -n crane-system
deployment.apps/cpu-stress created
查看describe,使用了期望的调度器。
卸载测试deployment
$ kubectl delete -f test-crane-schedule.yaml -n crane-system
deployment.apps "cpu-stress" deleted
三、精细化调度器
以后用到以下功能再继续更新。
https://gocrane.io/zh-cn/docs/tutorials/node-resource-tpolology-scheduler-plugins/
四、EHPA
https://gocrane.io/zh-cn/docs/tutorials/using-effective-hpa-to-scaling-with-effectiveness/
五、时序预测
https://gocrane.io/zh-cn/docs/tutorials/using-time-series-prediction/