31 March 2023

云计算中弹性扩缩是一种根据业务需求和负载变化,自动调整云资源的能力。它可以帮助用户节省成本,提高效率,保证服务可用性。弹性扩缩的主要方式有两种:水平扩缩和垂直扩缩。水平扩缩是指增加或减少云服务器的数量,垂直扩缩是指增加或减少云服务器的配置。弹性扩缩是云计算的核心特征之一,也是实现云计算优势的关键技术。

本文重点介绍水平扩缩,因为笔者在实践中接触更多的水平扩缩。垂直扩缩目前在离线大数据场景更有优势。

整体链路考虑

链路中,上游的扩容需要注意避免扩容后消费能力增加后,对下游的掉用量过大,导致下游资源不足,影响整体性能。涉及分布式背压问题,可以考虑上下游同时做hpa,如果下游资源无法hpa,则上游需要考虑增加限速。关于背压,更多的信息可以参考:背压

hpa 的扩缩容策略

k8s的扩缩容策略公式为:期望副本数 = ceil[当前副本数 * ( 当前指标 / 目标指标 )]

当前指标为一段时间的平均值。 计算期望副本数时,有10%的buffer,从而避免频繁的扩缩。 期望副本数受限于策略设定的最大副本数和最小副本数。

如何设定hpa的扩缩容策略

开始设定hpa的策略,不要过于激进。策略的设定基于当前cpu/qps对应max节点来设定。等待策略稳定运行后,再调整策略。这样不会引入额外的变量,导致hpa策略的设定变得过于复杂和伤害系统的稳定性。

起步设定建议::

  • 以qps指标为例,原有副本数 30 ,流量高峰期 单机 qps 为 500(此时节点资源充足),夜间单机 qps 为100. 设定期望 qps 为450.
  • 最大副本数=原有副本数,最小副本数=原有副本数 * 0.7 (适用于大多数8-24点流量大,0-8点流量非常小的业务)

两者结合的效果是,白天为以 34 个节点提供服务(比之前更充裕),夜间以 21 个节点提供服务. 节点会从8点开始扩容,实现21节点根据流量逐渐增长到34节点.

应用预热问题

java等vm语言,在刚启动时,会因为有大量的代码被jit编译器从字节码编译成机器码,所以在启动期间会占用较多cpu;同时因为启动时,大量代码还没有经过jit编译,完全依赖java的解释器解释执行,执行速度在某些场景比jit后代码慢1w倍;就会导致新增的应用上超时较多。

解决方案:

  • 提供更多的资源,k8s增大容器的cpu,比如limit翻倍;
  • 提供更多cpu的同时,增大jvm的jit编译线程数
  • 预热/slow_start :在网关/注册中心层面上提供预热/slow_start功能。例如,新启动的节点会在1m内逐渐增加流量,1m后才打入全量,而不是在启动瞬间就全量打入流量。

应用快速启动和优雅停止问题

这里直接引用12factor中的定义

`12-Factor 应用的 进程 是 易处理(disposable)的,意思是说它们可以瞬间开启或停止。 这有利于快速、弹性的伸缩应用,迅速部署变化的 代码 或 配置 ,稳健的部署应用。

进程应当追求 最小启动时间 。 理想状态下,进程从敲下命令到真正启动并等待请求的时间应该只需很短的时间。更少的启动时间提供了更敏捷的 发布 以及扩展过程,此外还增加了健壮性,因为进程管理器可以在授权情形下容易的将进程搬到新的物理机器上。

进程 一旦接收 终止信号(SIGTERM) 就会优雅的终止 。就网络进程而言,优雅终止是指停止监听服务的端口,即拒绝所有新的请求,并继续执行当前已接收的请求,然后退出。此类型的进程所隐含的要求是HTTP请求大多都很短(不会超过几秒钟),而在长时间轮询中,客户端在丢失连接后应该马上尝试重连。

对于 worker 进程来说,优雅终止是指将当前任务退回队列。例如,RabbitMQ 中,worker 可以发送一个NACK信号。 Beanstalkd 中,任务终止并退回队列会在worker断开时自动触发。有锁机制的系统诸如 Delayed Job 则需要确定释放了系统资源。此类型的进程所隐含的要求是,任务都应该 可重复执行 , 这主要由将结果包装进事务或是使重复操作 幂等 来实现。

进程还应当在面对突然死亡时保持健壮,例如底层硬件故障。虽然这种情况比起优雅终止来说少之又少,但终究有可能发生。一种推荐的方式是使用一个健壮的后端队列,例如 Beanstalkd ,它可以在客户端断开或超时后自动退回任务。无论如何,12-Factor 应用都应该可以设计能够应对意外的、不优雅的终结。Crash-only design 将这种概念转化为 合乎逻辑的理论。`

实践中实现快速启动的手段:

  • 启动并行化
  • 去除不必要的依赖和代码

实践中实现优雅停止的手段:

  • 计划中停止时,先通知注册中心摘除节点,然后等待30s时间再退出
  • 添加jvm shutdown hook,以便等待执行中的任务完成。需要考虑线程池、mq等的destory.

hpa 波动频繁问题

需要结合 冷却期,执行间隔 等参数来控制 初期可以将缩容持续时间调大,从而避免频繁的缩容。比如,持续30/60分钟才缩容。 扩容则可以尽量快。比如,持续1/2分钟就扩容。

初期目标:低成本实现应用的稳定性不受伤害,让团队整体有hpa的使用经验,熟悉后再追求极致的缩容比。

结合以上所有点,给出起步的关键参数设置:

  • 以qps指标为例,原有副本数 30 ,流量高峰期 单机 qps 为 500(此时节点资源充足),夜间单机 qps 为100. 设定期望 qps 为450.
  • 最大副本数=原有副本数,最小副本数=原有副本数 * 0.7 (适用于大多数8-24点流量大,0-8点流量非常小的业务)
  • 缩容策略,指标持续时间设置60m;
  • 扩容策略,指标持续实践设置1m;
  • 启用slow_start/预热功能,设置预热/slow_start时间为1m;

其他的建议:

  • 使用slo体系结合燃烧率告警,便于发现线上是否出现违背slo的情况
  • 使用prometheus 进行监控,它可以提供方便的ad-hoc查询和指标聚合能力