Skip to main content

Command Palette

Search for a command to run...

用 Keda 根据工作负载进行快速扩容

Updated
2 min read

太长不看版:用单一指标指导单一工作负载进行扩缩容实在是太低效了。

历史问题

众所周知,Kubernetes 有个亲生的 HPA 组件,在云原生早期,这个名义上的自动扩缩容的能力给 Kubernetes 赢得了不少掌声。当然现在回头看看,仅仅根据 CPU 和内存这样“贫瘠”的指标,不论是用于判断负载水平,还是用于计算扩容目标,都不是很够用的。这个阶段里,HPA 的扩缩容效率也是广受诟病的一个问题,在一个多级微服务调用的业务场景里,压力是逐级传递的,下图展示了一个常见情况:

如上图,用户流量进入集群之后:

  1. 首先在 Deploy A 造成负载,指标变化迫使 Deploy A 扩容

  2. A 扩容之后,吞吐量变大,B 受到压力,再次采集到指标变化,扩容 Deploy B

  3. B 吞吐变大,C ..

这个逐级传递的过程不仅缓慢,而且可以说是步步惊心——每一级的扩容都是直接被 CPU 或内存的飙高触发的,被“冲垮”的可能性是普遍存在的。这种被动、滞后的方式,很明显是有问题的。

推陈出新

造成 HPA 窘境的原因之一,就是“自扫门前雪”,每个 Pod 都只能根据自身负载情况来进行扩缩容决策。如果能够直接根据业务流量的变化进行决策,并且将流量流经的所有微服务进行扩缩容,看起来情况就会好很多了。 HPA 的自定义指标支持,给这个问题了一个可行的方案。该能力让 HPA 可以用其它的指标来作为扩缩容的触发器,例如我们可以用 Promethues 采集消息中间件的深度或者负载均衡器的队列长度,作为一个更能如实反映业务流量的指标,直接用来触发相关的多个微服务的扩缩容,如下图所示:

在上图中:

  1. Prometheus 采集消息队列和负载均衡等更能反映业务流量的指标

  2. 使用 Prometheus Adapter 将 Promethues Metrics 转换为 Kubernetes 的 Aggregated API

  3. HPA 使用自定义指标,同时对多个应用进行扩缩容。

这中间涉及到的 Prometheus Adapter,通过配置文件完成步骤 2 的转换:

- seriesQuery: '{__name__=~"^container_.*_total",container!="POD",namespace!="",pod!=""}'
  resources:
    overrides:
      namespace: {resource: "namespace"}
      pod: {resource: "pod"}
  seriesFilters:
  # since this is a superset of the query above, we introduce an additional filter here
  - isNot: "^container_.*_seconds_total$"
  name: {matches: "^container_(.*)_total$"}
  metricsQuery: "sum(rate(<<.Series>>{<<.LabelMatchers>>,container!="POD"}[2m])) by (<<.GroupBy>>)"

当然,完全可以自行实现 Aggregated API 来支持这种指标的采集和呈现工作。Prometheus 所提供的大量 Exporter 是吸引我们写这种古怪语法的最大动力。

那么如果是 KEDA 的话,这个问题又如何呢?KEDA 提供了几十个被称为 Scaler 的东西,其中除了 Promethues 之外,还包括 Kafka、Redis、PostgreSQL 等多种选择。所以在很多场景中,无需 Promethues,也能使用 Scaler 完成对输入指标的读取和判断。下面用 KEDA 为例,看看这种伸缩方法的具体实现。

KEDA

假设一个容器化应用由多个工作负载组成:

  • Ingress:负责接收业务流量

  • Backend 1、Backend 2:负责处理 Ingress 发来的任务

  • Database:数据库

我们希望达成的效果是 —— Ingress、Backend 1、Backend 2、Database,实例数量保持在 1:2:1.5:2 的关系,Keda 的大致流程如下图所示:

首先使用 Helm 安装 KEDA:

$ helm repo add kedacore https://kedacore.github.io/charts
$ helm install keda kedacore/keda --namespace default
NAME: keda
LAST DEPLOYED: Wed Nov 29 18:56:36 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
...

随便创建几个工作负载,冒充微服务:

$ kubectl create deploy ingress --image=nginx
deployment.apps/ingress created
$ kubectl create deploy backend1 --image=nginx
deployment.apps/backend1 created
$ kubectl create deploy backend2 --image=nginx
deployment.apps/backend2 created
$ kubectl create deploy database --image=nginx
deployment.apps/database created
$ kubectl get pods | cut -d - -f 1 | grep -v keda | sort
...
backend1
backend2
database
ingress

运行成功后,我们可以看到,四个微服务,每个微服务都有一个实例。

按照刚才瞎掰的比例,编写一个 ScaleObject

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: bk1
spec:
  scaleTargetRef:
    name: backend1
  triggers:
  - type: kubernetes-workload
    metadata: 
      podSelector: 'app=ingress'
      value: '0.5'
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: bk2
spec:
  scaleTargetRef:
    name: backend2
  triggers:
  - type: kubernetes-workload
    metadata: 
      podSelector: 'app=ingress'
      value: '0.67'      
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: db
spec:
  scaleTargetRef:
    name: database
  triggers:
  - type: kubernetes-workload
    metadata: 
      podSelector: 'app=ingress'
      value: '0.5'

上述代码引入了 kubernetes-workload 类型的触发器,他会监控 app=ingress 的容器,并对 scaleTargetRef 中提到的工作负载数量比例进行扩缩容。

提交到集群之后,会看到实例数量数量发生了变化:

$ kubectl get pods | cut -d - -f 1 | sort | uniq --count
...
   2 backend1
   2 backend2
   2 database
   1 ingress
   3 keda

我们把 Ingress 扩容到 2 实例,再次统计:

$ kubectl scale deployment ingress --replicas=2
deployment.apps/ingress scaled
$ kubectl get pods | cut -d - -f 1 | sort | uniq --count
...
   4 backend1
   3 backend2
   4 database
   2 ingress
   3 keda

可以看到,的确是按照我们设定的比例,同步产生了缩放。如果缩减 Ingress 服务实例数,几分钟之后,其它工作负载也会随之缩容。

$ kubectl scale deployment ingress --replicas=1
deployment.apps/ingress scaled
$ kubectl get pods | cut -d - -f 1 | sort | uniq --count                                                         \
...
   2 backend1
   2 backend2
   2 database
   1 ingress

结论

虽说云原生架构的复杂性问题越来越被强调,但是这一生态的宗旨应该还是没有变化——用简单的透明的手段解决复杂问题。

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts