你有没有见过这样的“魔法餐厅”?饭点时客人像潮水一样涌进来,后厨却不会手忙脚乱——因为餐厅会自动召唤更多厨师、架起更多灶台;深夜里客人稀少,厨师们又会悄悄“下班”,把工位留给第二天早高峰。这一切不需要店长吼叫,不需要提前囤人,餐厅就像一只聪明的生物,随客流涨落而呼吸。 这,就是云上的“弹性”。 今天我们要拆解的 Anyscale,就像是给“大模型餐厅”装上了一套会呼吸的后厨系统。大模型推理服务(就是让 AI 回答问题的那些服务)往往面对忽高忽低的流量:上午十个请求,下午十万个请求。如果机器配少了,高峰时会排队卡死,顾客骂声一片;如果机器配多了,闲时又要白烧电费。Anyscale 的回答是:让计算资源跟着请求量自动伸缩。它的底层基石是开源的 Ray 框架——一个能指挥成千上万台机器协同工作的“总调度台”;它的上网服务是弹性部署——把大模型推理打包成“招牌菜”,自动分发到每一个“灶台”上;它的核心本领是弹性伸缩——像餐厅根据排队人数增减小工一样,根据请求队列长度增减计算资源。 如果你是一名 AI 平台工程师,这篇内容会是你设计“能打胜仗、省得了钱”的大模型推理系统的必修课——从 Ray 分布式原语到资源调度,从冷却期到实例压缩,学完你就能理解 Anyscale 这类平台为什么能成为 AI 基础设施的新宠。

岗位关联

一句话价值:掌握弹性推理的核心架构与调优思路,帮助你从“手动配机器”升级为“让服务自己决定要多少机器”。

目录
  1. 可视化演示
  2. Ray框架基础
  3. 弹性伸缩架构
  4. 推理服务部署
  5. 资源调度优化
  6. 弹性伸缩策略
  7. 成本控制与总结
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示Ray框架弹性伸缩架构与GPU拓扑感知调度策略,切换模式观察自动扩缩容和资源分配的动态过程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

弹性伸缩架构 自动伸缩器根据请求队列和GPU利用率动态调整推理副本 负载升高时自动扩容,负载降低时自动缩容 replicas=ceil(queue_len/max_batch) Ray Serve支持多版本灰度发布与自动回滚保障稳定性

Ray框架基础

想象你开了一家“云端魔法餐厅”,后厨里有几十位厨师——但他们分布在不同的城市、不同的灶台(服务器)上,怎么让他们像一个人那样配合?你需要一套“总调度台”,把它放在每个灶台之间传话、分菜、记笔记。这就是 Ray 框架。

Ray 是一个开源的分布式计算引擎,它把几百台普通机器组织成一个虚拟的“超级后厨”。在 Anyscale 中,Ray 就是底层的地基——所有弹性伸缩、部署、调度,都发生在 Ray 这张计算网络上。你可以把 Ray 理解为“用于 AI 计算的操作系统”:它管理了进程、内存和任务调度,让你写的代码不需要关心“跑在哪台机器上”。

Ray 提供两种核心编程原语:Task(任务)和 Actor(演员)。Task 像一次性派给厨师的“订单”——做完就结束,适合纯计算;Actor 像一个长驻的“领班”——它在内存中长期存活,记录状态、响应请求,非常适合作推理服务的 Worker。此外,Ray 还自带分布式对象存储,好比后厨中央的“传菜电梯”:一份大文件放进去,任何厨师都能快速取用,不需要反复复制。

如果你把 100 个厨师扔进后厨,但传菜通道只有一条,整体出餐速度并不会变成 100 倍——这就是为什么分布式系统要精心设计调度。Ray 的价值,就是把这个“传菜通道”做得尽量宽、尽量聪明。

Ray 集群吞吐量=总计算量关键路径耗时≈N×单节点吞吐调度开销\text{Ray 集群吞吐量} = \frac{\text{总计算量}}{\text{关键路径耗时}} \approx \frac{N \times \text{单节点吞吐}}{\text{调度开销}}
这个公式想说明:集群的吞吐不是把所有节点吞吐加起来就行,还要扣掉“调度开销”——传菜慢了,厨师再多也白搭。
  1. 总计算量所有 Task 完成的工作总和,比如一次推理请求的总算力需求。
    总计算量=N×单节点吞吐\text{总计算量} = N \times \text{单节点吞吐}
  2. 关键路径耗时从提交任务到拿到结果的最长链路时间,包含排队、计算和数据传输。
    关键路径耗时=调度开销+计算时间\text{关键路径耗时} = \text{调度开销} + \text{计算时间}
  3. 调度开销Ray 调度器分配任务、传递对象数据的额外时间。开销越高,集群实际吞吐越接近“筷子夹菜”而非“传送带供菜”。
    有效吞吐≈N1+αN,α=单节点调度开销单节点计算时间\text{有效吞吐} \approx \frac{N}{1 + \alpha N}, \quad \alpha = \frac{\text{单节点调度开销}}{\text{单节点计算时间}}

弹性伸缩架构

魔法餐厅的门童有一套神奇的规则:门口排队超过 5 桌,就打电话叫 3 位兼职厨师;排队清空后,兼职厨师就结算走人。这个“排队人数→厨师数量”的反馈回路,是餐厅自动运转的智慧源泉。

Anyscale 的弹性伸缩架构也依此设计,分为三层:感知层(传感器的眼睛)、决策层(大脑)、执行层(手脚)。感知层持续采集指标:请求队列长度(排队桌数)、每个 Worker 的 GPU 利用率(厨师忙碌程度)、响应延迟(上菜速度);决策层拿着这些数字跑规则或算法,判断“现在该加人还是减人”;执行层则负责真正启动新 Worker 或回收空闲 Worker。

这一切构成一个闭环:指标采集 → 决策 → 执行 → 再采集。执行新 Worker 的启动并非瞬间完成,冷启动可能耗时几十秒甚至几分钟,因此决策层必须带上“前瞻性”——只看当前排队人数还远远不够,要会看流量的“惯性”。

其中 $\lambda_{\text{predicted}}$ 是下一时刻预测的请求到达率,$\mu_{\text{single}}$ 是单个 Worker 能处理的最大请求率。

这里的“前瞻性”非常关键——如果等排队到 100 桌才叫厨师,等厨师到场已经是半小时后的事了。弹性伸缩系统必须像有经验的门童,看到地铁口涌出一大波人,就提前打电话叫人。

目标 Worker 数=⌈λpredictedμsingle⌉\text{目标 Worker 数} = \left\lceil \frac{\lambda_{\text{predicted}}}{\mu_{\text{single}}} \right\rceil
简单说:下一波客人预计每秒来 100 个,一位厨师每秒只能做 20 道菜,那就得至少开 5 个灶台。
  1. 预测请求率基于历史流量和当前趋势推断的下一个时间窗请求数。通常用移动平均或简单指数平滑。
    λpredicted=(1−α)λactual+αλprev\lambda_{\text{predicted}} = (1 - \alpha) \lambda_{\text{actual}} + \alpha \lambda_{\text{prev}}
  2. 单 Worker 容量一个推理实例在目标时延内能处理的最大请求率,由模型速度和硬件决定。
    μsingle=1平均推理耗时\mu_{\text{single}} = \frac{1}{\text{平均推理耗时}}
  3. 向上取整只要有零头,就多开一个 Worker,保证不排队。
    ⌈x⌉≥x\lceil x \rceil \ge x

推理服务部署

菜单上的招牌菜得让每位厨师都能做——做法要写成标准配方,材料要提前备好。推理服务的“配方”,就是模型 + 推理代码 + 依赖环境,打包成一个不可更改的镜像。有了镜像,任何一台空机器上来都能 10 分钟开张,不需要现装环境。

Anyscale 在部署层做了三件重要事情。第一,模型打包与镜像加速:大模型权重像“食材包”,可以放在分布式对象存储中,Worker 启动时按需拉取,不同版本可以共享底层数据层,避免每次重复搬运上百 GB 的权重。第二,滚动发布与蓝绿发布:更新模型时,不会“轰”地关掉所有旧服务,而是逐个替换(滚动);或者让新旧两套并存、一次性切流量(蓝绿)。第三,请求路由与亲和性:新请求不会随便丢给任意 Worker,而是根据“亲和性”——比如同一用户的多次请求尽量进同一个带缓存状态的 Actor——减少重复计算。

部署就像开一家连锁店:配方(镜像)必须标准,换菜单(发版)必须平滑,客人入座(路由)必须合理——只有这三个环节都做好了,新店(新 Worker)开张才能又快又稳。

部署利用率=∑各 Worker 实际处理请求数总容量×时间窗口\text{部署利用率} = \frac{\sum\text{各 Worker 实际处理请求数}}{\text{总容量} \times \text{时间窗口}}
部署阶段的核心目标不是“能跑”,而是“跑得稳、换得快、不浪费”。
  1. 实际处理请求数每个 Worker 在时间窗口内完成的推理次数。
    实际负载=∑i=1Wri(t)\text{实际负载} = \sum_{i=1}^{W} r_i(t)
  2. 总容量所有 Worker 最大处理能力的总和,代表“如果全开足马力能处理多少”。
    总容量=W×μsingle\text{总容量} = W \times \mu_{\text{single}}
  3. 部署利用率实际处理量除以总容量。利用率太低说明资源闲置;逼近 1 说明随时会排队。
    ρ=rμ∈[0,1)\rho = \frac{r}{\mu} \in [0, 1)

资源调度优化

后厨有了足够的厨师,排班经理还得决定:谁会颠勺?谁做甜点?哪个灶台先炒哪个菜?这里说的就是资源调度。Anyscale 的调度器会为每个推理请求/模型副本寻找最合适的 Worker 和硬件。

首先是多 Worker 并行推理。同一个模型可以同时放多个副本(Worker),每个副本吃一部分流量。调度器要决定每个请求交给哪个副本,才能让负载均衡。如果请求是按顺序排队,整体吞吐取决于“最慢的那个 Worker”——这就像一排收银台,只要有一个收银员手慢,全场排队就变长。

其次是 GPU 显存管理与批处理。大模型推理极度依赖显存,调度器要决定一个 GPU 上放多少个模型副本,以及多少个请求合并成一个 batch 一起算。Batch 越大吞吐越高,但单请求延迟也会变长。最后是优先级队列与亲和性:交互式服务(聊天)优先级高于离线批量任务,就像现场顾客的大单优先于外卖订单;有状态 Actor 的请求要尽量调度到同一台机器。

资源调度的核心不是“把所有机器塞满”,而是“在延迟红线内让总吞吐最大”。好的排班经理不一定会让每个厨师都手忙脚乱,但一定能让每一条队列都走得流畅。

最优批大小(B∗)=arg⁡max⁡B[B延迟(B)],s.t. 显存(B)≤GPU 显存\text{最优批大小}(B^*) = \arg\max_{B} \left[ \frac{B}{\text{延迟}(B)} \right], \quad \text{s.t. } \text{显存}(B) \le \text{GPU 显存}
调度器要做的平衡是:让 GPU 排队排得尽量满,又不能满到把单请求延迟顶破。
  1. 批吞吐批越大,每个请求分摊的固定开销越小,但延迟也会变高。
    吞吐(B)=B延迟(B)\text{吞吐}(B) = \frac{B}{\text{延迟}(B)}
  2. 显存约束Batch 大小受限于 GPU 显存。
    显存(B)=权重+B×每请求 KV Cache\text{显存}(B) = \text{权重} + B \times \text{每请求 KV Cache}
  3. 最优解在显存允许范围内找一个让“吞吐/延迟”最大的点。

弹性伸缩策略

弹性伸缩要解决一个灵魂问题:到底什么时候加人、什么时候减人?加多少?加了之后要等多久才能再加?这听起来简单,但现实中无数系统就栽在这里。

Anyscale 的策略引擎一般采用“指标 + 阈值 + 冷却期”组合。指标可以选择 CPU 利用率、GPU 利用率、请求队列长度、P95 延迟等;每个指标设上下两个阈值:超过上阈值(比如队列长度 > 10)触发扩容,低于下阈值(比如队列长度 < 2)持续一段时间才触发缩容。之所以要有冷却期,是因为 Worker 冷启动需要时间,扩容后系统需要时间“消化”流量的下降;如果阈值抖动太快,系统会像一个不停开门关门的冰箱——每次伸缩都有代价,最终白白烧钱。

扩容可以保守一些:一次加一个,观察完再决定;也可以激进一些:预测流量洪峰,提前拉起一批 Worker 热身。缩容则要更稳重,因为杀进程容易,重新拉起难。系统还要有回稳策略——当多个指标互相矛盾时(CPU 高但队列短),以“用户可感知的指标”(延迟、排队)为准。

这一节的核心思想是:伸缩策略不是“越灵敏越好”,而是“稳、准、省”三者平衡。就像餐厅不会因为门口来了两个人就立刻请 5 个临时厨师——一切都要看趋势、看时长、看成本。

扩容触发: Q(t)>Qup 且持续 ΔT\text{扩容触发: } Q(t) > Q_{\text{up}} \ \text{且持续}\ \Delta T
缩容触发: Q(t)<Qdown 且持续 ΔTcool\text{缩容触发: } Q(t) < Q_{\text{down}} \ \text{且持续}\ \Delta T_{\text{cool}}
冷却期像餐后的“收尾时间”:刚送走一波客人别急着撤厨师,等确定没有下一波再说。
  1. 队列长度当前等待处理的请求数,是“排队等座的人”。
    Q(t)=max⁡(0,A(t)−μW(t−t0))Q(t) = \max(0, A(t) - \mu W (t - t_0))
  2. 持续判断指标必须连续超过阈值一段时间,而不是瞬间抖动就触发。
    状态维持(Q>Qup,ΔT)\text{状态维持}(Q > Q_{\text{up}}, \Delta T)
  3. 冷却时间每次伸缩操作后,必须等待指定时间才能进行下一次操作。
    ΔTcool≥平均冷启动时间\Delta T_{\text{cool}} \ge \text{平均冷启动时间}

成本控制与总结

整套弹性推理系统最终的检验标准不是技术酷炫,而是“花更少的钱,办同样的事”。Anyscale 的成本控制策略,本质上是把“计算资源”当作“按需租赁的厨师”来看待。使用弹性伸缩后,低谷期自动缩容,高峰期自动扩容——相比“按峰值长期固定机器”,通常能省下 40%~70% 的成本。

具体优化手段包括:冷热实例管理——热实例常驻处理线上流量,冷实例只存镜像和数据,需要时几十秒拉起;Spot 实例利用——Anyscale 可以在云厂商的甩卖节点(Spot/抢占式实例)上跑无状态推理 Worker,便宜 60%~90%,被回收时动态换新;多集群与自动关停——非业务时段把开发集群缩容到零。

回首整套系统:Ray 是后厨的血管和神经(基础框架),弹性伸缩是呼吸节奏(架构),部署是菜谱标准化(交付),调度是排班艺术(优化),策略是门童的智慧(决策),成本是经营的底线。六者合在一起,才是 Anyscale 作为“弹性推理服务平台”的真正含义——它不是某一个杀手锏,而是一整套让 AI 服务“能呼吸”的生态系统。

总成本=C常驻+C弹性=∑t(con⋅Wbase+cspot⋅W弹性(t))Δt\text{总成本} = C_{\text{常驻}} + C_{\text{弹性}} = \sum_{t} \left( c_{\text{on}} \cdot W_{\text{base}} + c_{\text{spot}} \cdot W_{\text{弹性}}(t) \right) \Delta t
成本优化的最高境界,是让每一分钱都在“干活”,而不是在“待命”。弹性不是免费的午餐,而是把每顿午餐都算得刚刚好。
  1. 常驻成本为保底流量常开的 Worker 费用,不受伸缩影响。
    C常驻=con⋅Wbase⋅TC_{\text{常驻}} = c_{\text{on}} \cdot W_{\text{base}} \cdot T
  2. 弹性成本随流量波动增减的 Worker 费用,按实际运行时长计费。
    C弹性=∑tcspot⋅W弹性(t)⋅ΔtC_{\text{弹性}} = \sum_t c_{\text{spot}} \cdot W_{\text{弹性}}(t) \cdot \Delta t
  3. 总成本两者之和。弹性策略的任务,就是在延迟达标的前提下最小化这个值。

AI 应用场景

场景一:大模型 API 服务的流量洪峰应对

阶段:部署与运维

解决的问题:传统方式按峰值流量固定 GPU 集群,夜间低谷利用率不足 10%,高峰期又可能因临时扩容太慢而雪崩。Anyscale 的弹性伸缩根据请求队列长度自动增删 Worker,让利用率稳定在 40%~70% 之间。

具体案例:一家 AI 写作公司上线了爆款 H5 活动,推理请求量在 2 分钟内增长 15 倍。过去需要运维半夜手动加节点;接入 Anyscale 后,系统在活动开始 1 分钟便自动扩容 3 个 Worker,活动结束 5 分钟自动缩容,全程无人值守。成本同比下降 55%。

场景二:多模型 A/B 测试与平滑发布

阶段:模型迭代发布

解决的问题:新模型上线时,若直接全量替换,一旦效果差将影响全部用户。Anyscale 的蓝绿发布 + 滚动更新机制,让新旧模型各占部分流量,边观察指标边切流量。

具体案例:某语音助手团队每周更新 3 次模型。在 Anyscale 上,他们用蓝绿发布将新模型先切 5% 流量,观测意图识别准确率稳定后逐步上调到 100%;如果效果不佳,一键回滚到旧版。整个流程自动化,不再需要凌晨熬夜盯发布。

场景三:混合负载下的成本优化

阶段:资源调度与成本优化

解决的问题:交互式推理要求低延迟,离线批量推理追求高吞吐,两者跑在同一集群会互相抢资源。Anyscale 通过优先级队列和 Spot 实例,把离线任务调度到便宜的可抢占节点。

具体案例:一家 AI 视频生成公司,白天跑用户实时生成任务,晚上跑批量训练数据预处理。他们在 Anyscale 上为两类任务设置不同优先级和实例类型:实时任务用常驻 GPU,离线任务用 Spot 实例。一个月后账单下降 43%,离线任务也从未影响在线服务延迟。

Python 演示

python87 行
import randomimport timefrom dataclasses import dataclassfrom typing import List  @dataclassclass WorkerInfo:    """一个推理 Worker 的简单描述"""    worker_id: int    load: float = 0.0  class ElasticBatchServer:    """简化版 Anyscale 弹性推理批处理模拟器"""     def __init__(self, base_workers: int = 3, max_workers: int = 6):        self.workers: List[WorkerInfo] = [            WorkerInfo(worker_id=i) for i in range(base_workers)        ]        self.max_workers = max_workers        self.base_workers = base_workers        self.queue = []        self.batch_size = 4  # 每批最大请求数        self.cooldown_count = 0        self.expand_threshold = 12  # 队列超过该长度则扩容        self.shrink_threshold = 3   # 队列低于该长度则缩容        self.total_latency = 0.0        self.total_requests = 0     def simulate_load(self, tick: int) -> int:        """模拟随时间变化的请求到达数量"""        peak = int(15 * (0.5 + 0.5 * abs(tick % 20 - 10) / 10)) + random.randint(1, 3)        return peak     def step(self, tick: int) -> dict:        incoming = self.simulate_load(tick)        self.queue.extend([1] * incoming)        served = 0        while self.queue and served < len(self.workers) * self.batch_size:            self.queue.pop(0)            served += 1            self.total_latency += 1.0 + float(self.queue) / 100.0            self.total_requests += 1         self._auto_scale()        if self.cooldown_count > 0:            self.cooldown_count -= 1        return {            "incoming": incoming,            "served": served,            "queue": len(self.queue),            "workers": len(self.workers),        }     def _auto_scale(self) -> None:        """根据队列长度和冷却期决定扩容或缩容"""        if self.cooldown_count > 0:            return        if len(self.queue) > self.expand_threshold and len(self.workers) < self.max_workers:            self.workers.append(WorkerInfo(worker_id=len(self.workers)))            self.cooldown_count = 3        elif len(self.queue) < self.shrink_threshold and len(self.workers) > self.base_workers:            self.workers.pop()            self.cooldown_count = 3     def report(self) -> str:        return (            f"总请求数={self.total_requests}, 平均延迟={self.total_latency / max(self.total_requests, 1):.3f}s, "            f"最终Worker数={len(self.workers)}"        )  def main() -> None:    server = ElasticBatchServer(base_workers=3, max_workers=6)    timeline = []    for tick in range(60):        result = server.step(tick)        timeline.append(result)        print(f"t={tick:02d} 来{result['incoming']:02d}个请求, "              f"处理{result['served']:02d}, 排队{result['queue']:02d}, "              f"Worker数={result['workers']}")    print("\n最终报告: " + server.report())  if __name__ == "__main__":    main()
目的:用 60 个时间步模拟推理服务的请求到达、批处理、排队和自动伸缩过程,直观展示弹性扩缩容的核心逻辑。
  1. self.workers: List[WorkerInfo] = [...]初始创建 3 个 Worker,对应基础计算资源
  2. self.queue.extend([1] * incoming)模拟每秒抵达的请求进入等待队列
  3. while self.queue and served < ...只要队列非空且 Worker 没饱和,就持续取请求执行推理
  4. self.batch_size = 4每个 Worker 每步最多处理 4 个请求,模拟 GPU 批处理
  5. _auto_scale()决策引擎:根据队列长度和冷却期决定扩容或缩容
  6. self.cooldown_count = 3冷却期,防止伸缩太频繁导致系统颠簸
  7. len(self.queue) > self.expand_threshold队列超过 12 触发扩容
  8. self.workers.pop()队列长时间空闲则缩容,回到 3 个基础 Worker

常见误区

误区我原本以为弹性伸缩就像开关灯,按钮一按机器就来了。只要把阈值设对了,流量一涨立刻就能扩容,流量一降也应该立刻缩容。

正解实际上 Worker 冷启动需要拉取镜像、加载模型权重、注册到集群,往往要 30 秒到几分钟。因此伸缩决策必须带“前瞻性”和“冷却期”。如果只看当前瞬间负载,你会发现扩完容流量已经过去了,白花一笔钱;缩完容流量又回来了,引发更严重的排队。就像餐厅盯着门口刚走的客人马上撤厨师,结果下一批进店时没人做菜。

为何常见:大多数人把弹性想象成“云上的开关”,忽略了容器启动和模型加载的真实物理耗时。

误区我把 CPU、GPU、内存、磁盘 IO、队列长度、P95 延迟全部设上阈值,以为监控得越全面,系统伸缩越敏捷。

正解指标过多容易导致“抖动打架”——某个指标想扩容,另一个指标想缩容,决策引擎左右摇摆。更合理的做法是:选择 1-3 个与用户体验强相关的核心指标(如队列长度、端到端延迟),其他指标只作为辅助确认。就像厨师判断“高峰期”看排队人数就够了,不必同时盯着电压、水压和气温。

为何常见:监控文化让人产生“看到的越多越安全”的直觉,却忘了决策系统需要简单可控。

误区我觉得流量一降就立刻缩容是最省钱的,反正再涨还能再扩嘛。于是把缩容阈值设得很高、冷却期设得很短。

正解缩容后重新拉起一个 Worker 的代价远高于继续保留它几分钟。若缩得太激进,系统会进入“扩容-缩容-再扩容”的振荡循环,冷启动的算力开销和镜像拉取流量反而更浪费钱。正确的做法是:缩容阈值要显著低于扩容阈值,且冷却期至少要覆盖一次完整的冷启动时间。

为何常见:省钱心理容易让人低估“重新拉起”的成本,最终为了省小钱花了更大代价。

误区装个 K8s,再配一套 HPA,不就实现弹性伸缩了吗?Anyscale 听起来也不过是个容器编排平台。

正解Anyscale 的核心是 Ray 框架的分布式计算语义,它不只管理“进程跑在哪”,更管理“任务如何并行、状态如何共享、对象如何传递”。K8s 管容器,Ray 管计算。Anyscale 把两者结合起来:用 K8s 管底层节点,用 Ray 管推理任务的弹性调度。这就像:K8s 是物业(提供房间),Ray 是餐厅经理(组织出餐流程)。

为何常见:容器化时代大家习惯用 K8s 解决一切,忽略了 AI 计算对任务编排和状态共享的特殊需求。

面试题

Q1. 在 Ray 框架中,适合用来做有状态推理服务长驻 Worker 的编程原语是?

单选题 · easy

  • A.Task,因为任务执行完就结束,便于回收
  • B.Actor,因为它能长期存活并保存状态← 正确答案
  • C.Object Store,因为它负责数据存储
  • D.Driver,因为它负责提交任务

答案:B。Actor 像“长驻的领班”,可以在内存中长期保存状态,适合推理服务这种需要反复接收请求的场景。Task 是一次性任务,做适合纯计算;Object Store 是数据存储而非执行单元;Driver 是提交任务的入口,不是执行推理的 Worker。

Q2. Anyscale 弹性伸缩决策引擎的典型输入指标包括哪些?

多选题 · mid

  • A.请求队列长度← 正确答案
  • B.GPU 利用率← 正确答案
  • C.镜像仓库中镜像个数
  • D.P95 推理延迟← 正确答案

答案:ABD。决策引擎感知的是“系统当前负荷”:请求队列长度反映积压、GPU 利用率和 P95 延迟反映处理能力。镜像仓库镜像个数属于部署管理内容,不直接反映负载,不会作为伸缩指标。

Q3. 关于 Anyscale 的弹性伸缩冷却期,下列说法正确的是?

单选题 · mid

  • A.冷却期越长越好,能最大程度避免伸缩抖动
  • B.冷却期应结合冷启动时间设置,太短易振荡,太长可能错过扩容窗口← 正确答案
  • C.冷却期只对缩容有效,扩容不需要冷却
  • D.冷却期是 Anyscale 平台固定的 5 分钟,用户不可配置

答案:B。冷却期是给系统“消化上次伸缩结果”的时间,长短应匹配合适的冷启动时间。若太短,系统还没稳定就再次伸缩,容易振荡;若太长,流量突变时扩得慢。扩缩容都需要冷却期,冷却期通常可配置,不是固定的。

Q4. 关于推理服务的滚动发布和蓝绿发布,以下说法正确的有?

多选题 · mid

  • A.滚动发布是逐个替换旧版本 Worker,服务不中断← 正确答案
  • B.蓝绿发布需要同时运行新旧两套环境,成本更高← 正确答案
  • C.蓝绿发布无法实现快速回滚
  • D.滚动发布在所有场景下都优于蓝绿发布

答案:AB。滚动发布像“逐个换岗”,每个 Worker 依次替换,服务不中断;蓝绿发布像“开一家分店”,新旧两套并存、一次切流量,成本更高但回滚更快更安全。C 和 D 说法相反——蓝绿发布恰恰因保留了旧环境而便于快速回滚,两种发布方式各有优劣。

Q5. Anyscale 资源调度中,“将同一用户的多次请求调度到同一个 Worker”属于哪种策略?

单选题 · hard

  • A.负载均衡策略
  • B.亲和性调度策略← 正确答案
  • C.优先级抢占策略
  • D.冷启动预热策略

答案:B。亲和性调度追求“把相关的东西放一起”。同一用户多次访问同一个带缓存状态的 Actor,可以复用 KV Cache 和对话历史,减少重复计算。负载均衡追求“分散”而非“聚集”,优先级抢占处理的是资源竞争,冷启动预热处理的是启动速度问题。

Q6. 在弹性推理场景中,GPU 显存(显存)管理最关注以下哪个矛盾?

单选题 · mid

  • A.显存越大模型越聪明
  • B.批量越大吞吐越高但单请求延迟越长← 正确答案
  • C.显存占用与网络带宽成正比
  • D.批量大小与模型精度互相矛盾

答案:B。批大小提升可以让 GPU 更“满”,吞吐更高;但每个请求要等同批请求一起算完,排队延迟变长。显存与网络带宽无直接关系,模型精度与批大小也不构成直接矛盾。

Q7. 以下哪项不是 Ray 框架的核心组件?

单选题 · easy

  • A.分布式调度器
  • B.分布式对象存储
  • C.Actor 模型
  • D.服务网格 Sidecar← 正确答案

答案:D。服务网格 Sidecar 是微服务通信层的概念,与 Ray 定位不同。Ray 的核心组件是任务调度器、对象存储、Actor 和 Driver,它们共同构成分布式计算引擎的地基。

Q8. 某推理服务当前 Worker 数为 4,请求队列长度为 3,扩容阈值为 10,缩容阈值为 2,冷却期为 30 秒。以下哪个下一步最合理?

单选题 · hard

  • A.立即扩容到 6 个 Worker
  • B.触发缩容,但需等待冷却期结束
  • C.保持当前 Worker 数不变,继续观察← 正确答案
  • D.立即关停所有 Worker 以节省成本

答案:C。队列长度 3 既不高于扩容阈值 10,也不低于缩容阈值 2,系统处于“稳定区”,应保持现状继续观察。立即扩容或关停都不符合当前指标信号,缩容条件(低于 2)尚未满足。

Q9. Anyscale 降低成本控制的主要手段包括哪些?

多选题 · hard

  • A.弹性伸缩按需扩容,低谷期自动缩容← 正确答案
  • B.使用 Spot 实例运行无状态推理 Worker← 正确答案
  • C.把所有冷数据持久化到 GPU 显存中
  • D.非业务时段将开发集群缩容到零← 正确答案

答案:ABD。弹性伸缩解决“高峰不够、低谷浪费”问题;Spot 实例用便宜的价格跑可容忍回收的任务;开发集群缩容到零避免空闲节点烧钱。C 是错误操作——GPU 显存是稀缺高速资源,不是冷数据存储位置,冷数据应放对象存储。

Q10. 当一个推理 Worker 节点发生故障时,Anyscale 调度器的正确行为是?

单选题 · easy

  • A.保持请求继续发给故障节点,等待其恢复
  • B.将请求重新路由到健康节点,并主动拉起新 Worker← 正确答案
  • C.立即降低所有请求的优先级
  • D.将模型权重重新训练一遍

答案:B。调度器的自愈职责是“绕开坏节点、补上缺的位”。它会把请求重新路由到健康 Worker,并自动创建新 Worker 替换故障者。故障恢复不是流量降级或重新训练模型,而是快速转移负载。