关键要点

  • Kubernetes的水平Pod自动扩展器(HPA)的反应延迟可能会影响边缘计算环境的性能,而使用自定义自动扩展器则可以根据特定领域的指标以及多种信号评估结果,实现更稳定的扩展或缩减行为。
  • 在自动扩展逻辑中应考虑Pod的启动时间,因为仅在CPU使用率突然升高时才采取行动会延迟扩展进程,进而降低性能。
  • 为防止副本数量出现波动,尤其是当使用高频指标进行监控时,必须制定安全的缩减策略并设置冷却期。
  • 工程师在自动扩展边缘计算工作负载时,应保留一定的CPU剩余资源,以便能够从容应对不可预测的突发负载高峰,而不会影响系统性能。
  • 延迟服务水平目标(如p95或p99值)是判断系统是否过载的重要指标,因此在制定自动扩展策略时应将这些指标与CPU使用情况一同考虑。

在过去的十年里,Kubernetes已经发展成为现代IT基础设施的重要组成部分之一。它通过提供可扩展的架构,并允许人们使用声明性模型来定义资源,从而帮助各类组织管理大规模、高度分布式的容器化工作负载。

因此,Kubernetes为管理分布式工作负载提供了一种非常高效的方法。然而,尽管许多组织都在拥有无限处理能力的云环境中运行Kubernetes集群,但向边缘计算领域的过渡为Kubernetes用户带来了新的运营需求。

边缘计算是指在数据生成地点附近的设备或服务器上运行应用程序,而不是在中央化的云环境中进行处理。在边缘环境中运行的应用程序必须具备极低的延迟特性、高度的弹性,并且能够在工作负载量突然增加时依然保持稳定的性能。

由于边缘应用的处理能力、内存资源和网络带宽都较为有限,因此高效利用这些资源并快速扩展边缘应用就显得至关重要,这样才能同时保证最终用户的体验质量以及服务的可靠性。

Kubernetes配备了水平Pod自动扩展器功能,该功能可以根据当前的资源使用情况(包括CPU利用率、内存占用量以及自定义指标)动态调整部署中的Pod数量。

HPA在应对云环境中的常规流量变化时表现良好,但在处理边缘计算环境中那种突发性、波动性的负载需求时效果较差。在这种情况下,使用KEDA这类解决方案或自定义自动扩展器可能会更加合适。

图1:水平式Pod自动扩展器的工作原理

HPA的刚性、其对滞后指标的依赖性,以及其缺乏上下文感知能力,通常会导致HPA所管理的Pod数量出现过度扩展、扩展不足或反复波动的情况。在资源有限的环境中,这些行为可能会带来严重的后果,甚至危及系统的稳定性。

为了克服HPA的一些局限性,我使用自定义Pod自动扩展器(CPA)为边缘计算环境开发了一个自动扩展系统。CPA允许工程师自行设计算法,结合多种指标来快速响应系统状态的变化,并根据集群上运行的应用程序的特性调整扩展策略。

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

HPA的功能是通过以下简单的比例计算方法来确定应用程序所需的副本数量:

desiredReplicas = currentReplicas * currentMetricValue / desiredMetricValue

这个公式是硬编码在Kubernetes系统中的。因此,工程师既无法修改这个公式,也无法调整扩展的力度,更无法添加任何特定于业务领域的逻辑——除非为他们的环境重新开发一个自动扩展系统。

虽然这种机制适用于基于云的应用程序,但对于那些对延迟敏感且流量变化较大的边缘应用来说,却存在明显的问题。

算法灵活性的缺失

物联网网关和游戏边缘服务器的工作负载通常与其拥有的资源并不成正比。例如,一个物联网网关在短时间内可能会因为接收到大量传感器数据而使使用量增加十倍;而游戏边缘服务器则需要在用户开始玩游戏之前增加额外的计算资源。

HPA无法实现基于时间敏感性和预测性数据的动态扩展控制。它既不允许逐步减少资源占用,也无法限制扩展的速度;同时,如果没有对应用程序进行大规模修改,也就无法根据多种指标来调整扩展策略。

HPA在处理边缘环境中短暂出现的流量高峰时,其低效的机制可能会导致以下问题:

  • 设备注册请求量突然激增
  • 用户连接数急剧增加
  • 媒体转码任务负担加重
  • 由于网关切换导致API请求量骤增

图2:表明系统突然负荷增加的短暂峰值

这种快速的变化会导致资源浪费——在那些硬件资源较为有限的节点上,这些被创建出来的Pod会消耗大量宝贵的CPU和内存资源,进而进一步加剧节点的压力,甚至可能导致节点被强制淘汰的情况

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

Kubernetes在autoscaling/v2版本中加入了對自定义指标的支持,但实现这一功能需要以下组件:

  • 指标服务器
  • 自定义指标API
  • Prometheus监控工具
  • 数据导出机制
  • 适配器组件

虽然这种架构已经得到了广泛的应用,但它确实会增加系统的资源消耗和运营复杂性,在那些资源有限的环境中,这些额外的开销可能会带来严重的问题。

图3:使用自定义指标及指标服务器实现的HPA自动扩展机制

该图展示了在支持外部指标的情况下,HPA如何通过autoscaling/v2版本实现自动扩展。在上述示例中,我们通过添加以下组件来配置集群:

  • 部署自定义指标适配器(Istio)以收集所需的指标数据。
  • 生成的适配器会将自己标记为“custom.metrics.k8s.io”。
  • 随后在集群中安装Prometheus监控工具。
  • Prometheus会从相关Pod中采集所需的数据。
  • 第一步中部署的Istio适配器会被配置为从集群中的Prometheus实例获取数据。
  • 用于数据查询的具体逻辑被编写到HPA的配置文件中。下方的代码示例展示了如何查询每个Pod接收到的请求数量。

通过施加负载,我们可以利用Grafana监控工具来观察系统实际是如何根据请求量进行自动扩展的。

边缘计算环境的架构框架

边缘计算环境的一般结构通常由若干位于终端用户附近的自主边缘节点构成,而中央控制模块则设在远程的云平台或数据中心中。

每个边缘节点都能独立运行,能够处理大量的本地流量,并执行许多用于管理这些流量的功能。因此,这类节点需要具备可扩展的逻辑系统,以便能够根据当前状况快速、高效地进行扩展。由于从边缘节点到云端的回程带宽通常较为有限,因此双向通信的成本可能会非常高。

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

  • 游戏引擎平台
  • 实时视频处理
  • 用于增强现实/虚拟现实的低延迟计算技术
  • 物联网网关数据的聚合处理
  • 机器学习的推理运算
  • 本地化内容的缓存/代理服务

这些应用各自表现出不同的可扩展性特征,而HPA传统的基于CPU的算法并不适用于优化这类应用的扩展需求。

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

为了解决HPA在扩展机制上的灵活性不足问题,人们开发了CPA,以满足以下需求:

  • 允许系统监控任意指标(例如CPU使用率、延迟、队列长度、自定义关键绩效指标等)。
  • 将监控指标与扩展算法分离开来。
  • 支持利用预测结果或补偿机制来主动进行扩展操作。
  • 通过实施安全的降级策略,防止出现“震荡”现象。
  • 保留足够的CPU资源余量,以应对边缘工作负载在突发情况下产生的变化。
  • 能够在保持稳定性的同时,比HPA更快地做出响应。

图4:CPA的架构结构

CPA的出现消除了Kubernetes内置自动扩展器所带来的限制,使开发者能够自行定义自定义扩展逻辑,同时仍能利用Kubernetes原生提供的可扩展控制机制。

CPA的评估算法

扩展评估过程会利用从数据收集阶段获得的各项指标来做出决策。早期的原型版本中,评估模块是根据固定的CPU使用阈值来进行扩展操作的,每当某个阈值被达到时,扩展幅度就会增加一定的数值。虽然这种实现方式较为简单,但它并不能真实反映现实世界中的自动扩展系统的工作原理,也无法满足边缘计算工作负载的实际运营需求。

相比之下,最新的实现方案采用了一种基于云服务提供商、游戏网络后端以及那些拥有运营大规模、对延迟敏感的平台的经验的SRE团队所提出的最佳实践而设计的自动扩展模型。这种新模型用三个主要的工作负载状态指标取代了原有的固定数值阈值,这三个指标分别是:CPU剩余处理能力、对延迟服务水平目标的关注程度,以及容器启动过程中的补偿机制。

## CPU剩余处理能力
当系统中拥有足够的CPU剩余处理能力时,就可以应对边缘工作负载中出现的突发性高负荷情况,从而避免出现排队等待或延迟问题。自动扩展系统会设置一个目标利用率安全区间,通常在70%到80%之间,以此来维持这种剩余处理能力缓冲。如果所有容器的平均CPU使用率持续高于这一安全区间阈值,自动扩展系统就会计算出需要增加多少个副本才能重新恢复这一缓冲空间。

## 对延迟服务水平目标的关注程度
如果应用程序能够提供延迟数据,比如第95百分位的响应时间,那么自动扩展系统就会利用这些数据来做出扩展决策。当实际延迟值接近或超过了预设的延迟服务水平目标时(例如,对于交互式边缘工作负载而言,这一目标是60毫秒),自动扩展系统会根据延迟偏差的程度相应地增加副本数量。这样一来,自动扩展系统就避免了仅以CPU使用率作为性能评估的唯一指标,而在那些I/O操作密集型或混合类型的工作负载环境中,这种做法尤为重要。

## 容器启动过程中的补偿机制
与中心式云环境中的容器启动速度相比,边缘节点上的容器通常会花费更长的时间来启动,这主要是由于磁盘吞吐量较低或者镜像文件尚未被完全加载所致。为了弥补这一问题,自动扩展系统会考虑根据本地观测数据估算出的容器启动时间,并在预期会出现高负荷的情况下提前进行扩展操作。如果CPU使用率上升速度过快,导致在容器完成启动之前系统对CPU的需求就已经超过了可用容量,自动扩展系统就会立即触发扩展动作。

综上所述,这三个输入信号共同构成了一个综合性的扩展决策机制,使得自动扩展系统能够比Kubernetes默认使用的固定HPA算法更加善于根据具体环境状况做出决策。具体的决策规则如下:
– 如果所有指标都处于正常范围内,那么就不会进行任何扩展操作。
– 如果有一个指标超过了相应的阈值,自动扩展系统会执行适度的扩展动作。
– 如果有两个或更多指标超过了各自的阈值,自动扩展系统将会执行规模更大的扩展操作,且扩展幅度会与这些指标的严重程度成正比。

需要注意的是,缩减资源规模应该是一个缓慢进行的过程,并且需要在一个稳定的环境条件下进行,这样才能避免因HPA算法导致的典型波动现象。因此,通过这种改进,自动扩展系统已经从一种简单的“阈值监控”型反应式扩展机制,转变成了一个能够有效管理现实环境中边缘工作负载的、具有情境感知能力的智能扩展引擎。

实现与负载生成

CPA使用了开源的Custom Pod Autoscaler框架,该框架是Kubernetes原生的控制器,允许开发者使用Python编写自定义逻辑来实现Pod的自动扩展。

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

  • metric.py – 用于收集指标数据
  • evaluate.py – 用于计算所需的副本数量

用户可以自定义间隔时间(默认为15秒),Kubernetes会定期调用CPA控制器。该控制器会运行metric脚本,将生成的JSON数据传递给evaluation脚本,然后根据评估结果来调整Pod的数量。

CPA配置文件

每个自定义自动扩展器都会通过一个config.yaml文件进行配置,该文件用于定义指标来源、评估逻辑、目标工作负载、扩展限制以及执行间隔:

name: cpautilization
namespace: default
interval: 10000
metricSource:
  type: python
  pythonScript: /cpa/metric.py
evaluation:
  type: python
  pythonScript: /cpa/evaluate.py
target:
  kind: Deployment
  name: testcpa
limits:
  minReplicas: 1
  maxReplicas: 20

指标收集脚本:该脚本用于获取CPU使用率、延迟时间或自定义信号等指标。在我们的实现中,我们使用了Prometheus来收集这些数据。

from cpa import metrics
import json


def 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使用率、延迟指标、Pod启动时间以及安全的扩展限制来计算所需的副本数量。

# 参数设置
CPU_HEADROOM_TARGET = 0.75      # 保持CPU平均使用率在75%左右
LATENCY_SLO_MS = 60            # 交互式工作负载的延迟指标示例值
SCALE_UP_FACTOR = 1.3              # 当系统负荷过高时,副本数量增加30%
MAX_SCALE_UP_STEP = 4                # 每次最多增加4个Pod
SCALE_DOWN_factor = 0.8              # 缓慢减少副本数量
MIN_STABLE_SECONDS = 30            # 需要连续30秒的稳定数据才能进行缩减操作
POD_STARTUP_seconds = 10           # Pod的预期启动时间


last_scale_time = 0                # 上次调整的时间
SCALE_COOLDOWN = 15                # 冷却间隔时间


def main():
  spec = json.loads(sys.stdin.read())
  evaluate(spec)


def evaluate/spec):
  global last_scale_time


  if len(spec["metrics"]) != 1:
    sys.stderr.write("应提供1个指标")
    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)  # 可选指标


  now = time.time()


  # 冷却间隔保护机制,避免频繁调整
  if now - last_scale_time < SCALE_COOLDOWN:
    output(current_replicas)
    return


  # 始终以当前的副本数量作为基准
  target_replicas = current_replicas


  # CPU使用率调节逻辑
  # 将利用率转换为比例值
  cpu_ratio = avg_cpu / 100.0


  if cpu_ratio > CPU_HEADROOM_TARGET:
    # 例如:如果CPU使用率为120%,则扩展比例为120/75=1.6,即增加60%的副本数量
    scale_multiplier = min(cpu_ratio / CPU_HEADROOMTARGET, SCALE_UP_FACTOR)
    proposed = math.ceil(current_replicas * scale_multiplier)


    # 限制每次调整的幅度
    step = min(proposed - current_replicas, MAX_SCALE_UP_STEP)
    target_replicas = current_replicas + max(step, 1')


  # 延迟指标调节逻辑
  if p95_latency is not None and p95_latency > LATENCY_SLO_MS:
    # 根据延迟指标的超标程度来调整副本数量
    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启动时间补偿机制
  # 如果预期负载较大,可以提前进行扩展
  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))


  # 安全地减少副本数量
  # 如果指标数据低于预设阈值,则减少副本数量
  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阈值来判断是否需要扩展的逻辑更为复杂且稳定。

长期负载压力

采用基于CPU剩余容量的扩展机制,使得CPA能够在保持性能利用率可预测的同时实现平滑扩展,同时避免了不必要的复制数量增加。

短暂的性能波动

CPA中的容器启动补偿机制以及保守的缩减规则有效防止了系统在遇到短暂的性能提升时出现过度反应。

渐进式负载增加

具备延迟感知功能的扩展逻辑使得自动扩展器能够在CPU资源达到极限之前就发现性能下降的情况,从而做出更快、更准确的响应。

通过调整CPU剩余容量、动态修改延迟性能指标阈值,并设置冷却时间间隔,我们生成了各种随机负载模式,以此模拟现实世界中不规则的网络流量行为。

图5:生成的随机负载

图6:部署的容器数量

与HPA相比,CPA具有以下优势:

  • 波动幅度更小
  • 启动的容器副本数量更少
  • 恢复到稳定状态的速度更快
  • 平均延迟更加稳定
  • 减少了CPU资源的浪费

图7:HPA与CPA的对比

经验总结

新的自动扩展逻辑为边缘应用带来了以下好处:

  • 单一指标并不能适用于所有情况。CPU只是众多可用于评估性能、延迟以及容器启动时间的指标之一。
  • 预测性扩展有助于减少系统的不稳定行为。通过利用容器启动补偿机制来预测集群在未来可能出现的饱和情况,有效减少了突发性的资源饱和事件。
  • 这种自动扩展逻辑允许延迟执行缩减操作。突然的规模缩减可能会在系统中引发波动,从而影响用户体验;而渐进式的缩减方式则能确保系统表现平稳,符合用户的预期。
  • 在计算资源和内存资源有限的边缘环境中,HPA的激进扩展策略可能会产生诸如内存压力、容器被强制终止或性能受限等不良后果。
  • CPA的设计具有更大的灵活性。它的架构将数据收集功能与数据处理逻辑分离开来。随着应用程序产生的监控数据变得越来越复杂,这种分离机制能够确保扩展逻辑的发展不会受到所收集数据类型的影响。

结论

通过使用基于延迟的评估机制来替代传统的固定阈值缩放方法,并对Pod的启动时间进行补偿,Custom Pod Autoscaler(CPA)已经成为一种灵活且可扩展的解决方案。CPA使工程师能够制定出既能满足应用程序性能需求,又能克服Kubernetes HPA在边缘计算环境中所存在的可扩展性限制的自动扩展策略。虽然正确实施基于CPA的扩展策略能带来诸多好处,但这也需要大量的微调工作、严谨的操作流程以及高质量的监控指标。

因此,在那些扩展操作会同时影响应用程序性能与用户体验的场景中,基于CPA的扩展策略才是最合适的选择。

Kubernetes事件驱动式自动扩展功能与水平Pod自动扩展机制相结合,为各种类型的工作负载提供了更多基于事件或外部信号进行扩展的选项。

CPA专为那些在决策过程中需要考虑启动延迟、资源限制以及多种性能指标的场景而设计,因此它特别适合在边缘计算环境中使用。通过适当的调优和监控,CPA能够为边缘环境提供一种可扩展、高效且能够确保自动扩展操作具有良好预测性的解决方案。

Comments are closed.