写点什么

在边缘应用中,实现 Kubernetes 的主动扩缩容

作者:Rajeev Kallayil Ravi
  • 2026-03-25
    北京
  • 本文字数:6322 字

    阅读完需:约 21 分钟

过去十年间,Kubernetes 已经演变为现代 IT 基础设施的核心平台之一。它通过可扩展的架构与声明式的资源定义模型,让企业能够管理大规模、高度分布式的容器化负载,并能够自动化各类运维任务。

因此,它提供了一种高度可扩展的分布式工作负载管理方法。许多组织都在云环境中运行 Kubernetes 部署,这样能够提供无限的处理能力,但是在向边缘计算环境转变的过程中,Kubernetes 用户面临着新的运维需求。

边缘计算需要在靠近数据生成位置的设备或服务器上运行应用,而非集中式地部署在云端。运行在边缘的应用必须满足极低延迟的要求,具备高度的弹性,并在面对大量且不可预测的工作负载峰值时表现稳定。

由于边缘应用的处理能力、内存和网络带宽都是有限的,高效使用这些资源并快速扩缩容边缘应用,对于维持终端用户的体验质量与服务可靠性至关重要。

Kubernetes 提供了Horizontal Pod Autoscaler(HPA)的功能,它可以基于当前的资源使用率(包括 CPU、内存和自定义指标)动态调整部署中的 Pod 数量。

HPA 在应对云环境中观测到的流量模式时非常有效;但在管理动态、突发式的边缘工作负载时效果明显要差一些,在这类场景中,KEDA或自定义扩缩器等替代方案会更合适。

图 1:HPA 的工作原理

HPA 具有僵化性,依赖滞后的指标,而且缺乏上下文感知,这通常会导致 Pod 数量扩容得过多、过少或反复震荡。在资源受限的环境中,这些行为可能代价高昂,甚至存在风险。

我们基于Custom Pod Autoscaler(CPA)为边缘计算构建了一款自定义的扩缩器,以部分解决 HPA 的局限性。CPA 使工程师能够开发自定义算法、使用多种指标组合、快速响应系统状态变化,并根据集群中运行的应用特性调整扩缩容方式。

Kubernetes HPA 在边缘场景下的局限性

HPA 的功能是使用一种公式化的比例方法计算应用期望的副本数,公式如下:

desiredReplicas = currentReplicas * currentMetricValue / desiredMetricValue
复制代码

该公式被硬编码在 Kubernetes 系统中。因此,工程师无法重写公式、调整扩缩容的激进程度,也无法添加任何特定领域的逻辑,除非为环境重新构建一整套的扩缩器。

虽然这适用于云原生应用,但对延迟敏感、流量多变的边缘应用而言可能就会存在问题。

算法的灵活性不足

物联网(Internet of things,IoT)网关和游戏边缘服务器的工作负载通常与资源不是简单的等比关系。换句话说,IoT 网关可能在短时间内使用率暴增十倍(例如,收到大量传感器事件),而游戏边缘服务器需要在用户加入游戏前就扩容,而不能等到 CPU 升高后。

HPA 无法编码时间感知或预测逻辑。它不支持渐进缩容,无法限制扩容的速率,也无法在不大量改造应用的情况下基于多种指标做出决策。

在边缘环境中,HPA 处理短期峰值的低效性会导致如下的问题:

  • 设备注册风暴(Registration bursts from devices)

  • 用户连接洪峰(Floods of user connections)

  • 媒体转码激增(Spikes in media transcoding)

  • 网关波动引发的 API 流量激增(API surges resulting from gateway churn)

HPA 将所有突发流量视为持续负载,导致系统扩容过快,远超处理短期峰值所需规模。

图 2:表示突发流量的短期峰值

这种快速变化会造成 Pod 浪费,消耗小型节点上宝贵的 CPU 和内存资源,最终加剧节点压力,甚至引发驱逐风暴(eviction storm)

自定义指标带来的运维开销

Kubernetes 在autoscaling/v2中增加了对自定义指标的支持,但需要:

  • 指标服务器

  • 自定义指标 API

  • Prometheus

  • 导出器(Exporter)

  • 适配器(Adapter)

这套架构虽然成熟且得到了广泛应用,但需要引入额外的资源开销与运维复杂度,在部分资源受限环境中极具挑战性。

图 3:使用自定义指标与指标服务器的 HPA 扩缩容

上图展示了支持外部指标的 autoscaling/v2 版 HPA。在示例中,集群配置了以下依赖:

  • 部署自定义指标适配器(Istio)以查询指标

  • 生成的适配器将自身注册为“custom.metrics.k8s.io”

  • 在集群中部署 Prometheus 社区版

  • 生成的 Prometheus Pod 会抓取 Pod 端点以获取执行所需的指标

  • 第一步部署的 Istio 适配器配置为查询集群内运行的 Prometheus 实例

  • 查询逻辑写入 HPA 配置中,下方代码片段展示了查询每个 Pod 请求数的逻辑

通过施加负载,我们可借助 Grafana 控评估抓取到的请求数量,并观察扩缩容行为。

边缘架构概述

边缘计算环境的通用结构通常需要围绕多个物理上靠近终端用户的自治边缘工作节点来构建,并在远端云或数据中心中部署中央控制器。

每个边缘节点会独立运行,处理大量的本地流量,并执行管理流量所需的许多功能,因此需要一套可快速、高效、基于当前状态扩缩容的逻辑系统。由于边缘到云端的回传带宽通常非常有限,往返通信成本很高。

可运行在边缘计算平台的示例应用包括:

  • 游戏引擎平台

  • 实时视频处理

  • AR/VR 低延迟计算

  • IoT 网关数据聚合

  • 机器学习推理

  • 本地内容缓存/代理服务

这些应用的扩缩容模式均与 HPA 传统基于 CPU 的算法所优化的模式有所差异。

设计自定义的 Pod 自动扩缩器

为克服 HPA 基于少数僵化行为进行扩缩容的不足,人们设计出了 CPA,以满足以下需求:

  • 允许系统监控任意指标(比如,CPU、延迟、队列长度、自定义 KPI)

  • 将指标监控与扩缩容算法解耦

  • 支持基于预测或补偿的主动扩缩容

  • 通过安全缩容策略防止震荡

  • 保留足够的 CPU 余量以应对边缘工作负载的突发波动

  • 在保持稳定的前提下比 HPA 响应更快

图 4:CPA 的架构

CPA 的开发解除了 Kubernetes 原生扩缩器的限制,允许开发者定义自定义扩缩容的逻辑,同时可以使用可扩展的 Kubernetes 原生控制器。

CPA 评估算法

扩缩容评估会从指标采集模块获取标准化的指标,并据此做出决策。在早期原型中,评估组件基于固定的 CPU 阈值扩容副本,每达到一个阈值就按固定值增加。这种方式虽然易于实现,但无法反映真实环境中扩缩容系统的行为,也无法满足边缘工作负载的运维需求。

与之不同的是,最新的实现包含一套基于云服务商、游戏网络后端和运行大规模低延迟平台的 SRE 团队最佳实践的扩缩容模型。新模式使用三种核心工作负载状态信号替代了僵化的数值阈值,即 CPU 余量(CPU headroom)、延迟 SLO 感知(latency SLO awareness)、Pod 启动补偿(pod startup compensation)。

CPU 余量

当存在足够 CPU 余量时,可以吸收边缘工作负载峰值所需的 CPU,避免排队或延迟损失。扩缩器会设定目标利用率安全区,通常会在 70%–80%,以维持余量缓冲。如果所有 Pod 的平均 CPU 使用率持续高于余量阈值,扩缩器会计算需要增加多少副本才能恢复缓冲空间。

延迟 SLO 感知

如果应用能够导出延迟数据(例如,95 分位的响应时间 p95),CPA 会使用该数据做出扩缩容决策。 当 p95 延迟接近或超过延迟服务目标(比如,交互式边缘工作负载 60ms)时,扩缩器会按延迟 SLO 超标程度成比例增加副本数。由此,扩缩器会避免将 CPU 作为唯一性能指标,这在 IO 密集型和混合工作负载中尤为重要。

Pod 启动补偿

与中心化云环境中较快的容器启动速度不同,边缘节点因磁盘吞吐量较低或镜像冷启动,容器启动时间通常会更长。为解决长启动时间的问题,扩缩器会根据估算得到 Pod 的启动时间(基于本地观察得出该数据),并对即将到来的负载进行预判扩容。 如果 CPU 使用上升速度过快,那么很可能在 Pod 启动完成前就耗尽了可用容量,扩缩器将触发主动扩容。

这三种输入信号共同提供了复合的扩缩容建议,使 CPA 比 Kubernetes 默认的固定 HPA 算法更具上下文感知能力:

  • 所有信号正常,则不进行扩缩容

  • 某一个信号超出了阈值,则适度扩容

  • 两个及以上信号超标,则 CPA 按照信号的严重程度大幅扩容

缩容设计为缓慢执行,并需要一段稳定窗口,以避免 HPA 常见的震荡行为。

由此,CPA 从简单的“阈值监控”响应式扩缩器,转变为了可有效管理真实边缘工作负载的上下文感知扩缩容引擎。

实现与负载生成

CPA 使用了开源的Custom Pod Autoscaler Framework,这是一个 Kubernetes 原生控制器,支持使用 Python 开发自定义 Pod 扩缩容逻辑。

Custom Pod Autoscaler 框架负责与 Kubernetes 通信。开发者只需要提供两个 Python 脚本:

  • metric.py — 采集/获取指标

  • evaluate.py — 计算期望的副本数

按用户定义的间隔(默认 15 秒),Kubernetes 会调用 CPA 控制器。控制器运行指标脚本,将 JSON 输出管道传递给评估脚本,然后根据评估结果扩缩容 Pod。

CPA 配置文件

每个自定义扩缩容器都通过 config.yaml 进行配置,定义指标源、评估逻辑、目标工作负载、扩缩容限制和执行间隔:

name: cpautilizationnamespace: defaultinterval: 10000metricSource:  type: python  pythonScript: /cpa/metric.pyevaluation:  type: python  pythonScript: /cpa/evaluate.pytarget:  kind: Deployment  name: testcpalimits:  minReplicas: 1  maxReplicas: 20
复制代码

指标采集脚本

指标脚本负责获取 CPU 使用率、延迟或自定义信号。本实现中,由 Prometheus 采集 CPU 和 p95 延迟。

from cpa import metricsimport jsondef main():    cpu = metrics.get_average_cpu_utilization("testcpa")    replicas = metrics.get_current_replicas("testcpa")    latency = metrics.get_custom_metric("service_latency_p95_ms")    output = {        "resource": "testcpa",        "runType": "api",        "metrics": [{            "resource": "testcpa",            "value": json.dumps({                "current_replicas": replicas,                "avgcpu_utilization": cpu,                "p95_latency_ms": latency            })        }]    }    print(json.dumps(output))if __name__ == "__main__":    main()
复制代码

评估脚本

以下评估算法实现了一套基于 CPU、延迟 SLO、Pod 启动时间以及安全扩缩容约束的扩缩容逻辑。

# PARAMETERSCPU_HEADROOM_TARGET = 0.75        # Keep CPU at ~75% average usageLATENCY_SLO_MS = 60               # Example latency SLO for interactive workloadsSCALE_UP_FACTOR = 1.3             # Increase replicas by 30% when overloadedMAX_SCALE_UP_STEP = 4             # Never add more than 4 pods at onceSCALE_DOWN_FACTOR = 0.8           # Reduce replicas slowlyMIN_STABLE_SECONDS = 30           # 30 seconds of stable metrics to scale downPOD_STARTUP_SECONDS = 10          # Expected cold-start timelast_scale_time = 0SCALE_COOLDOWN = 15def main():    spec = json.loads(sys.stdin.read())    evaluate(spec)def evaluate(spec):    global last_scale_time    if len(spec["metrics"]) != 1:        sys.stderr.write("Expected 1 metric")        exit(1)    eval_metric = json.loads(spec["metrics"][0]["value"])    current_replicas = eval_metric.get("current_replicas", 1)    avg_cpu = eval_metric.get("avgcpu_utilization", 0)    p95_latency = eval_metric.get("p95_latency_ms", None)   # Optional metric    now = time.time()    # Cooldown protection (avoid thrashing)    if now - last_scale_time < SCALE_COOLDOWN:        output(current_replicas)        return    # Always start with current replicas    target_replicas = current_replicas    # CPU HEADROOM LOGIC    # Convert utilization to ratio    cpu_ratio = avg_cpu / 100.0    if cpu_ratio > CPU_HEADROOM_TARGET:        # Example: if CPU = 120%, scale by 120/75 = 1.6 ⇒ +60% replicas        scale_multiplier = min(cpu_ratio / CPU_HEADROOM_TARGET, SCALE_UP_FACTOR)        proposed = math.ceil(current_replicas * scale_multiplier)        # Cap the step size        step = min(proposed - current_replicas, MAX_SCALE_UP_STEP)        target_replicas = current_replicas + max(step, 1)    # LATENCY SLO LOGIC    if p95_latency and p95_latency > LATENCY_SLO_MS:        # Scale proportionally to the SLO violation        violation_ratio = p95_latency / LATENCY_SLO_MS        proposed = math.ceil(current_replicas * violation_ratio)        step = min(proposed - current_replicas, MAX_SCALE_UP_STEP)        target_replicas = max(target_replicas, current_replicas + step)    # POD STARTUP COMPENSATION    # scale ahead of predicted load.    if avg_cpu > 90 and POD_STARTUP_SECONDS > 0:        predicted_load = current_replicas * (avg_cpu / 50)        predicted_replicas = math.ceil(predicted_load)        step = min(predicted_replicas - current_replicas, MAX_SCALE_UP_STEP)        target_replicas = max(target_replicas, current_replicas + max(step, 1))    # SCALE DOWN SAFELY    # scale down if metrics are below thresholds.    if cpu_ratio < 0.40 and (not p95_latency or p95_latency < LATENCY_SLO_MS * 0.7):        proposed = math.floor(current_replicas * SCALE_DOWN_FACTOR)        target_replicas = max(1, proposed)    if target_replicas != current_replicas:        last_scale_time = now    output(target_replicas)
复制代码

验证与评估

增强的扩缩容逻辑在所有测试场景中均表现更优。基于 CPU 阈值余量的扩缩容逻辑比早期原型的固定阈值逻辑更成熟、更稳定。

持续压力

基于 CPU 余量逻辑的持续负载使 CPA 能够平滑扩缩容,同时保持了可预测的资源利用率,避免不必要的副本膨胀。

短期峰值

CPA 中的 Pod 启动补偿与保守缩容规则避免了 HPA 对短期性能增长的典型过度反应。

渐进增长的负载

延迟感知的扩缩容使扩缩器在达到 CPU 硬限制前就能检测到性能下降,响应更快、更精准。

通过调整余量等级、动态设置延迟 SLO 阈值与冷却间隔,我们可以生成随机负载模式,模拟不规则的真实流量行为。

图 5:生成的随机负载

图 6:部署的 Pod 数量

与 HPA 相比,CPA 表现出以下特点:

  • 震荡幅度更低

  • 启动副本更少

  • 更快恢复稳态

  • 平均延迟更稳定

  • CPU 浪费减少

图 7:HPA vs. CPA

经验总结

新的扩缩容逻辑带来以下优势,它们都是与边缘应用高度相关的:

  • 单一指标无法适配所有场景。CPU 只是决定性能、延迟和 Pod 启动时间的众多指标之一。

  • 预测式扩缩容有助于减少不稳定行为。使用 Pod 启动补偿能够预判集群未来请求可能会饱和,这有助于减少突发饱和事件。

  • 支持延迟缩容。突然缩容会造成系统震荡,导致用户体验下降,缓慢缩容会提供用户习惯的平滑行为。

  • 在计算与内存资源有限的边缘环境中,HPA 的激进副本扩缩容可能带来意外的副作用,比如,内存压力、Pod 驱逐和限流。

  • 更高的灵活性。CPA 架构将指标采集与使用指标的逻辑分离。随着应用产生更高级的遥测数据,这种分离使扩缩容逻辑可独立于采集指标演进。

结论

通过使用基于 CPU 余量目标、延迟感知评估和 Pod 启动时间补偿的策略替代固定阈值扩缩容,Custom Pod Autoscaler(CPA)成为了一种灵活且可扩展的解决方案。CPA 为工程师提供了创建满足应用性能需求的扩缩容策略的能力,同时解决了 Kubernetes HPA 在边缘计算中的可扩展性局限。尽管基于 CPA 的策略正确实施后能带来诸多收益,但它也需要大量调优、严谨的运维文化和高质量的指标。

因此,虽然 CPA 可以与 Kubernetes 原生扩缩容能力配合使用,但基于 CPA 的策略最适合那些扩缩容会直接影响性能与用户体验的应用。

最近,Kubernetes事件驱动扩缩容(KEDA)与 HPA 的结合发展,极大丰富了基于事件与外部信号对各类工作负载进行扩缩容的选择。

CPA 是专门为存在启动延迟、资源限制和多性能指标共同影响扩缩容决策的场景而设计的,这使其尤其适合边缘环境。通过合理调优与监控,CPA 为在边缘环境实现可预测、高效率、对性能敏感的扩缩容提供了一套可落地、可扩展的方案。

原文链接:

 Proactive Autoscaling for Edge Applications in Kubernetes