1 弹性伸缩回答什么问题:容量跟随负载
电商 API 在下午只需少量服务器,晚间活动开始后请求量却可能迅速增加。始终按高峰准备,低谷时会闲置;始终按低谷准备,高峰时又会排队、超时。弹性伸缩要解决的是:根据负载与运行状态,自动调整一组实例的期望容量,让实际可服务容量逐步跟上需求。
期望容量是控制系统希望保有的实例数量,实际容量是当前已经存在的数量,就绪容量则是已经通过检查、能够接收业务的数量。它们可能暂时不同。把期望值从 2 改为 4,只表示系统开始补足实例,并不表示立刻获得了 4 台的处理能力。申请资源、启动系统、拉起应用、预热连接都需要时间。理解这段差距,是读懂伸缩事件的起点。
这里主要讨论水平伸缩:增加或减少承担同类工作的实例。C4 云服务器讨论过实例规格;把某台实例从较小规格升级到较大规格,属于垂直伸缩。垂直伸缩可能需要重启,也存在单机上限;水平伸缩可以逐步增加副本,但要求工作能够分散。2 台机器不会自动使任何应用的吞吐量翻倍,串行处理、共享锁和依赖服务都可能限制收益。
因此,弹性伸缩的前提是应用能够水平扩展。新实例必须拿到可用配置并参与处理;移除任意实例时,必要状态不能随之丢失;请求或任务也应能被合理分配。若用户登录状态只在某台实例内存中,扩容可能无法分担已有会话,缩容还会让用户掉线。伸缩系统改变的是副本数量,应用设计决定副本是否有用。
可以把伸缩理解为持续运行的反馈环:观察指标,比较目标,调整期望容量,等待实例就绪,再观察结果。它既有收益,也有边界:资源配额、可用容量、预算与依赖承载能力都会限制扩容。设计时要回答的不是“能否自动加机器”,而是“什么证据说明该加、加多少、多久可用,以及何时安全减少”。

2 伸缩组与启动模板:要扩什么、怎么出生
伸缩组管理一批用途相同、可以互相替代的实例,通常包含最小容量、期望容量和最大容量。最小容量保障基础服务,最大容量约束扩张范围,期望容量表示此刻的控制目标。例如最小 2、期望 3、最大 12,系统会努力维持 3 个实例;当策略提出增加到 15 时,仍受最大值 12 限制。上限能控制风险,却不能证明业务容量足够。
启动模板或启动配置回答“新增实例如何出生”。它通常包含镜像、实例规格、网络与安全组引用、身份权限、启动脚本和应用配置来源。C4 中的 CPU、内存与网络能力,在这里成为每个副本的能力边界。同一组实例应具有可比较的服务能力;若混用不同规格,就要确认平台如何折算容量单位,不能直接把实例数量等同于吞吐量。
模板应使用可追踪的版本。临时登录某台实例安装依赖,可能让它能服务,却无法保证后续自动创建的实例也能成功。更可靠的做法是将程序与依赖纳入镜像或可重复执行的部署过程,记录模板版本、应用版本与配置版本。启动脚本应允许重试,并将失败原因写入可查询的日志;权限不足、配置缺失和下载失败都应被明确暴露。
实例创建成功后,还要经过应用初始化与就绪检查。进程存在不等于业务可用:配置可能尚未加载,必要连接可能尚未建立。就绪条件应验证接收请求所需的最低能力,预热完成后才交给流量入口。否则扩容期间新增实例反而返回错误,而组内实例数量仍显得正常。
伸缩组还要选择子网,并让实例尽量分布到多个可用区;具体网络与可用区设计在 C2、C9 展开。跨区分布能降低单区故障的集中影响,但仍需确认剩余区域的资源与依赖可用。首次启用策略前,应直接用模板创建测试实例,验证它从空白启动到处理请求的全过程,而不只是检查模板字段是否填完。
3 指标与阈值:何时加、何时减
指标必须能反映“每份容量承受了多少工作”。CPU 利用率适合计算占比较高、增加实例后单机负载会下降的服务;请求队列长度或每个工作进程的积压量适合异步处理;每实例请求数、业务处理中任务数等自定义指标,适合有明确容量模型的场景。指标名称常见并不代表适用,关键是它与用户体验及实例数量之间是否存在稳定关系。
例如 API 在等待数据库时,CPU 可能只有 20%,但响应时间已经很长。此时只看 CPU 会错过故障。反过来,后台计算导致 CPU 升高,却没有影响前台请求,盲目扩容可能只增加成本。应同时查看业务吞吐量、延迟、错误率与依赖延迟,用这些证据解释扩容指标;不一定把每个观测指标都配置成伸缩触发器。
目标跟踪让系统围绕某个目标值调整容量,例如希望组内平均 CPU 接近 50%。假设当前有 4 个同规格、均已就绪的实例,平均 CPU 为 80%,负载保持不变,且可以均匀分摊,则估算期望实例数为:向上取整(4 × 80% ÷ 50%)=7。按这个模型,7 个实例的平均 CPU 约为 45.7%。实际策略还会受上下限、取整、预热与厂商算法约束,不能把这个估算当作必然执行结果。
这个计算隐含了处理能力近似线性增长的假设。如果新增实例也等待同一个慢查询,CPU 与响应时间不会按预期改善。若实例尚在预热,直接把它们当成完整容量,也会低估所需资源。解释计算时,必须明确当前数量指哪些实例、指标来自哪个时间窗口,以及实例规格是否一致。
步进策略则按越线程度给出不同动作,例如平均 CPU 连续 3 个评估周期超过 65% 时增加 1 个,超过 80% 时增加 3 个。目标跟踪适合表达“维持某个利用率”,步进适合表达“某种压力程度采取某种动作”。例子中的周期数、阈值与增量都应通过测试确定,不能直接复制为通用参数。

还要区分采样周期、聚合窗口和触发条件。瞬时值超过阈值,与连续多个窗口超过阈值,表达的是不同证据强度。平均值有助于平滑噪声,却可能隐藏个别实例热点;队列总量会随副本数量变化,而每实例积压量更接近单份容量的压力。指标缺失也不能直接解释为负载为零,缩容应对此采取保守处理。
4 冷却、步进与抖动抑制
伸缩动作生效有延迟,指标又可能在短时间内上下波动。若每次越线都立即加实例、回落就立即减实例,系统会在扩容与缩容之间来回摆动。这种振荡会重复支付启动成本,还会让就绪容量不稳定。抖动抑制的目标,是让系统对持续变化作出反应,同时避免把噪声解释成新的容量需求。
冷却时间是常见机制之一。假设某策略执行动作后有 300 秒冷却,若平台在此期间抑制该策略的重复动作,就能给新容量留出显现效果的时间。但不同平台对扩容、缩容、目标跟踪和健康替换的处理并不相同;配置时应明确冷却约束的是哪种动作,不能认为所有自动行为都会暂停。
看一个具体时间线:第 0 秒检测到 CPU 持续升高,策略增加 2 个实例;第 90 秒实例就绪;第 180 秒指标才覆盖足够的新容量效果。若第 60 秒再次扩容,依据的可能仍是旧实例承受的压力。300 秒冷却可减少这种重复反应。然而,若流量持续快速增长,过长的扩容冷却也可能阻止及时补足容量,需要配合预热参数与更大的扩容步进。
再看持续 30 秒的尖刺。若必须连续 3 个 60 秒窗口越线,尖刺可能不会触发扩容;若短窗口已经触发,后续 300 秒冷却也不会撤销已执行的动作。冷却主要影响后续动作,是否触发首次动作由评估窗口等条件决定。把所有尖刺过滤都归功于冷却,会误判策略效果。
缩容通常应比扩容保守。可以为缩容设置更低的触发阈值、更长的持续观察期,或限制每次减少的数量。例如扩容关注超过 65% 的持续压力,缩容则要求低于 30% 持续 10 分钟,并且没有明显队列积压。这种间隔留出缓冲,避免刚扩出的容量因平均利用率降低而马上被撤回。

合理参数来自启动耗时与负载变化速度。应记录从动作触发到实例就绪、再到业务指标稳定的时间,分别设计扩容与缩容节奏。若每次增加数量过小,即使频繁触发也追不上高峰;若每次增量过大,则可能长期过量配置。步进解决动作幅度,窗口与冷却解决动作时机,几者要一起验证。
5 健康检查与替换:坏实例谁踢掉
容量控制还需要处理另一类变化:请求量没有增加,但某个实例已经失去服务能力。主机故障、应用崩溃、启动失败都可能让实际可服务容量下降。伸缩组通常依据配置的健康来源,将不健康实例移除并补建替代实例,以恢复期望容量。这里的目的在于恢复可用副本,并不意味着期望容量一定增大。
健康来源需要明确区分。基础设施检查可能只能发现实例或宿主机问题;应用检查才能识别进程虽然存在、接口却无法响应的情况。如果使用流量入口的检查结果作为替换依据,还应核对检查端口、路径、超时与连续失败次数。某个检查被配置在入口上,并不自动表示伸缩组会使用它来替换实例。
新实例需要合理的健康检查宽限期或启动保护。应用正常启动需要 120 秒,而检查从第 10 秒开始,连续失败后立即替换,就可能出现“创建、误判、销毁、再创建”的循环。宽限期应覆盖正常初始化时间,并保留异常启动的可见性;无限放宽会让真实故障长期占据容量。就绪检查负责决定何时接流量,替换判定负责决定何时重建,二者职责要清楚。
移除实例也有顺序。对于仍有部分服务能力的实例,应先停止分配新请求,再让已有连接或任务在限定时间内结束,然后终止实例。这个过程常称为排水或连接耗尽,具体流量入口机制留到 C12。若实例已经彻底失联,就无法保证排水完成,此时要依靠客户端重试、业务幂等与任务恢复控制影响。

健康检查还要避免放大共享依赖故障。如果每次数据库短暂超时都使所有实例被标为需要销毁,整组实例会反复重建,数据库又承受新的连接与初始化请求。应区分实例自身无法服务、暂时不宜接流量,以及共享依赖故障,并明确各自触发的是隔离、告警还是替换。只有替换能够解决的问题,才适合通过重建来修复。
6 定时与预测:活动容量预热
反馈伸缩需要先看到负载,再等待容量就绪。对于已知时间的活动,这段等待可以提前安排。若活动 20:00 开始,实例启动、应用预热与验证合计需要 6 分钟,就应在活动前预留超过这段耗时的准备窗口。定时策略可以提前提高最小容量或期望容量,让活动开始时已经拥有可服务能力,而不只是刚提交创建请求。
定时动作必须与动态策略协调。例如临时把期望容量调到 8,但最小容量仍为 2,动态缩容可能在活动开始前把空闲副本减少。若业务要求预热容量必须保留到指定时间,应在该窗口提高容量下限,并在活动结束、队列消退后恢复常态。恢复动作也要记录,避免临时安排变成长期成本。
计划里应写清时区、生效时间、活动持续时间和撤回条件。预热完成要通过就绪数量、试探请求、延迟和错误率确认。只看到实例运行并不能验收。若某个可用区资源不足,定时动作仍可能失败,因此应留出调整规格、分布或申请资源的时间,并监控创建失败事件。
预测伸缩可作为可选能力,根据历史负载周期估计未来需求,适合规律较稳定且历史数据足够的业务。新活动、流量结构变化、促销入口改变都可能使预测失准。预测结果应有容量边界,并由实时观测修正。对于首次大型活动,依据压测与业务预估安排定时预热,通常比单纯依赖历史预测更容易解释和验收。

7 常见坑:无状态假设破灭、缩容杀任务、指标选错
第 1 个坑是把“部署了多个副本”当作“已经无状态”。登录会话、购物车中间状态、上传进度或任务归属若只存在实例本地,实例被替换或缩容时就可能中断业务。应先盘点哪些状态必须跨实例保留,再设计共享状态服务、可恢复任务记录或客户端凭据等机制。短期的会话固定可能缓解路由问题,却不会消除实例失效带来的状态风险。
第 2 个坑是缩容杀死长任务。某个工作实例 CPU 很低,可能正在等待外部处理,并不代表可以立即终止。任务系统应有停止领取新任务、完成或交还在途任务、超时后恢复执行的协议。必要时对正在执行重要任务的实例设置缩容保护,并安排保护解除条件,避免所有实例长期受保护导致无法回收。
任务重试还涉及业务正确性。支付回调、订单处理等操作可能在终止前已经完成业务写入,却来不及确认任务成功;重试后必须识别重复操作。幂等键、状态检查与明确的任务确认时机,比单纯延长终止等待更关键。排水期限应覆盖常见任务,超过期限的工作则必须有恢复路径。
第 3 个坑是指标选错,尤其是用伸缩掩盖内存泄漏。增加实例可能暂时降低单机请求压力,却不会修复每个进程持续增长的内存占用。若空闲后内存仍不回落,并且占用更接近实例运行时间而非流量,就应调查泄漏或无界缓存。伸缩可以缓冲影响,但应同时修复程序并约束单实例资源风险。
还有一个容易忽略的指标陷阱:平均 CPU 较低,少数实例却持续过载。原因可能是长连接集中、热点请求或工作分配不均。继续增加空闲实例未必改善热点,应对照单实例分布与业务请求类型分析。若扩容指标无法随副本增加而合理回落,就要重新验证容量模型,不能只继续下调阈值。
最后,最大容量、配额和预算需要各司其职。最大 12 只约束这组实例数量,不约束所有依赖成本;账户配额足够也不代表某个区域随时能创建实例。监控应包含触顶、创建失败、就绪不足与异常替换次数,使“策略请求扩容”和“真正获得容量”之间的失败能够及时被发现。
8 案例:高峰电商 API 的伸缩策略
某电商 API 的晚高峰流量约为日常的 3 倍,实例分布在 2 个可用区。团队设定最小容量 2、最大容量 12,常态期望容量由实时策略调整。2 个最小副本提供基础冗余,但能否在单区故障后承接全部流量,还要依据剩余容量测试,不能仅凭跨区部署作出保证。
先进行容量测量。在固定请求结构下,确认单实例在目标延迟与错误率约束内的稳定吞吐量,再测试增加副本后的总体收益。例如日常 2 个实例平均 CPU 为 40%,在负载可线性分摊的假设下,3 倍流量若仍由 2 个实例处理,需求将超过可用 CPU 能力;按 50% 目标估算,需要向上取整(2 × 40% × 3 ÷ 50%)=5 个实例。这个数只是初始计划,还要考虑请求结构与依赖限制。
策略采用 CPU 目标跟踪,目标 50%,同时观测请求延迟、错误率与数据库延迟。活动前将最小容量临时提高到 6,给估算留出余量,并完成试探请求验证。活动期间保留动态扩容,最大值仍为 12;如果压测证明依赖在更低容量就达到极限,则应先解决依赖或调整流量入口的保护措施,不能因为组上限为 12 就认为扩到 12 一定有效。
启动测量显示,新实例通常在 120 秒内就绪,新增容量的指标效果还需要后续窗口才能看清。团队以 300 秒作为相关策略冷却的初始值,核对平台是否另有预热设置;缩容要求低负载持续 10 分钟,每次小幅减少,并检查在途请求与任务。活动结束后先确认流量和积压回落,再恢复最小容量 2,避免流量尾部仍高时被计划动作强行收缩。
上线前做 3 类演练:逐步把流量提高到 3 倍,观察扩容能否追上;终止或隔离 1 个测试实例,观察检查与替换;让负载缓慢回落,观察缩容是否影响在途工作。每次记录触发时刻、期望与就绪容量、模板版本、实例就绪耗时、业务延迟、错误率和依赖状态,并注明测试流量是否覆盖真实高峰的请求结构。
验收交付包括可追踪的模板、策略参数及其理由、冷却与预热测量、容量边界,以及演练记录。成功标准是高峰期间业务指标符合预先约定的目标、低谷能够安全回收、故障替换不引发持续抖动。仅截图证明“实例从 2 变成 6”还不足以验收,因为数量变化只是实现服务目标的中间过程。
9 排查案例:扩了仍超时、缩容雪崩、振荡
活动开始后实例从 4 个增加到 8 个,API 仍然超时。先建立同一时间轴:流量何时增加、策略何时触发、每个实例何时就绪、超时何时出现。然后把伸缩事件、组与单实例指标、业务延迟、依赖延迟对齐。期望容量增加但实例创建失败,与实例全部就绪却吞吐不变,需要走不同路径。
路径 A 是数据库等依赖成为瓶颈。证据可能是实例已全部就绪,CPU 没有明显升高,而数据库等待时间、连接等待与慢查询同时增加。新增实例使数据库连接总量进一步上升,超时甚至加重。此时应检查查询、连接预算与请求并发,必要时采取限流或降级,再评估依赖容量。验收修复要看依赖等待与业务延迟回落,不能以继续扩容代替诊断。
路径 B 是冷启动太慢。伸缩事件显示创建成功,但应用日志显示仍在初始化,就绪容量长期低于期望容量。应拆分资源申请、系统启动、应用启动与预热耗时,定位最慢环节。若程序启动时要完成大量初始化,可优化镜像与启动流程,或提前定时预热;若创建环节受配额或资源不足阻塞,则应处理相应约束。更低的 CPU 目标无法缩短这些步骤本身。
路径 C 是健康检查误杀。实例不断出生又被终止,事件中的健康失败与应用尚未启动完成的时间吻合,或检查路径、端口、网络访问条件配置错误。先核对健康失败原因和模板版本,再调整检查与合理宽限期,用新实例完整验证。若所有实例同时因共享依赖变慢而失败,应修正检查语义,避免反复重建放大故障。

另一类故障发生在缩容后:剩余实例瞬间过载,重试增多,延迟进一步恶化,形成雪崩。证据包括缩容时刻与错误率上升吻合、在途任务被中断、剩余实例连接或队列突增。先恢复足够容量并暂缓继续缩容,再核对排水期限、缩容步幅与低负载判断。若重试没有间隔与上限,还要控制重试放大效应。恢复后必须重做下降负载演练。
若实例数量反复在相邻值间变化,先看动作是否对应同一组过于接近的阈值,再看评估窗口是否过短、预热是否缺失,以及定时策略与动态策略是否相互影响。扩容后平均值自然下降,若缩容条件过敏,就会马上撤销刚增加的容量。可拉开扩缩阈值、延长缩容观察、调整步幅,并基于启动实测设置预热或冷却。每次只改变有证据支持的参数,比较修改前后的事件时间轴。
排障结论应能形成完整因果链:某项观测说明什么,采取的修改解决哪个环节,复测又怎样证明改善。只写“调大冷却后好了”无法说明是否真正消除了问题;若业务流量恰好下降,参数修改可能只是与恢复同时发生。保留流量、事件与依赖证据,才能把偶然恢复转化为可复用的策略经验。
放进方案里
在方案中先写出可扩展假设:哪些请求或任务能够分散到多个实例,哪些状态必须跨实例保留,单实例退出后如何继续工作。用压测说明单实例能力与增加副本后的收益,并标出数据库等共享依赖的承载边界。若收益已经明显下降,应注明限制来源,避免把增加副本写成没有上限的容量承诺。
列明伸缩组的启动模板版本、镜像与应用版本、实例规格、配置来源和运行权限,说明实例如何完成初始化以及何时算就绪。记录子网与可用区分布,并说明新增实例创建失败、某个可用区容量不足时如何告警和处置。模板必须能够独立创建可服务实例,不能依赖人工补装。
给出最小、最大和常态期望容量的依据,写清伸缩指标、统计口径、目标值或步进条件、评估窗口与指标缺失时的处理。将业务延迟和错误率作为效果验收证据,同时列出触顶、就绪不足及依赖异常的告警。说明最大容量与配额、预算之间的关系,指定谁负责处理触顶后的业务保护。
分别描述扩容与缩容节奏,附上启动和预热耗时的测量结果,解释冷却及观察窗口的选择。写明缩容前如何停止新工作、等待在途工作、恢复中断任务,以及何时允许终止实例。健康检查的来源、失败判定、宽限期和替换流程也要明确,避免共享依赖故障触发整组反复重建。
最后附活动预热时间表和演练记录,覆盖流量上升、实例故障、负载回落与依赖变慢。记录策略版本、事件时间轴、就绪容量和业务结果,给出通过标准及回退条件。让审核者能据此判断容量是否来得及补足、是否能安全回收,以及自动化失效时由谁接手。
10 小结:走向云上缓存与 NoSQL(C14)
弹性伸缩通过指标与策略调整期望容量,通过模板、就绪检查和健康替换形成实际服务能力。设计重点是可水平扩展的前提、可解释的指标、合理的动作节奏,以及安全退出机制。可用容量和业务结果比实例数量本身更能说明效果。
可以自测:4 个实例平均 CPU 为 80%,目标 50% 时,估算为何得到 7 个?为什么数据库变慢时继续扩容可能无效?300 秒冷却为什么不能保证过滤首次尖刺?若能结合假设和时间轴回答,就掌握了本课的核心。
学习路径上,下一课是 C14 云上缓存与 NoSQL:先解决「扩出来的实例如何从共享层恢复热状态」。块存储(C6)与负载均衡(C12)在路径其他位置展开,本课只把容量控制与实例生命周期主线建立起来。
下一课转向 C14 云上缓存与 NoSQL。
伸缩会不断创建与替换实例;本地盘与进程内状态默认不可靠。下一课讲缓存与 NoSQL 如何承载可重建的热数据与会话,并检查:扩出来的新实例能否从共享缓存恢复必要状态、缓存失效与击穿会不会在伸缩抖动时放大。
作业:把本课的无状态假设、排水时间和健康宽限期各写成 1 条,作为 C14「哪些状态可进共享缓存、哪些必须可重建」清单的输入。