1 - 介绍

Kapacity 整体介绍

Kapacity 旨在为用户提供一套具备完善技术风险能力的、智能且开放的云原生容量技术,帮助用户安全稳定地实现极致降本增效,解决容量相关问题。

Kapacity 基于蚂蚁集团内部容量系统的核心理念和多年的大规模生产实践经验而构建,该内部容量系统目前已能安全稳定地为蚂蚁持续节省年均约 10w 核的算力成本,同时,Kapacity 也结合了来自云原生社区的最佳实践。

观看我们在 KubeCon China 2023 上的中文演讲「我们如何构建生产级 HPA:从智能算法到无风险自动扩缩容」来深入了解 Kapacity Intelligent HPA 的设计思想和实现原理!

核心能力

Intelligent HPA

Kubernetes HPA 是一项用于对云原生工作负载进行自动扩缩容的常见技术,但它在实际大规模生产使用上的效果和实用度上却并不理想,主要由以下几个原因导致:

  • HPA 的自动扩缩容通过响应式的方式驱动,仅当应用负载已经超出设定水位时才会触发扩容,此时容量风险已经出现,只能起到应急的作用而非提前规避风险,尤其对于自身启动时间较长的应用,几乎起不到快速应对流量洪峰的作用。
  • HPA 通过简单的指标折比来计算扩缩容目标副本数,只适用于应用副本数和相关指标呈严格线性相关的理想场景,但实际生产当中应用的各类指标和副本数之间存在错综复杂的关系,该算法很难得到符合容量水位要求的副本数。
  • 容量弹性作为变更故障率较高的一类场景,HPA 除了支持限制扩缩容速率外没有提供任何其他的风险防控手段,在稳定性要求较高的生产环境中大规模落地是很难令人放心的。
  • HPA 作为 Kubernetes 内置能力,一方面自然有其开箱即用的好处,但另一方面也使其绑定在了具体的 k8s 版本上,自身的行为难以被用户扩展或调整,难以满足各类用户在不同应用场景下的定制化需求。

为此,我们构建了 Intelligent HPA (IHPA),它是一个更加智能化的、具备完善技术风险能力的且高度可扩展定制的 HPA 替代方案。它具有如下几个核心特性:

智能弹性

容量弹性本质上是一个数据驱动决策的过程,而非简单的扩缩容。IHPA 支持在不同场景因地制宜地使用不同的算法,除了简单的定时与响应式算法,其还支持多种智能算法如预测式、突增式等,同时支持按自定义配置策略对算法进行组合生效,从而能够适配更多业务场景、扩缩容的决策也更为精准。

以预测式算法为例,在生产上,应用的容量水位通常会受到多条外部流量,甚至是自身定时任务、机器性能等的影响,且副本数与容量水位之间的关系也未必是线性,这为基于应用容量的副本数预测带来了很大的挑战。

为此,IHPA 引入了蚂蚁在内部大规模弹性生产实践中打磨出的一套基于机器学习的预测式算法,该算法首先通过针对流量时序做过优化的 Swish Net for Time Series Forecasting (SNTSF) 对潜在影响应用容量水位的多条流量进行时序预测,随后通过 Linear-Residual Model 将这些组分流量和应用容量及其对应副本数进行综合建模,最终推理得出应用未来的推荐副本数。

通过这种流量驱动容量的思想,该算法能够很好地应对生产上多周期流量、趋势变化流量、多条流量共同影响容量、容量与副本数呈非线性关系等复杂场景,通用性和准确性兼具。

多级弹性

不同于原生 HPA 只支持工作负载扩缩容,IHPA 支持在整个弹性过程中精细化地控制工作负载下每一个 Pod 的状态,通过灵活的 Pod 状态转换提升弹性效率并降低弹性风险。

目前,Kapacity 中定义了下面几种 Pod 状态:

  • Online:Pod 正常对外提供服务的状态(Running and Ready),也是新扩容 Pod 的默认状态。
  • Cutoff:Pod 流量完全摘除的状态(Running but Not Ready)。在实践上,支持优先缩容到此状态,并辅以一段稳定性观察期,一旦发现问题能够秒级回滚到 Online 状态。
  • Standby:Pod 资源被换出,保持在低水位的状态。相比于 Cutoff 状态,该状态能够实际释放 Pod 所占用资源供其他应用使用,也支持分钟级回滚到 Online 状态。
  • Deleted:Pod 被真正删除的状态。实际到了该状态 Pod 本身就不存在了。

稳定性保障

IHPA 吸收了蚂蚁多年大规模弹性生产实践的经验教训,沉淀出了独有的弹性变更稳定性保障能力。

灰度变更

IHPA 在执行扩缩容时支持采用自定义灰度分批的变更策略,最大程度地减小了弹性变更的爆炸半径;同时还支持加入上文提到的 Cutoff/Standby 中间态实现多阶段灰度,提升应急回滚速度,进一步降低弹性变更风险。

下面以使用 Cutoff 作为中间态的一次灰度缩容为例:某应用的工作负载原来有 6 个 Pod,期望缩容到 2 个,此时会按照用户的灰度配置自动分批变更 Pod 为 Cutoff 状态,每次变更都会间隔一定时间进行稳定性观察。当待缩容 Pod 都切换为 Cutoff 状态后会进入最终的额外稳定性观察期,如果最后没有发现风险则再执行真正的缩容,如果期间发现风险,则能够快速回滚到 Online 状态。

稳定性检查与变更熔断

在弹性变更的过程中,有时候仅仅通过观察弹性指标是否异常无法及时暴露出风险,因此,IHPA 支持用户自定义的变更期稳定性检查,包括自定义指标异常判断等,多维度地分析变更状况,一旦发现异常支持自动采取应急熔断措施,如变更暂停或变更回滚,真正做到弹性变更常态化无人值守。

开放扩展

Kapacity 在项目设计之初就极度关注模块化与扩展性,比如整个 IHPA 能力就拆分为了管控、决策、执行三大模块,任一模块都可以做替换或扩展,其中可扩展的部分包括但不限于:

  • 可自定义生成应用水平画像(目标副本数)的具体算法以及算法所依赖的指标、参数等
  • 可扩展多级弹性支持的工作负载、自定义 Pod 状态切换的逻辑等
  • 可自定义变更期稳定性检查逻辑
  • 可自定义 Pod 缩容的优先级(对于支持的工作负载)
  • ……

借助其高度可扩展定制的特性,用户可以很轻松地将 Kapacity 和其他开源上下游方案或者内部自建系统结合使用,各取所需、各取所长。

2 - 开始使用

了解如何安装和上手 Kapacity

2.1 - 安装指南

了解如何安装或卸载 Kapacity

环境准备

安装流程

安装 cert-manager

使用 Helm 安装 cert-manager

helm repo add jetstack https://charts.jetstack.io
helm repo update

helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set installCRDs=true

安装 Prometheus

使用 Helm 安装 Prometheuskube-state-metrics

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

helm install \
  prometheus prometheus-community/prometheus \
  --namespace prometheus \
  --create-namespace \
  --set alertmanager.enabled=false \
  --set prometheus-node-exporter.enabled=false \
  --set prometheus-pushgateway.enabled=false

可以使用如下命令查看 Prometheus Server 的 ClusterIP 和端口:

kubectl get svc -n prometheus prometheus-server
NAME                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
prometheus-server   ClusterIP   10.104.214.48   <none>        80/TCP    5m

安装 Kapacity

使用 Helm 安装 Kapacity。其中的 Prometheus Server 地址相关参数即为上一步查看到的值。

helm repo add kapacity https://traas-stack.github.io/kapacity-charts
helm repo update

helm install \
  kapacity-manager kapacity/kapacity-manager \
  --namespace kapacity-system \
  --create-namespace \
  --set prometheus.address=http://<prometheus-server-clusterip>:<prometheus-server-port> 

验证 Kapacity 安装是否成功

kubectl get deploy -n kapacity-system
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
kapacity-manager   1/1     1            1           5m

卸载流程

helm uninstall kapacity-manager -n kapacity-system
helm uninstall prometheus -n prometheus
helm uninstall cert-manager -n cert-manager

2.2 - 快速开始

快速上手 Kapacity

2.2.1 - IHPA (Intelligent HPA)

快速上手 IHPA

IHPA (Intelligent HPA) 是一个更加智能化的、具备完善技术风险能力的且高度可扩展定制的 HPA 替代方案。

你可以跟随下面的指南来快速尝试 IHPA 的一些核心功能。

2.2.1.1 - 使用定时扩缩容

准备开始

你需要拥有一个安装了 Kapacity 的 Kubernetes 集群。

运行示例工作负载

下载 nginx-statefulset.yaml 文件,并执行以下命令以运行一个 NGINX 服务:

kubectl apply -f nginx-statefulset.yaml

验证服务部署完成:

kubectl get po
NAME      READY   STATUS    RESTARTS   AGE
nginx-0   1/1     Running   0          5s

创建配置了定时画像源的 IHPA

下载 cron-portrait-sample.yaml 文件,其内容如下所示:

apiVersion: autoscaling.kapacitystack.io/v1alpha1
kind: IntelligentHorizontalPodAutoscaler
metadata:
  name: cron-portrait-sample
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: StatefulSet
    name: nginx
  minReplicas: 1
  maxReplicas: 10
  portraitProviders:
  - type: Cron
    priority: 1
    cron:
      crons:
      - name: cron-1
        start: 0 * * * *
        end: 10 * * * *
        replicas: 1
      - name: cron-2
        start: 10 * * * *
        end: 20 * * * *
        replicas: 2
      - name: cron-3
        start: 20 * * * *
        end: 30 * * * *
        replicas: 3
      - name: cron-4
        start: 30 * * * *
        end: 40 * * * *
        replicas: 4
      - name: cron-5
        start: 40 * * * *
        end: 50 * * * *
        replicas: 5

执行以下命令创建该 IHPA:

kubectl apply -f cron-portrait-sample.yaml

验证结果

通过查看 IHPA 的事件可以看到工作负载的副本数正按我们的配置进行动态调整:

kubectl describe ihpa cron-portrait-sample
...
Events:
  Type     Reason                Age                From             Message
  ----     ------                ----               ----             -------
  Normal   CreateReplicaProfile  38m                ihpa_controller  create ReplicaProfile with onlineReplcas: 3, cutoffReplicas: 0, standbyReplicas: 0
  Normal   UpdateReplicaProfile  33m (x2 over 33m)  ihpa_controller  update ReplicaProfile with onlineReplcas: 3 -> 4, cutoffReplicas: 0 -> 0, standbyReplicas: 0 -> 0
  Normal   UpdateReplicaProfile  23m                ihpa_controller  update ReplicaProfile with onlineReplcas: 4 -> 5, cutoffReplicas: 0 -> 0, standbyReplicas: 0 -> 0
  Warning  NoValidPortraitValue  13m                ihpa_controller  no valid portrait value for now
  Normal   UpdateReplicaProfile  3m15s              ihpa_controller  update ReplicaProfile with onlineReplcas: 5 -> 1, cutoffReplicas: 0 -> 0, standbyReplicas: 0 -> 0

你也可以通过直接观察工作负载的副本数变化来验证。

清理资源

执行以下命令清理所有资源:

kubectl delete -f cron-portrait-sample.yaml 
kubectl delete -f nginx-statefulset.yaml 

2.2.1.2 - 使用响应式扩缩容

准备开始

你需要拥有一个安装了 Kapacity 与 Prometheus 的 Kubernetes 集群。

运行示例工作负载

下载 nginx-statefulset.yaml 文件,并执行以下命令以运行一个 NGINX 服务:

kubectl apply -f nginx-statefulset.yaml

验证服务部署完成:

kubectl get po
NAME      READY   STATUS    RESTARTS   AGE
nginx-0   1/1     Running   0          5s

创建配置了动态响应式画像源的 IHPA

下载 dynamic-reactive-portrait-sample.yaml 文件,其内容如下所示:

apiVersion: autoscaling.kapacitystack.io/v1alpha1
kind: IntelligentHorizontalPodAutoscaler
metadata:
  name: dynamic-reactive-portrait-sample
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: StatefulSet
    name: nginx
  minReplicas: 1
  maxReplicas: 10
  portraitProviders:
  - type: Dynamic
    priority: 1
    dynamic:
      portraitType: Reactive
      metrics:
      - type: Resource
        resource:
          name: cpu
          target:
            type: Utilization
            averageUtilization: 30
      algorithm:
        type: KubeHPA

执行以下命令创建该 IHPA:

kubectl apply -f dynamic-reactive-portrait-sample.yaml

增加负载

执行以下命令获取 NGINX 服务的 ClusterIP 和端口:

kubectl get svc nginx
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
nginx        ClusterIP   10.111.21.74   <none>        80/TCP     13m

启动一个不同的 Pod 作为客户端,该 Pod 会不断地向 NGINX 服务发出请求,其中的服务地址和端口请替换为上一步中得到的值:

# 在单独的终端中运行它以便负载生成继续,你可以继续执行其余步骤
kubectl run -i --tty load-generator --rm --image=busybox --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://<service-ip>:<service-port> > /dev/null; done"

等待几分钟后,可以通过 IHPA 的事件看到工作负载被扩容了:

kubectl describe ihpa dynamic-reactive-portrait-sample
...
Events:
  Type    Reason                Age    From             Message
  ----    ------                ----   ----             -------
  Normal  CreateReplicaProfile  6m58s  ihpa_controller  create ReplicaProfile with onlineReplcas: 1, cutoffReplicas: 0, standbyReplicas: 0
  Normal  UpdateReplicaProfile  3m45s  ihpa_controller  update ReplicaProfile with onlineReplcas: 1 -> 6, cutoffReplicas: 0 -> 0, standbyReplicas: 0 -> 0

停止产生负载

在我们创建 busybox 容器的终端中,输入 <Ctrl> + C 来终止负载的产生。

等待几分钟后,可以通过 IHPA 的事件看到工作负载被缩容了:

kubectl describe ihpa dynamic-reactive-portrait-sample
...
Events:
  Type    Reason                Age    From             Message
  ----    ------                ----   ----             -------
  Normal  CreateReplicaProfile  9m58s  ihpa_controller  create ReplicaProfile with onlineReplcas: 1, cutoffReplicas: 0, standbyReplicas: 0
  Normal  UpdateReplicaProfile  6m45s  ihpa_controller  update ReplicaProfile with onlineReplcas: 1 -> 6, cutoffReplicas: 0 -> 0, standbyReplicas: 0 -> 0
  Normal  UpdateReplicaProfile  3m15s  ihpa_controller  update ReplicaProfile with onlineReplcas: 6 -> 4, cutoffReplicas: 0 -> 0, standbyReplicas: 0 -> 0
  Normal  UpdateReplicaProfile  2m45s  ihpa_controller  update ReplicaProfile with onlineReplcas: 4 -> 1, cutoffReplicas: 0 -> 0, standbyReplicas: 0 -> 0

清理资源

执行以下命令清理所有资源:

kubectl delete -f dynamic-reactive-portrait-sample.yaml 
kubectl delete -f nginx-statefulset.yaml 

2.2.1.3 - 使用预测式扩缩容

准备开始

你需要拥有一个安装了 Kapacity 与 Prometheus 的 Kubernetes 集群。

请确保你的 Kubernetes 集群中有可用的 DNS(如 CoreDNS)来解析 Service 域名。如果没有,则需要对 Kapacity 做如下配置调整:

使用如下命令查看 Kapacity gRPC Server 的 ClusterIP 和端口:

kubectl get svc -n kapacity-system kapacity-grpc-service
NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kapacity-grpc-service   ClusterIP   192.168.38.172   <none>        9090/TCP   5m

使用如下命令更新 Kapacity 的配置,其中的 Kapacity gRPC Server 地址相关参数即为上一步查看到的值:

helm upgrade \
  kapacity-manager kapacity/kapacity-manager \
  --namespace kapacity-system \
  --reuse-values \
  --set algorithmJob.defaultMetricsServerAddr=<kapacity-grpc-server-clusterip>:<kapacity-grpc-server-port> 

安装并配置 Ingress NGINX Controller

Kapacity IHPA 的预测式扩缩容使用「基于流量驱动的副本数预测」算法,因此我们需要至少一条流量指标来使用预测式扩缩容。这里我们使用 Ingress NGINX 作为工作负载入口流量的例子。

如果你的 Kubernetes 集群中还没有 Ingress NGINX Controller,请参考官方文档进行安装。

安装完成后,请按照此文档进行配置以确保 Prometheus 能够采集 Ingress NGINX 的指标。

配置 Kapacity 以识别 Ingress NGINX 指标

使用如下命令在 Kapacity 的自定义 Prometheus 指标配置中加入 Ingress NGINX 的指标:

kubectl edit cm -n kapacity-system kapacity-config
apiVersion: v1
data:
  prometheus-metrics-config.yaml: |
    resourceRules:
      ...
    # 在 rules 中加入 Ingress NGINX 的指标
    rules:
    - seriesQuery: '{__name__="nginx_ingress_controller_requests"}'
      metricsQuery: round(sum(irate(<<.Series>>{<<.LabelMatchers>>}[3m])) by (<<.GroupBy>>), 0.001)
      name:
        as: nginx_ingress_controller_requests_rate
      resources:
        template: <<.Resource>>
        # 注意:如果你的 Prometheus 是用 Prometheus Operator 安装的,请加上下面的 overrides 字段
        # overrides:
        #   exported_namespace:
        #     resource: namespace
    externalRules:
      ...    
kind: ConfigMap
...

可以看到,该配置与 Prometheus Adapter 的配置完全兼容,更多背景信息可参考此用户指南

随后,使用如下命令重启 Kapacity Manager 以加载最新配置:

kubectl rollout restart -n kapacity-system deploy/kapacity-manager

运行示例工作负载

  1. 下载 nginx-statefulset.yaml 文件,并执行以下命令以运行一个 NGINX 服务:
kubectl apply -f nginx-statefulset.yaml

验证服务部署完成:

kubectl get po
NAME      READY   STATUS    RESTARTS   AGE
nginx-0   1/1     Running   0          5s
  1. 下载 nginx-ingress.yaml 文件,并执行以下命令以为 NGINX 服务创建一个 Ingress:
kubectl apply -f nginx-ingress.yaml

验证 Ingress 创建成功,并记录下 Ingress 的 ADDRESS:

kubectl get ing
NAME           CLASS   HOSTS               ADDRESS           PORTS   AGE
nginx-server   nginx   nginx.example.com   139.224.120.211   80      2d
  1. 下载 periodic-client.yaml 文件,将其中的 <nginx-ingress-address> 替换为上一步记录的 Ingress 的 ADDRESS,随后执行以下命令创建一个按周期性(以 1 小时为 1 个周期)规律向 NGINX 服务发送请求的客户端 Pod:
kubectl apply -f periodic-client.yaml

它会产生如下图所示的周期性流量:

由于算法学习需要一定数据量,建议至少运行 24 小时后再进行后续步骤。

训练时序预测模型

请参考用户指南使用该配置完成时序预测模型的训练,随后执行以下命令将模型及其附属文件保存为一个 ConfigMap 供后续算法任务使用,其中的 <model-save-path> 请替换为实际的模型保存目录路径:

kubectl create cm -n kapacity-system example-model --from-file=<model-save-path>

创建配置了动态预测式画像源的 IHPA

下载 dynamic-predictive-portrait-sample.yaml 文件,其内容如下所示:

apiVersion: autoscaling.kapacitystack.io/v1alpha1
kind: IntelligentHorizontalPodAutoscaler
metadata:
  name: predictive-sample
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: StatefulSet
    name: nginx
  minReplicas: 1
  maxReplicas: 10
  portraitProviders:
  - type: Dynamic
    priority: 1
    dynamic:
      portraitType: Predictive
      metrics:
      - type: Resource
        resource:
          name: cpu
          target:
            type: AverageValue
            averageValue: 1m
      - type: Pods
        pods:
          metric:
            name: kube_pod_status_ready
          target:
            type: NA
      - name: qps
        type: Object
        object:
          describedObject:
            apiVersion: networking.k8s.io/v1
            kind: Ingress
            name: nginx-server
          metric:
            name: nginx_ingress_controller_requests_rate
          target:
            type: NA
      algorithm:
        type: ExternalJob
        externalJob:
          job:
            type: CronJob
            cronJob:
              template:
                spec:
                  schedule: "0/30 * * * *"
                  jobTemplate:
                    spec:
                      template:
                        spec:
                          containers:
                          - name: algorithm
                            args:
                            - --tsf-model-path=/opt/kapacity/timeseries/forecasting/model
                            - --re-history-len=24H
                            - --re-time-delta-hours=8
                            - --re-test-dataset-size-in-seconds=3600
                            - --scaling-freq=10min
                            volumeMounts:
                            - name: model
                              mountPath: /opt/kapacity/timeseries/forecasting/model
                              readOnly: true
                          volumes:
                          - name: model
                            configMap:
                              name: example-model
                          restartPolicy: OnFailure
          resultSource:
            type: ConfigMap

请将算法参数 --re-time-delta-hours 的值替换成你所在时区的 UTC 偏移值,如 UTC+8 时区则填写 8,UTC-7 时区则填写 -7

下面简单解释该 IHPA 的一些配置:

先来看指标,在「基于流量驱动的副本数预测」算法中,我们需要多类指标来共同驱动该算法,因此我们约定了下面的指标配置规范:

  • 第一个指标应当配置为该工作负载的目标资源指标,因此类型只能为 Resource 或者 ContainerResource。它指定了我们期望 IHPA 帮我们维持的目标资源水位。
  • 第二个指标应当配置为该工作负载的在线副本数指标,算法会使用该指标查询该工作负载的历史 Ready Pod(即承载流量的 Pod)的数量。该指标的类型只能为 Pods,它会在工作负载维度按照 Pod 名称正则匹配做聚合查询,Kapacity 默认配置了基于 kube-state-metrics 的 kube_pod_status_ready 指标可供直接使用。需要注意的是,由于该指标仅用于历史查询,我们不需要为它指定目标值,因此这里我们将其 targettype 写为一个占位符 NA
  • 第三个及以后的指标应当配置为和该工作负载的目标资源指标存在正相关的流量指标(如 QPS、RPC 等),算法会对这些指标进行时序预测,随后基于历史资源水位和副本数,给出未来能够满足目标资源水位的预测副本数。这些指标的类型可以是除了 ResourceContainerResource 的任意类型,但注意必须为这些指标设置与训练时设置的相同的 name。同样地,这些指标也仅用于历史查询,因此不需要设定目标值。

再来看算法参数,这里简单说明其中几个关键参数的作用,更多信息可参考算法脚本自身的 flags 说明:

  • --re-history-len:该参数指定了副本数推荐算法学习的历史长度,一般建议至少覆盖应用的两个行为周期。
  • --re-time-delta-hours:该参数指定了应用所在时区的 UTC 偏移值,副本数推荐算法需要感知时区信息以学习时间特征。
  • --re-test-dataset-size-in-seconds:该参数指定了副本数推荐算法学习的测试集大小,默认为一天(86400),只有历史长度不足一天时才需要将其改短,如本示例中设置为一小时(3600)。
  • --scaling-freq:该参数指定了算法最终输出的副本数预测结果的精度,即最终实际扩缩容的最高频率,因此其不能短于时序预测算法的原始预测精度(训练时序预测模型时使用的 freq 参数)。算法会按照给定的精度对原始预测结果按最大值做重采样后输出,比如如果该参数设置为 1 小时,则算法最终会给出每小时该工作负载所需的最大副本数,最终该工作负载最多每小时进行一次扩缩容。

执行以下命令创建该 IHPA:

kubectl apply -f dynamic-predictive-portrait-sample.yaml

验证结果

  1. 验证 IHPA 自动创建了运行算法任务的 CronJob,且上一次任务运行成功:
kubectl get cj -n kapacity-system
NAME                                   SCHEDULE       SUSPEND   ACTIVE   LAST SCHEDULE   AGE
default-predictive-sample-predictive   0/30 * * * *   False     1        26m             2d1h
kubectl get job -n kapacity-system
NAME                                            COMPLETIONS   DURATION   AGE
default-predictive-sample-predictive-28286564   1/1           16s        28m
  1. 验证算法结果成功写入了 IHPA 的预测式水平画像:
kubectl get hp predictive-sample-predictive -o yaml
apiVersion: autoscaling.kapacitystack.io/v1alpha1
kind: HorizontalPortrait
metadata:
  name: predictive-sample-predictive
  namespace: default
  ...
spec:
  ...
status:
  conditions:
  - lastTransitionTime: "2023-10-25T11:00:00Z"
    message: portrait has been successfully generated
    observedGeneration: 1
    reason: SucceededGeneratePortrait
    status: "True"
    type: PortraitGenerated
  portraitData:
    expireTime: "2023-10-25T11:30:00Z"
    timeSeries:
      timeSeries:
      - replicas: 4
        timestamp: 1698231600
      - replicas: 3
        timestamp: 1698232200
      - replicas: 2
        timestamp: 1698232800
    type: TimeSeries
  1. 验证 IHPA 按算法的预测结果对工作负载的副本数进行调整:
kubectl describe ihpa predictive-sample
...
Events:
  Type     Reason                Age                 From             Message
  ----     ------                ----                ----             -------
  Warning  NoValidPortraitValue  29m (x10 over 85m)  ihpa_controller  no valid portrait value for now
  Normal   UpdateReplicaProfile  25m                 ihpa_controller  update ReplicaProfile with onlineReplcas: 1 -> 4, cutoffReplicas: 0 -> 0, standbyReplicas: 0 -> 0
  Normal   UpdateReplicaProfile  15m                 ihpa_controller  update ReplicaProfile with onlineReplcas: 4 -> 3, cutoffReplicas: 0 -> 0, standbyReplicas: 0 -> 0
  Normal   UpdateReplicaProfile  5m9s                ihpa_controller  update ReplicaProfile with onlineReplcas: 3 -> 2, cutoffReplicas: 0 -> 0, standbyReplicas: 0 -> 0

清理资源

执行以下命令清理所有资源:

kubectl delete -f dynamic-predictive-portrait-sample.yaml
kubectl delete -f periodic-client.yaml
kubectl delete -f nginx-ingress.yaml
kubectl delete -f nginx-statefulset.yaml

2.2.1.4 - 使用多阶段灰度扩缩容

准备开始

你需要拥有一个安装了 Kapacity 的 Kubernetes 集群。

运行示例工作负载

下载 nginx-statefulset.yaml 文件,并执行以下命令以运行一个 NGINX 服务:

kubectl apply -f nginx-statefulset.yaml

验证服务部署完成:

kubectl get po
NAME      READY   STATUS    RESTARTS   AGE
nginx-0   1/1     Running   0          5s

创建配置了多阶段灰度缩容的 IHPA

下载 gray-strategy-sample.yaml 文件,其内容如下所示:

apiVersion: autoscaling.kapacitystack.io/v1alpha1
kind: IntelligentHorizontalPodAutoscaler
metadata:
  name: gray-strategy-sample
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: StatefulSet
    name: nginx
  minReplicas: 1
  maxReplicas: 10
  portraitProviders:
  - priority: 1
    static:
      replicas: 1
    type: Static
  - cron:
      crons:
      - name: cron-1
        replicas: 5
        start: 0 * * * *
        end: 10 * * * *
    priority: 2
    type: Cron
  behavior:
    scaleDown:
      grayStrategy:
        grayState: Cutoff         # GrayState is the desired state of pods that in gray stage.
        changeIntervalSeconds: 30 # ChangeIntervalSeconds is the interval time between each gray change.
        changePercent: 50         # ChangePercent is the percentage of the total change of replica numbers which is used to calculate the amount of pods to change in each gray change.
        observationSeconds: 60    # ObservationSeconds is the additional observation time after the gray change reaching 100%.

该 IHPA 配置了以下两个画像源:

  • 静态画像源:优先级为 1,副本数始终为 1。
  • 定时画像源:优先级为 2,每小时第 0 分钟到第 10 分钟的副本数为 5。

由于定时画像源的优先级高于静态画像源,因此在其生效期间指定的副本数会覆盖静态画像源的副本数。

执行以下命令创建该 IHPA:

kubectl apply -f gray-strategy-sample.yaml

验证结果

在任意小时的第 0~9 分钟,我们可以看到定时画像源生效,工作负载的副本数从 1 扩容到了 5:

kubectl get po -L 'kapacitystack.io/pod-state' -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP          NODE             NOMINATED NODE   READINESS GATES   POD-STATE
nginx-0   1/1     Running   0          50m   10.1.5.52   docker-desktop   <none>           1/1
nginx-1   1/1     Running   0          56s   10.1.5.68   docker-desktop   <none>           1/1
nginx-2   1/1     Running   0          54s   10.1.5.69   docker-desktop   <none>           1/1
nginx-3   1/1     Running   0          52s   10.1.5.70   docker-desktop   <none>           1/1
nginx-4   1/1     Running   0          50s   10.1.5.71   docker-desktop   <none>           1/1

该工作负载对应服务的 Endpoint 数量也变为 5 个:

kubectl get ep nginx
NAME    ENDPOINTS                                            AGE
nginx   10.1.5.52:80,10.1.5.68:80,10.1.5.69:80 + 2 more...   3d3h

在第 10 分钟我们可以看到多阶段灰度缩容开始,其中 2 个 Pod 变为了 Cutoff 状态,并且从服务的 Endpoint 中摘除:

kubectl get po -L 'kapacitystack.io/pod-state' -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP          NODE             NOMINATED NODE   READINESS GATES   POD-STATE
nginx-0   1/1     Running   0          51m   10.1.5.52   docker-desktop   <none>           1/1
nginx-1   1/1     Running   0          63s   10.1.5.68   docker-desktop   <none>           1/1
nginx-2   1/1     Running   0          61s   10.1.5.69   docker-desktop   <none>           1/1
nginx-3   1/1     Running   0          59s   10.1.5.70   docker-desktop   <none>           0/1               Cutoff
nginx-4   1/1     Running   0          57s   10.1.5.71   docker-desktop   <none>           0/1               Cutoff
kubectl get ep nginx
NAME    ENDPOINTS                                AGE
nginx   10.1.5.52:80,10.1.5.68:80,10.1.5.69:80   3d3h

再过 30 秒后,可以看到 4 个 Pod 变为了 Cutoff 状态,并且从服务的 Endpoint 中摘除:

kubectl get po -L 'kapacitystack.io/pod-state' -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP          NODE             NOMINATED NODE   READINESS GATES   POD-STATE
nginx-0   1/1     Running   0          51m   10.1.5.52   docker-desktop   <none>           1/1
nginx-1   1/1     Running   0          96s   10.1.5.68   docker-desktop   <none>           0/1               Cutoff
nginx-2   1/1     Running   0          94s   10.1.5.69   docker-desktop   <none>           0/1               Cutoff
nginx-3   1/1     Running   0          92s   10.1.5.70   docker-desktop   <none>           0/1               Cutoff
nginx-4   1/1     Running   0          90s   10.1.5.71   docker-desktop   <none>           0/1               Cutoff
kubectl get ep nginx
NAME    ENDPOINTS      AGE
nginx   10.1.5.52:80   3d3h

再过 1 分钟后,可以看到工作负载最终被缩容到 1 个 Pod:

kubectl get po -L 'kapacitystack.io/pod-state' -o wide
NAME      READY   STATUS    RESTARTS   AGE    IP          NODE             NOMINATED NODE   READINESS GATES   POD-STATE
nginx-0   1/1     Running   0          52m    10.1.5.52   docker-desktop   <none>           1/1

你也可以通过 IHPA 的事件看到缩容的整个流程:

kubectl describe ihpa gray-strategy-sample
...
Events:
  Type    Reason                Age    From             Message
  ----    ------                ----   ----             -------
  Normal  CreateReplicaProfile  3m53s  ihpa_controller  create ReplicaProfile with onlineReplcas: 1, cutoffReplicas: 0, standbyReplicas: 0
  Normal  UpdateReplicaProfile  2m44s  ihpa_controller  update ReplicaProfile with onlineReplcas: 1 -> 5, cutoffReplicas: 0 -> 0, standbyReplicas: 0 -> 0
  Normal  UpdateReplicaProfile  104s   ihpa_controller  update ReplicaProfile with onlineReplcas: 5 -> 3, cutoffReplicas: 0 -> 2, standbyReplicas: 0 -> 0
  Normal  UpdateReplicaProfile  74s    ihpa_controller  update ReplicaProfile with onlineReplcas: 3 -> 1, cutoffReplicas: 2 -> 4, standbyReplicas: 0 -> 0
  Normal  UpdateReplicaProfile  14s    ihpa_controller  update ReplicaProfile with onlineReplcas: 1 -> 1, cutoffReplicas: 4 -> 0, standbyReplicas: 0 -> 0

清理资源

您可以通过执行以下命令清理样例相关资源

kubectl delete -f gray-strategy-sample.yaml 
kubectl delete -f nginx-statefulset.yaml 

3 - 用户指南

详细了解如何使用 Kapacity 的各项功能

3.1 - IHPA (Intelligent HPA)

详细了解如何使用 IHPA

本节将展开介绍 IHPA 相关的基本概念和一些具体使用细节。

如果你还不知道什么是 IHPA,可以先阅读介绍

如果你希望快速上手使用 IHPA 的核心功能,可以跟随快速开始

3.1.1 - 概念

IHPA 相关的基本概念

3.1.1.1 - IHPA 整体架构

组件架构图

图例:

  • 蓝底的为 IHPA 自身组件
  • 绿底的为 IHPA 自身的 Kubernetes 定制资源(CR)对象
  • 黄底的为 IHPA 依赖的其他 Kubernetes 资源对象
  • 白底的为相关外部系统

部分缩写的全称:

  • IHPA: IntelligentHorizontalPodAutoscaler
  • HPortrait: HorizontalPortrait
  • CM: ConfigMap

组件概述

Replica Controller

IHPA 执行层组件,负责具体工作负载副本数量和状态控制,其通过对接不同的原生和第三方组件支持 Pod 的扩缩容、摘挂流和激保活等操作。

IHPA Controller

IHPA 控制面组件,直接接受用户或外部系统的 IHPA 配置(包括目标工作负载、指标、算法、变更与稳定性配置等),下发画像任务并整合画像结果,再根据画像结果执行多级分批弹性伸缩。

HPortrait Controller

内置水平弹性伸缩算法管理组件,负责运行和管理针对不同工作负载的不同弹性伸缩算法的工作流,并将其输出结果转换为标准画像格式。具体的算法子任务则会作为单独的 Kubernetes Job 或者其他大数据/算法平台的任务被调度执行。这些子任务会从外部监控系统中获取历史与实时指标数据进行计算并生成画像结果。

特别的,部分简单算法(如响应式算法等)的逻辑是直接实现在该组件中,不再走单独的算法子任务。

Metrics Provider Server

统一监控指标查询组件,屏蔽底层监控系统差异,为外部运行的组件(如算法任务等)提供统一监控指标查询服务。

其提供的 API 与 Kubernetes Metrics API 类似,但不同的是它能够同时支持实时和历史指标查询。

Agent(暂未引入)

运行在 Kubernetes 集群节点上的 agent 组件,主要负责执行 Pod 的激保活等需要与底层操作系统进行交互的操作。

3.1.1.2 - 预测式扩缩容原理

预测式扩缩容的优势

通过上图的对比,我们可以得出预测式扩缩容相比于响应式的几个优势:

  • 预测式扩缩容能够提前对流量变化做出反应
  • 预测式扩缩容能够更稳定地控制资源水位
  • 预测式扩缩容具有更高的精度,能够更有效地利用资源

基于流量驱动的副本数预测

下面将详细介绍 IHPA 的「基于流量驱动的副本数预测」算法的设计思路与工作原理。

为什么要使用流量驱动

对于在线应用来说,容量(资源)指标(如 CPU 利用率)与流量是强相关的,即流量变化驱动了容量指标的变化。我们通过预测流量来评估容量,而非直接对容量指标进行预测,会有如下的好处:

  • 流量指标是最上游的指标,先于容量指标发生变化,响应快
  • 容量指标易受多种因素干扰(如应用自身的代码问题、宿主机性能等),而流量指标仅与应用特征直接相关(如用户的使用习惯),更易于进行时序预测

流量、容量与副本数关联建模

为了将副本数预测问题转化为流量预测问题,我们设计了一个 Linear-Residual Model 来找到流量、容量与副本数三者之间的关联函数,如下图所示:

在该模型中,我们将资源利用率设置为目标指标,因为控制应用的资源水位是我们使用弹性伸缩的最终目的,这也是最符合直觉的。

但不同于 Kubernetes HPA 的响应式折比算法,虽然我们将资源利用率设置为目标指标,但该算法不会仅仅考虑资源利用率这一项指标,而是将历史流量(支持多条)、历史资源利用率、历史副本数都作为输入。这些指标会先通过一个线性模型,该模型能够学习这三者之间的线性关联,并得到上图中的关联函数;随后,它们会与其他信息一起(当前仅包含时间信息)通过一个残差模型,该将其他信息纳入考虑后对关联函数进行修正,能够学习到流量、容量和副本数之间的复杂非线性关联。

这里举一个简单的例子来说明残差模型的主要作用:假设某在线应用在每周日凌晨会执行一个内部定时任务,该任务会带来额外 CPU 资源消耗,但它与该应用处理的外部流量没有关联,这时候仅通过线性模型是无法学习到这一特征的。而引入残差模型后,该模型能够基于时间信息学习到该特征,因而在每周日凌晨的时间,我们给定与其他时间相同的流量和副本数,它所给出的函数会输出更高的 CPU 消耗,符合实际情况。

在目前的算法实现当中,我们使用了 ElasticNet 作为线性模型,LightGBM 作为残差模型,它们都是传统机器学习算法,不强依赖 GPU,相比于深度学习算法具有更低的使用开销,并且也能得到较好的效果。当然,你也可以根据自身的使用需求替换这些模型的具体实现,也欢迎提供你认为在某些场景下更优的实现。

在使用该模型得到关联函数之后,我们就能够把副本数预测问题转化为流量预测问题:已知目标资源利用率,只需要输入预测的流量,就能够得到(在预测流量下能够维持目标平均资源利用率的)预测的副本数。

模型细节

我们设工作流信息(包含流量、资源利用率、副本数信息)为 $k$,目标(资源利用率)值为 $y$,元信息(包括时间等)为 $\omega$,我们首先使用线性模型来描绘目标函数的骨架:$$\hat y_l = L(x)$$ 随后我们计算线性模型的误差:$$e = y - \hat y$$ 之后,我们将该误差和元信息通过残差模型进行修正:$$\hat e = R(\hat y,\omega)$$ 最后,我们能够得到 $y$ 的估计值:$$\hat y_r = \hat y_l + \hat e$$ 其中,$L$ 和 $R$ 分别可以是任意的线性和残差模型。

流量时序预测

我们设计了一个名为 Swish Net for Time Series Forecasting 的深度学习模型来对流量指标进行时序预测,该模型专为 IHPA 的使用场景而优化,其具有下面两个主要特点:

  • 轻量:该模型具有较简单的模型结构,这使得其具有较小的模型大小和较低的训练成本。以单条流量预测,12 个历史点预测 12 个未来点(在 10 分钟精度下即为 2 小时长度的预测)为例,其训练得到的模型大小小于 1 MiB;使用 PC 级 CPU 训练的情况下,一个 epoch 也只需要 1 分钟左右,约 1~2 小时就能训练完成。
  • 在生产流量时序预测上的表现较好:我们使用生产流量数据集将该模型与其他常见深度学习时序预测模型进行了对比,可以看到该模型在生产流量时序预测任务上的表现优于其他模型,如下表所示:
MAE RMSE
DeepAR 1.734 31.315
N-BEATS 1.851 41.681
ours 1.597 28.732

模型细节

假设已经知道历史流量 $y_{1:T,i}$,未来真实流量为 $y_{T+1:T+\tau,i}$,预测流量为 $\hat y_{T+1:T+\tau,i}$,流量所属的类别(如 App)为 $i$。流量时间序列会存在周期性,趋势性,自回归特征,我们分别设计以下几个模块对这几种特性进行捕捉,并聚合信息对未来进行预测。

  1. 模型的 Embedding Layer 将类别信息和时间信息进行高维向量投射,类别信息表达不同序列的差异性,时间信息可以表达时间序列的周期性:$$V_i = Embed(i)$$ $$V_t = Embed(t)$$

  2. 模型的时间特征和类别特征跟历史流量交叉特征点积可以进一步抽取不同序列的不同的差异性和周期性特征:$$\tilde V_i = V_i \odot y_{1:T}$$ $$\tilde V_t = V_t \odot y_{1:T}$$

  3. 流量时间序列下一时间步骤与上一时间步骤的差分特征可以剔除趋势性,更好的表达时间序列的周期性,趋势性特征包含在原始序列中:$$\tilde y_{1:T} = y_{2:T} - y_{1:T-1}$$

  4. Multilayer Perception 层模型的输入以及网络结构表达如下:$$in = concate(V_i,V_t,\tilde V_i,\tilde V_t,Embed(i),Embed(t),\tilde y_{1:T},y_{1:T})$$ $$\hat y_{T+1:T+\tau,i} = MLP(in)$$ 多层时间网络对以上特征模块的信息进行汇总,并预测未来时间步的时间序列。

  5. 模型学习的损失函数为 MSE:$$loss = \sum_{i,t}(y_{i,t}-\hat y_{i,t})^2$$

完整算法工作流

最后,我们将上述两个模型与相关数据源完整串接起来,就可以得到 IHPA 预测式扩缩容算法的完整工作流,如下图所示:

图例:

  • 蓝色线为离线链路,需要较大量数据 & GPU & 较长的执行时间,执行频率较低
  • 黄色线为在线链路,需要中等量数据 & CPU & 较短的执行时间,执行频率较高

注:

  • 这里的「应用」是一个抽象概念,代表弹性伸缩的最小单元,通常是一个具体的工作负载如 Deployment 等
  • 这里的「所有流量」并非代表监控系统中的所有可监控流量,而是代表与目标资源用量指标呈显著正相关的组分流量,可以根据具体应用场景按需选取

3.2 - 算法

详细了解 Kapacity 中的通用算法

3.2.1 - 训练时序预测模型

准备开始

本文档将指导您如何训练 Kapacity 所使用的时序预测深度学习算法的模型。

在开始之前,请确保您的环境已经安装了 Conda

安装依赖

执行以下命令以下载你所使用的 Kapacity 算法版本的代码、安装算法依赖并激活算法运行环境:

git clone --depth 1 -b algorithm-<your-kapacity-algorithm-version> https://github.com/traas-stack/kapacity.git
cd kapacity/algorithm
conda env create -f environment.yml
conda activate kapacity

准备配置文件

训练脚本会从一个外部配置文件读取数据集拉取和模型训练的相关参数,因此我们需要提前准备好该配置文件。你可以下载这份示例配置文件 tsf-model-train-config.yaml 并按需修改其内容,该文件的内容如下所示:

targets:
- workloadNamespace: default
  workloadRef:
    apiVersion: apps/v1
    kind: StatefulSet
    name: nginx
  historyLength: 24H
  metrics:
  - name: qps
    type: Object
    object:
      metric:
        name: nginx_ingress_controller_requests_rate
      describedObject:
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        name: nginx-server
freq: 10min
predictionLength: 3
contextLength: 7
hyperParams:
  learningRate: 0.001
  epochs: 100
  batchSize: 32

下面解释其中各字段的含义:

  • targets:待训练的工作负载及其指标信息列表(算法支持训练多个工作负载的多个指标到一个模型,但也会使模型变得更大),如果你希望手动准备数据集而非算法自动拉取,可不填写该字段。
    • workloadNamespace:待训练工作负载所在命名空间。
    • workloadRef:待训练工作负载对象的引用标识。
    • historyLength:拉取作为数据集的指标历史长度,支持 min(分钟)、H(小时)、D(天)三个时间单位。一般建议至少覆盖周期性指标的两个完整周期。
    • metrics:该工作负载待训练的指标列表,格式同 IHPA 的指标字段。注意必须为每个指标设置不同的 name 以在同一个模型中的同一个工作负载下区分不同的指标。
  • freq:模型的精度,即下面 predictionLengthcontextLength 参数的单位。目前支持的值为 1min10min1H1D。注意该参数不会影响模型大小。
  • predictionLength:模型的预测点数,最终预测长度为 predictionLength * freq。该参数设置地越大,模型也会越大。一般不建议设置得过大,因为预测的时间点越久远,预测的准确度也会越低。
  • contextLength:模型在预测(推理)时参考的历史点数,最终参考的历史长度为 contextLength * freq。该参数设置地越大,模型也会越大。
  • hyperParams:深度学习模型的超参数,一般不需要调整。

训练模型

执行以下命令进行模型训练,注意将相关参数替换为真实值:

python kapacity/timeseries/forecasting/train.py \
  --config-file=<your-config-file> \
  --model-save-path=<your-model-save-path> \
  --dataset-file=<your-datset-file> \
  --metrics-server-addr=<your-metrics-server-addr> \
  --dataloader-num-workers=<your-dataloader-num-workers>

下面解释各参数的含义:

  • config-file:上一步准备的配置文件的地址。
  • model-save-path:模型要保存到的目录地址。
  • dataset-file:手动准备的数据集文件的地址。和 metrics-server-addr 参数二选一填写。
  • metrics-server-addr:用于自动拉取数据集的指标服务器的地址,即可访问的 Kapacity gRPC 服务的地址。和 dataset-file 参数二选一填写。
  • dataloader-num-workers:用于加载数据集的子进程数量,一般建议设置为本机 CPU 核数,如果设置为 0 则仅使用主进程加载。

执行后你将看到类似下面内容的训练日志,请等待直到训练完成(命令正常退出):

2023-10-18 20:05:07,757 - INFO: Epoch: 1 cost time: 55.25944399833679
2023-10-18 20:05:07,774 - INFO: Epoch: 1, Steps: 6 | Train Loss: 188888896.6564227
Validation loss decreased (inf --> 188888896.656423).  Saving model ...
2023-10-18 20:05:51,157 - INFO: Epoch: 2 cost time: 43.38192820549011
2023-10-18 20:05:51,158 - INFO: Epoch: 2, Steps: 6 | Train Loss: 212027786.7585510
EarlyStopping counter: 1 out of 15
2023-10-18 20:06:30,055 - INFO: Epoch: 3 cost time: 38.89493203163147
2023-10-18 20:06:30,060 - INFO: Epoch: 3, Steps: 6 | Train Loss: 226666666.7703293

训练完成后你可以在 model-save-path 目录下看到训练好的模型及其附属文件:

-rw-r--r-- 1 admin  staff   316K Oct 18 20:18 checkpoint.pth
-rw-r--r-- 1 admin  staff   287B Oct 18 20:04 estimator_config.yaml
-rw-r--r-- 1 admin  staff    29B Oct 18 20:04 item2id.json

3.3 - 使用自定义指标

背景

Kubernetes Metrics API 提供了一套 K8s 体系内的通用指标查询接口,但它只支持查询指标的当前实时值,而不支持查询历史值,此外,它也不支持基于工作负载维度的 Pod 指标聚合查询,无法满足各类智能算法的数据需求。因此,Kapacity 对 Metrics API 进行了进一步的抽象和扩展,在最大程度兼容用户使用习惯的同时支持了通用的指标历史查询与工作负载维度聚合查询等高阶查询能力。

目前,Kapacity 提供的 Metrics API 支持下面两种指标查询后端:

使用 Prometheus 作为指标查询后端(默认)

将 Kapacity Manager 的启动参数 --metric-provider 设置为 prometheus 以使用 Prometheus 作为指标查询后端。

如果使用此后端,你只需要有一个 Prometheus,无需安装 Kubernetes Metrics Server 或其他 Metrics Adapter(包括 Prometheus Adapter)。

你可以在 Kapacity Manager 所在命名空间的 ConfigMap kapacity-config 中找到 prometheus-metrics-config.yaml 这份配置,通过修改这份配置,你可以完全自定义不同指标类型的 Prometheus 查询语句。这份配置的格式完全兼容 Prometheus Adapter 的配置,因此,如果你此前使用 Prometheus Adapter 配置了自定义 HPA 指标,可以直接复用以前的配置。

当前 Kapacity 提供的默认配置如下所示:

resourceRules:
  cpu:
    containerQuery: |-
      sum by (<<.GroupBy>>) (
        irate(container_cpu_usage_seconds_total{container!="",container!="POD",<<.LabelMatchers>>}[3m])
      )      
    readyPodsOnlyContainerQuery: |-
      sum by (<<.GroupBy>>) (
          (kube_pod_status_ready{condition="true"} == 1)
        * on (namespace, pod) group_left ()
          sum by (namespace, pod) (
            irate(container_cpu_usage_seconds_total{container!="",container!="POD",<<.LabelMatchers>>}[3m])
          )
      )      
    resources:
      overrides:
        namespace:
          resource: namespace
        pod:
          resource: pod
    containerLabel: container
  memory:
    containerQuery: |-
      sum by (<<.GroupBy>>) (
        container_memory_working_set_bytes{container!="",container!="POD",<<.LabelMatchers>>}
      )      
    readyPodsOnlyContainerQuery: |-
      sum by (<<.GroupBy>>) (
          (kube_pod_status_ready{condition="true"} == 1)
        * on (namespace, pod) group_left ()
          sum by (namespace, pod) (
            container_memory_working_set_bytes{container!="",container!="POD",<<.LabelMatchers>>}
          )
      )      
    resources:
      overrides:
        namespace:
          resource: namespace
        pod:
          resource: pod
    containerLabel: container
  window: 3m
rules: []
externalRules:
- seriesQuery: '{__name__="kube_pod_status_ready"}'
  metricsQuery: sum(<<.Series>>{condition="true",<<.LabelMatchers>>})
  name:
    as: ready_pods_count
  resources:
    overrides:
      namespace:
        resource: namespace
workloadPodNamePatterns:
- group: apps
  kind: ReplicaSet
  pattern: ^%s-[a-z0-9]+$
- group: apps
  kind: Deployment
  pattern: ^%s-[a-z0-9]+-[a-z0-9]+$
- group: apps
  kind: StatefulSet
  pattern: ^%s-[0-9]+$

为了支持工作负载维度聚合查询等高阶查询能力,我们在 Prometheus Adapter 配置之上扩展了部分字段,下面对这些扩展字段作简要说明:

  • workloadPodNamePatterns:Kapacity 的部分算法会需要查询工作负载维度的指标信息,如某工作负载 Pods 的 CPU 总用量、某工作负载的 Ready Pods 数量等,此时 Kapacity 会以工作负载 Pod 名称正则匹配的方式对 Pod 维度的指标做聚合查询,因此需要通过该字段配置不同类型工作负载的 Pod 名称正则匹配规则。如果你使用了默认配置以外的其他工作负载,需要在该字段中添加相应配置。
  • readyPodsOnlyContainerQuery:Kapacity 的部分算法在查询工作负载 Pods 的资源总用量时会有额外的条件,如仅查询某工作负载 Ready Pods 的 CPU 总用量,此时我们需要使用该字段提供一条单独的 PQL 语句来做此特殊条件下的查询。Kapacity 默认提供了基于 kube-state-metrics 所提供指标的查询语句,你也可以按需修改为其他实现。

使用 Kubernetes Metrics API 作为指标查询后端(不推荐)

将 Kapacity Manager 的启动参数 --metric-provider 设置为 metrics-api 以使用 Kubernetes Metrics API 作为指标查询后端。

如果使用此后端,你需要安装 Kubernetes Metrics Server 或其他 Metrics Adapter(如 Prometheus Adapter),由它们来屏蔽下层监控系统差异。

但需要注意的是,此后端不支持指标历史值查询,也不支持基于工作负载维度的 Pod 指标聚合查询,因此其可用范围非常有限,只适用于部分仅使用简单算法的场景,如响应式扩缩容

3.4 - 常见问题

IHPA

预测式算法任务执行报错 replicas estimation failed

该报错是由于「流量、容量与副本数关联建模」算法没有产出可用的模型导致,可以尝试下面的方法解决此问题:

  • 通过调整算法任务参数 --re-history-len 增大历史数据长度。
  • 结合报错返回的详细模型评估信息,通过调整算法任务参数 --re-min-correlation-allowed--re-max-mse-allowed 适当放宽模型的验证要求。但需要注意,如果放宽的值和默认值差距过大,模型的准确性将很难得到保证。

4 - 贡献指南

了解如何贡献 Kapacity 项目

欢迎参与 Kapacity!

非常感谢您帮助 Kapacity 变得更好。

贡献代码其实并不是唯一做出您的贡献的方式,我们也非常欢迎许多其他形式的贡献,例如:

以下是一些具体的贡献指南。

提交 Issue

在提交 Issue 之前,请检查是否已经存在类似的 Issue,避免重复。

如果要报告安全漏洞,请务必在 New issue 中选择 Report a security vulnerability 选项。不要将其作为普通的公开 Issue 提交。

贡献代码

代码相关贡献,请阅读开发者指南

4.1 - 开发指南

了解如何开发 Kapacity

5 - 路线图

未来版本的路线图

2023

  • 支持基于机器学习的智能副本数预测算法,具备提前预测扩缩容的能力。
  • 支持流量突增识别算法,支持流量突增场景的识别和应急能力,可替代传统响应式算法作为应用容量兜底保护。
  • 支持自定义副本画像准入验证,进一步控制算法产出画像的应用规则,降低预期外风险。
  • 支持稳定性检查与变更熔断能力,支持自定义指标异常检测以及检测异常后的自动暂停和回滚能力。

未来

  • 完整支持 IHPA 扩展框架,用户可以无侵入地自主扩展 IHPA 的各项行为和功能。
  • 上线 Kapacity Agent,支持 Pod Standby 状态切换、Pod 健康度打分等,进一步增强多级弹性能力,同时作为混部的基础能力。
  • 上线 Kapacity Scheduler,支持按照 Pod 资源使用情况与节点资源剩余情况等进行动态调度以减少资源碎片问题,同时支持更多高级调度策略。
  • 支持 Pod Online 和 Standby 状态按需批量切换,以支持分时调度。
  • 支持通过智能算法进行 Pod 资源规格(CPU、内存等)推荐,并支持通过 VPA 对 Pod 资源规格进行动态调整。
  • 支持可视化控制台,并支持多维度成本与碳排放计算。