SkyPilot Sky Serve 架构与实战:从单一 Endpoint 到跨云多副本弹性推理服务
2026/9/16 14:54:46 网站建设 项目流程

SkyPilot Sky Serve 架构与实战:从单一 Endpoint 到跨云多副本弹性推理服务

【免费下载链接】skypilotThe AI Compute Platform for frontier teams. SkyPilot turns fragmented AI compute into one AI supercomputer, so frontier AI teams build custom intelligence faster.项目地址: https://gitcode.com/GitHub_Trending/sk/skypilot

Sky Serve 是 SkyPilot 内置的推理服务托管库(位于 sky/serve 目录),其目标非常直接:对外暴露一个稳定的 Endpoint,把进入的流量透明地分发到运行在不同资源、不同区域、甚至不同云厂商上的多个服务端点副本上。负载均衡(Load Balancing)、故障转移(Failover)与弹性伸缩(Autoscaling)全部由 Sky Serve 在后台自动处理,用户只需要提交一个声明式的 Service YAML。读完本文,你将掌握 Sky Serve 的四组件架构、Service YAML 的完整配置语义、请求级负载均衡与 QPS 驱动的自动扩缩容原理,并能用sky serve命令一键部署并运维一个多副本推理服务。

设计目标:一个 Endpoint,任意规模的服务集群

在 SkyPilot 的定位中,前端团队的计算往往横跨多个云厂商、多个区域,还可能同时使用按需实例与 Spot 实例。传统做法需要自己搭建网关、写健康检查、处理实例被抢占时的迁移——工作量大且极易出错。Sky Serve 把这些复杂性收敛为一个用户感知不到的"服务层":

暴露一个 Endpoint,将任何进入的流量分发到运行在不同资源、区域和云上的服务端点。

它透明地处理了三大核心问题:负载均衡(把请求均匀或按策略地分到健康副本)、故障转移(副本失败时自动把流量切到其他健康副本并重建新副本)、自动伸缩(根据流量或队列长度动态增减副本数量)。这意味着用户可以把 Spot 实例的抢占、某个区域容量不足、甚至整个云不可用都交给系统兜底,服务对外始终只有一个稳定的 URL。

四组件架构总览

Sky Serve 的核心架构由四个关键组件构成(架构图见 docs/source/images/sky-serve-architecture.png):

  1. Load Balancers(负载均衡器):接收所有进入的请求,按照所选策略把请求代理转发到健康副本。实现位于 sky/serve/load_balancer.py。
  2. Load Balancing Policies(负载均衡策略):定义请求如何在健康副本之间分布,例如轮询、最少负载等。实现位于 sky/serve/load_balancing_policies.py。
  3. Autoscalers(自动伸缩器):根据流量指标(QPS、队列长度等)动态决定副本的扩缩。实现位于 sky/serve/autoscalers.py。
  4. Replica Managers(副本管理器):负责副本的创建、销毁、健康探测与故障恢复。实现位于 sky/serve/replica_managers.py。

四个组件共同运行在一个名为Controller的常驻进程中(由sky serve up自动在云端拉起)。Controller 负责协调一切:它维护服务与副本的元数据数据库(sky/serve/serve_state.py 中的servicesreplicasversion_specs三张表),调度 Replica Manager 执行副本的sky.launch/sky down,并驱动 Autoscaler 周期性产出扩缩容决策。

负载均衡器如何工作:同步 + 代理 + 重试

load_balancer.py 中的SkyServeLoadBalancer是一个基于 FastAPI + Uvicorn 的异步代理进程:

  • LB_CONTROLLER_SYNC_INTERVAL_SECONDS = 20秒与 Controller 同步一次(_sync_with_controller),拉取当前健康副本列表,同时把这一周期内累计的请求时间戳上报给 Controller,供 Autoscaler 计算 QPS(constants.py)。
  • 每个请求进入_proxy_with_retries:先由策略从健康副本中选出一个 URL,再用httpx.AsyncClient以流式方式代理请求(_proxy_request_to),响应通过自定义的_CleanupStreamingResponse边收边转发。
  • 由于支持 Spot 实例上的推理,副本可能在请求处理途中被抢占,因此代理内置了最多LB_MAX_RETRY = 3次重试;无可用副本时返回 503,客户端中途断开返回 499,重试耗尽返回 500(load_balancer.py)。流式响应的默认超时为 120 秒,足够覆盖 Llama2-70B 这类大模型的一次生成。

Service YAML 配置全解析

Sky Serve 的配置入口是 Service YAML,核心是service:段。解析与校验逻辑全部集中在 sky/serve/service_spec.py 的SkyServiceSpec.from_yaml_config。以下以一个真实的 vLLM 服务为例(完整文件见 examples/serve/vllm.yaml):

service: readiness_probe: path: /v1/models initial_delay_seconds: 1200 # vLLM 安装需要 5-10 分钟 replicas: 2 resources: ports: 8081 accelerators: A100:1 setup: | conda activate chatbot ... run: | conda activate chatbot python3 -m fastchat.serve.openai_api_server --host ${HOST_IP} --port 8081

readiness_probe:健康探测配置

readiness_probe既可以是字符串(等价于path),也可以是完整字典(service_spec.py):

配置项含义默认值
path就绪探测的 HTTP 路径,必须以/开头必填
initial_delay_seconds服务启动后此期间内的探测失败被忽略(用于等待模型加载/依赖安装)1200
timeout_seconds单次就绪探测请求的超时时间15
endpoint_probe_interval_seconds探测副本端点的间隔10
post_data以 POST 方式探测时携带的 JSON 请求体None(默认 GET)
headers探测时附加的自定义请求头None
consecutive_failure_threshold_timeout连续失败达到该超时后判定副本失败None

这些默认值均来自 sky/serve/constants.py。initial_delay_seconds默认 1200 秒是因为真实 LLM 推理服务的就绪探测往往要等模型权重加载与依赖安装,过早判定失败会导致副本被误杀。

副本策略:replica_policy / replicas

service:段中,副本数量既可以用简写replicas: 2(等价于固定 2 个副本),也可以用完整的replica_policy:(二者不能同时出现,见 service_spec.py):

service: replica_policy: min_replicas: 1 max_replicas: 4 target_qps_per_replica: 2.0 # 触发自动伸缩的每副本目标 QPS num_overprovision: 0 # 在目标副本数之上额外多起的副本数 upscale_delay_seconds: 300 # 扩容生效前的持续观察时间 downscale_delay_seconds: 1200 # 缩容生效前的持续观察时间 base_ondemand_fallback_replicas: 1 # 常驻的按需实例副本数 dynamic_ondemand_fallback: false # 抢占后是否动态补按需实例

关键校验规则(service_spec.py):

  • 设置target_qps_per_replica必须同时设置max_replicas
  • 未设置target_qps_per_replica时,min_replicasmax_replicas必须相等(固定副本数);
  • max_replicas不得小于min_replicas

load_balancing_policy 与负载均衡策略注册表

load_balancing_policy字段用于选择流量分发策略。load_balancing_policies.py 通过LoadBalancingPolicy.__init_subclass__机制把策略自动注册进全局LB_POLICIES注册表,未知策略名会在 YAML 解析阶段直接报错(service_spec.py)。当前可用的策略:

策略名行为默认
least_load最少负载:实时跟踪每个副本的在途请求数,优先选择负载最小的副本,并轮转打破平局✅ 默认
round_robin轮询:副本列表变化时先随机打乱,避免第一个副本长期承接最多流量
instance_aware_least_load实例感知最少负载:按副本 GPU 型号的target_qps_per_replica归一化负载后再选择

round_robin的实现尤其值得注意:set_ready_replicas在副本集合变化时先random.shuffle再重置指针(load_balancing_policies.py),这是为了防止 Autoscaler 反复扩缩容导致排在最前的副本总是承接最多请求。

实例感知策略:异构 GPU 集群的按能力调度

target_qps_per_replica字典(按 GPU 型号给出不同目标 QPS)时,必须配合load_balancing_policy: instance_aware_least_load使用,否则 YAML 校验失败(service_spec.py):

service: replica_policy: min_replicas: 1 max_replicas: 4 target_qps_per_replica: A100: 2.0 H100: 4.0 load_balancing_policy: instance_aware_least_load

InstanceAwareLeastLoadPolicy会把每个副本的当前负载除以该副本 GPU 型号对应的目标 QPS,得到"归一化负载"后再取最小者(load_balancing_policies.py),并对A100:1这类带数量后缀的型号做基名匹配(A100:1匹配A100)。匹配不到时使用默认值 1.0 作为兜底,避免除零。对应地,Autoscaler 也会切换为InstanceAwareRequestRateAutoscaler(autoscalers.py),扩容时按最快的 GPU 的 QPS 估算所需副本数,缩容时优先回收 QPS 能力最低的副本。

端口与 TLS

  • ports:副本对外暴露的端口,必须处于 1-65535 之间(service_spec.py),同时需要在resources.ports中声明。
  • tls:提供keyfilecertfile后,负载均衡器将升级为 HTTPS 端点(load_balancer.py),证书通过TLSCredential.dump_uvicorn_kwargs()直接注入 Uvicorn。

自动伸缩:QPS 驱动与队列长度驱动

Autoscaler 位于 sky/serve/autoscalers.py,由Autoscaler.from_spec根据配置自动选择实现(autoscalers.py):

  • 配置了poolQueueLengthAutoscaler(队列长度驱动);
  • 配置了use_ondemand_fallbackFallbackRequestRateAutoscaler(QPS 驱动 + Spot/按需兜底);
  • target_qps_per_replica为字典 →InstanceAwareRequestRateAutoscaler
  • 其他 →RequestRateAutoscaler(标准 QPS 驱动)。

决策模型与滞回(Hysteresis)

所有基于 QPS 的 Autoscaler 都继承自_AutoscalerWithHysteresis。核心思想是不因为单次波动就扩缩容,而是要求目标副本数在连续多个决策周期内持续偏离当前值才生效:

  • 决策间隔默认 20 秒;当目标副本数为 0(min_replicas: 0且无流量)时缩短到 5 秒,加快服务拉起(autoscalers.py)。
  • 扩容阈值 =upscale_delay_seconds / 20,默认 300 秒 → 需要连续 15 个周期(约 5 分钟)都要求扩容才真正扩容;缩容阈值默认 1200 秒 → 约 20 分钟。
  • 有一个快速通道:当前目标副本数为 0 时立即采用新目标,避免冷启动延迟(autoscalers.py)。

RequestRateAutoscaler._calculate_target_num_replicas的算法非常直观:用最近 60 秒窗口内的请求时间戳数量除以窗口长度得到每秒请求数,再除以单副本目标 QPS 后向上取整,最后裁剪到[min_replicas, max_replicas]区间(autoscalers.py)。请求时间戳由负载均衡器每 20 秒上报一次,Autoscaler 用bisect维护滑动窗口。

Spot 与按需兜底

FallbackRequestRateAutoscaler在 QPS 伸缩之上叠加了 Spot 策略(autoscalers.py):

  • base_ondemand_fallback_replicas:始终保留 N 个按需实例,提供基本的可用性保证;
  • dynamic_ondemand_fallback:任何 Spot 副本被抢占,就用按需实例动态补位(按num_ready_spot而非num_nonterminal_spot计算缺口,因为被抢占的 Spot 可能永远无法恢复)。

扩容时 Spot 与按需分别通过SPOT_OVERRIDE = {'use_spot': True}ONDEMAND_OVERRIDE = {'use_spot': False}资源覆盖来下发(autoscalers.py),Replica Manager 在launch_cluster中把这些覆盖应用到每个副本的sky.Task资源上(replica_managers.py)。

副本的缩容优先级

需要缩容时,_select_nonterminal_replicas_to_scale_down按四重标准排序(autoscalers.py):

  1. 状态越早期越先缩(PENDING → PROVISIONING → STARTING → NOT_READY → READY,见 serve_state.py);
  2. 版本号小的(旧版本)先缩;
  3. 对 Pool 场景,运行作业数少的空闲 worker 先缩;
  4. 副本 ID 大的(后启动的)先保留,因为可能即将就绪。

副本生命周期与故障恢复

Replica Manager 通过 replica_managers.py 中的launch_clusterterminate_cluster管理每个副本对应的独立 SkyPilot 集群,并为每个副本注入环境变量SKYPILOT_SERVE_REPLICA_ID(值取自 constants.py)。

副本状态机

每个副本在数据库中维护一个ReplicaStatus(serve_state.py):

  • 非终态:PENDING(排队等待启动)→PROVISIONING(VM 创建中)→STARTING(依赖安装/服务启动中)→READY(就绪探测通过)→NOT_READY(曾就绪但探测失败)→SHUTTING_DOWN(下线中);
  • 终态失败:FAILED(用户 run/setup 失败)、FAILED_INITIAL_DELAY(超过初始延迟仍不健康)、FAILED_PROBING(健康检查失败)、FAILED_PROVISION(启动失败)、FAILED_CLEANUP(下线失败)、PREEMPTED(Spot 被抢占/Kubernetes Pod 被驱逐)。

服务的整体状态则汇总为ServiceStatus(如CONTROLLER_INITREADYFAILEDSHUTTING_DOWN等,serve_state.py)。

不可恢复故障的熔断

Autoscaler 在generate_scaling_decisions中会先检查最新版本是否有不可恢复故障(unrecoverable_failure):如果最新版本从未就绪、且副本进入终态失败(如用户代码 bug 导致应用在就绪前崩溃),则立即停止一切扩缩决策,避免无限"终止-重启"循环浪费资源(autoscalers.py)。判断逻辑见 replica_managers.py:只要服务曾经就绪过(first_ready_time >= 0),就认为用户代码无 bug,失败可自动恢复。

优雅下线与排空

缩容/更新下线副本时,terminate_cluster支持replica_drain_delay_seconds(默认 120 秒,见 replica_managers.py),即先让副本进入排空状态、处理完在途请求再真正执行sky downsky down失败会触发最多 3 次重试并标记FAILED_CLEANUP,提示可能存在资源泄漏。

命令行实战:一键部署与运维

安装 SkyPilot 后,所有 Sky Serve 操作都通过sky serve子命令完成。

启动服务

# 部署一个名为 vllm 的服务 sky serve up -n vllm examples/serve/vllm.yaml

启动后,Controller 会自动在云端创建(默认资源配置cpus: 4+, memory: 8+,见 constants.py),并在30001-30020端口范围内为负载均衡器分配端口(constants.py)。部署成功后控制台会打印服务 Endpoint 地址。默认的 Controller 自动停止策略为idle_minutes: 10, down: false(constants.py),可通过~/.sky/config.yaml中的serve.controller.autostop覆盖。

查看状态与测试

# 查看服务与副本状态 sky serve status vllm # 直接打印服务 Endpoint sky serve status --endpoint vllm # 用 curl 直接向 Endpoint 发请求验证 curl -L http://<endpoint>/v1/models

sky serve status会展示服务的ServiceStatus、每个副本的ReplicaStatus(带颜色区分)以及自动伸缩策略摘要(Fixed 2 replicasAutoscaling from 1 to 4 replicas (target QPS per replica: 2.0)等文本由autoscaling_policy_str()生成,见 service_spec.py)。状态输出效果可参考 docs/source/images/sky-serve-status-vllm.png 与 docs/source/images/sky-serve-status-vicuna-ready.png。

查看日志

# 服务端日志(Controller 与负载均衡器) sky serve logs vllm --controller sky serve logs vllm --load-balancer # 按副本查看日志 sky serve logs vllm --replica 1

更新与下线

# 用新 YAML 滚动更新服务(自动执行新一轮副本部署与流量切换) sky serve update vllm examples/serve/vllm.yaml # 下线服务并回收全部副本集群 sky serve down vllm

更新采用版本化机制:每次更新产生一个新version,数据库version_specs表按(service_name, version)保存各版本规格(serve_state.py)。滚动更新期间新旧版本副本并存,Autoscaler 依据active_versions协调下线节奏(autoscalers.py)。

扩展阅读:在仓库中继续深挖

  • 服务规范与全部配置字段:sky/serve/service_spec.py
  • 负载均衡策略注册表与三种策略实现:sky/serve/load_balancing_policies.py
  • 四种 Autoscaler 与滞回机制:sky/serve/autoscalers.py
  • 副本状态机与故障恢复:sky/serve/replica_managers.py、sky/serve/serve_state.py
  • 默认参数(超时、端口、决策间隔):sky/serve/constants.py
  • 更多可直接运行的示例:vLLM 推理 examples/serve/vllm.yaml、Stable Diffusion examples/serve/stable_diffusion_service.yaml、多区域多副本的 Spot 策略示例 examples/serve/spot_policy

从架构上看,Sky Serve 把"多云多副本推理服务的运维"抽象成了三层:负载均衡器只负责流量分发与请求时间戳上报;Autoscaler 只负责根据指标产出扩缩容决策;Replica Manager 只负责把决策落实为一个个真实的 SkyPilot 集群生命周期。理解这条职责链,就能在遇到流量不均、伸缩迟缓或副本反复失败时,快速定位问题出在哪个组件。

【免费下载链接】skypilotThe AI Compute Platform for frontier teams. SkyPilot turns fragmented AI compute into one AI supercomputer, so frontier AI teams build custom intelligence faster.项目地址: https://gitcode.com/GitHub_Trending/sk/skypilot

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询