- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 引言
- Ray Serve 架构与核心概念
- Ray Serve 与 vLLM、TorchServe、Triton 对比
- LLM 模型服务:vLLM + Ray Serve 集成
- 自动扩缩容设置与资源管理
- 多模型服务模式(Model Composition)
- 批量推理优化
- Kubernetes 部署(KubeRay Operator)
- 监控、日志与指标设置
- 故障排查与运维注意事项
- 故障案例与恢复流程
- 生产部署检查清单
- 结语
- 参考资料

引言
在生产环境中服务 ML 模型,与训练是完全不同的工程领域。单一模型的服务用 Flask 或 FastAPI 也能做到,但在需要同时服务多个模型、根据流量自动扩缩容、高效管理 GPU 资源,还要处理故障恢复的生产环境中,专门的服务框架是必不可少的。
Ray Serve 是构建在 Ray 之上的可扩展模型服务库,不绑定特定框架,PyTorch、TensorFlow、Scikit-Learn、XGBoost 等任何模型都能服务。尤其凭借多模型合成、可编程 API、分布式服务这三大核心优势,能用 Python 代码自然地组合出复杂的推理流水线,这一点使它与其他服务框架区分开来。
本文将结合实战代码,涵盖从 Ray Serve 的架构到 LLM 服务、自动扩缩容、Kubernetes 部署、运维故障排查等生产环境所需的全部内容。
Ray Serve 架构与核心概念
架构概览
Ray Serve 是运行在 Ray 集群之上、基于 Actor 的服务框架。整体结构如下。
Client (HTTP/gRPC)
|
v
+-------------------+
| HTTP Proxy | (Ingress、路由、负载均衡)
+-------------------+
|
v
+-------------------+ +-------------------+
| Deployment A | | Deployment B |
| - Replica 1 | | - Replica 1 |
| - Replica 2 | | - Replica 2 |
| - Replica 3 | | - Replica 3 |
+-------------------+ +-------------------+
| |
v v
+-------------------------------------------+
| Ray Cluster (分布式运行时) |
| - Head Node (Serve Controller) |
| - Worker Node 1 (GPU) |
| - Worker Node 2 (GPU) |
+-------------------------------------------+
三个核心概念
Deployment(部署单元):封装模型或业务逻辑的服务基本单位。每个 Deployment 都可以独立扩缩容、更新、回滚。通过 @serve.deployment 装饰器定义。
Replica(副本):Deployment 的运行实例,以 Ray Actor 的形式实现。一个 Deployment 可以拥有多个 Replica 来做水平扩展,每个 Replica 都运行在独立的进程中。GPU 分配、CPU 核心数等资源可以逐个单独设置。
DeploymentHandle(部署句柄):在 Python 代码中调用另一个 Deployment 时使用的异步句柄。可以像普通函数调用一样使用,Ray Serve 会在内部处理负载均衡和序列化。这是多模型合成的核心机制。
import ray
from ray import serve
from transformers import pipeline
@serve.deployment(
num_replicas=2,
ray_actor_options={"num_gpus": 1},
)
class SentimentModel:
def __init__(self):
self.model = pipeline(
"sentiment-analysis",
model="distilbert-base-uncased-finetuned-sst-2-english",
device=0,
)
async def __call__(self, request):
data = await request.json()
result = self.model(data["text"])
return {"prediction": result}
@serve.deployment(num_replicas=1)
class Router:
def __init__(self, sentiment_handle):
self.sentiment = sentiment_handle
async def __call__(self, request):
# 通过 DeploymentHandle 进行内部调用
result = await self.sentiment.remote(request)
return result
sentiment_app = SentimentModel.bind()
router_app = Router.bind(sentiment_app)
serve.run(router_app, route_prefix="/predict")
Ray Serve 与 vLLM、TorchServe、Triton 对比
在选择模型服务框架时,准确理解每种工具的优势与局限非常重要。
| 项目 | Ray Serve | vLLM | TorchServe | Triton Inference Server |
|---|---|---|---|---|
| 主要用途 | 通用模型服务、多模型流水线 | LLM 专用高性能推理 | PyTorch 模型服务 | 通用 GPU 推理服务器 |
| 框架支持 | 全面(PyTorch、TF、ONNX 等) | LLM 专用(HuggingFace 模型) | 仅 PyTorch | TF、PyTorch、ONNX、TensorRT |
| 实现语言 | Python | Python/C++ | Java/Python | C++ |
| 多模型合成 | Python 原生支持 | 不支持 | 有限 | Model Ensemble(基于 DAG) |
| 自动扩缩容 | 内置(以 Replica 为单位) | 依赖外部 | 依赖外部 | 依赖外部 |
| LLM 优化 | 支持 vLLM 集成 | Paged Attention、Continuous Batching | 有限 | TensorRT-LLM 后端 |
| K8s 集成 | KubeRay Operator | 直接部署或 Ray 集成 | KServe 集成 | Triton Operator |
| 批量推理 | @serve.batch 装饰器 | Continuous Batching | Dynamic Batching | Dynamic Batching |
| 流式响应 | 支持 | 支持 | 有限 | 支持 |
| 学习曲线 | 中等 | 低(仅限 LLM) | 中等 | 高 |
选择标准总结:
- 只服务 LLM:单独使用 vLLM,或 Ray Serve + vLLM 集成
- 需要多模型流水线:Ray Serve 是最自然的选择
- 以 PyTorch 模型为主的简单服务:TorchServe
- 需要极致的 GPU 性能:Triton Inference Server
- 需要分布式多节点 GPU 服务:Ray Serve + vLLM,或 Triton + Ray Serve 集成
LLM 模型服务:vLLM + Ray Serve 集成
Ray Serve LLM 是在 Ray Serve 之上运行 vLLM 引擎的框架,既提供 OpenAI 兼容 API,又支持分布式服务与自动扩缩容。自 2025 年 Ray Serve LLM API 正式发布以来,vLLM 集成变得更加简便。
from ray import serve
from ray.serve.llm import LLMServer, LLMConfig, build_openai_app
# 定义 LLM 配置
llm_config = LLMConfig(
model_id="meta-llama/Llama-3.1-8B-Instruct",
engine_kwargs={
"tensor_parallel_size": 2,
"max_model_len": 4096,
"gpu_memory_utilization": 0.85,
"enforce_eager": False,
},
# 自动扩缩容设置
deployment_config={
"autoscaling_config": {
"min_replicas": 1,
"max_replicas": 4,
"target_ongoing_requests": 5,
}
},
accelerator_type="A100",
)
# 构建并部署 OpenAI 兼容应用
app = build_openai_app(llm_config)
serve.run(app, host="0.0.0.0", port=8000)
部署完成后,可以直接用 OpenAI SDK 调用。
# 健康检查
curl http://localhost:8000/v1/models
# 调用 Chat Completions API
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-3.1-8B-Instruct",
"messages": [
{"role": "user", "content": "请说明一下 Ray Serve 的优点。"}
],
"max_tokens": 512,
"temperature": 0.7,
"stream": true
}'
多个 LLM 同时服务
也可以在一个 Ray 集群中同时服务多个 LLM。
from ray.serve.llm import LLMConfig, build_openai_app
# 配置多个模型
configs = [
LLMConfig(
model_id="meta-llama/Llama-3.1-8B-Instruct",
engine_kwargs={
"tensor_parallel_size": 1,
"gpu_memory_utilization": 0.85,
},
accelerator_type="A100",
),
LLMConfig(
model_id="Qwen/Qwen2.5-7B-Instruct",
engine_kwargs={
"tensor_parallel_size": 1,
"gpu_memory_utilization": 0.85,
},
accelerator_type="A100",
),
]
# 将所有模型构建为单个应用
app = build_openai_app(configs)
自动扩缩容设置与资源管理
自动扩缩容基本设置
Ray Serve 的自动扩缩容器在应用层运作,基于进行中的请求数(ongoing requests)来调整 Replica 数量。设置 num_replicas="auto" 即可启用默认的自动扩缩容。
from ray import serve
@serve.deployment(
# 自动扩缩容详细设置
autoscaling_config={
"min_replicas": 1, # 最小 Replica 数
"max_replicas": 10, # 最大 Replica 数(相对峰值预留 20% 余量)
"target_ongoing_requests": 5, # 每个 Replica 的目标进行中请求数
"upscale_delay_s": 30, # 判定扩容前的等待时间
"downscale_delay_s": 300, # 判定缩容前的等待时间(保守)
"upscaling_factor": 1.0, # 扩容比例
"downscaling_factor": 0.5, # 缩容比例
"smoothing_factor": 0.5, # 指标平滑因子
"metrics_interval_s": 10, # 指标采集周期
},
max_ongoing_requests=10, # 每个 Replica 的最大并发处理请求数
ray_actor_options={
"num_gpus": 1,
"num_cpus": 4,
"memory": 16 * 1024 * 1024 * 1024, # 16GB
},
health_check_period_s=10,
health_check_timeout_s=30,
graceful_shutdown_timeout_s=60,
)
class GPUModel:
def __init__(self):
# 加载模型
pass
async def __call__(self, request):
# 推理逻辑
pass
自动扩缩容工作原理
自动扩缩容器的扩缩决策遵循以下公式。
desired_replicas = ceil(total_ongoing_requests / target_ongoing_requests)
举例来说,若 target_ongoing_requests=5,当前总的进行中请求数为 23 个,则需要 ceil(23/5) = 5 个 Replica。这一决策会被 upscale_delay_s 和 downscale_delay_s 延迟执行,以防止扩缩容剧烈波动。
资源管理策略
Zero-replica 缩容(缩容至零):长时间没有流量时,可以把 Replica 缩减到 0 以节省资源。但这会产生冷启动延迟,因此不适合实时服务。
@serve.deployment(
autoscaling_config={
"min_replicas": 0, # 没有流量时缩容至 0
"max_replicas": 5,
},
)
class BatchModel:
pass
Fractional GPU 分配(分数 GPU 分配):以分数为单位分配 GPU,可以在一块 GPU 上服务多个模型。
@serve.deployment(
ray_actor_options={"num_gpus": 0.25} # 仅使用 GPU 的 25%
)
class LightModel:
pass
多模型服务模式(Model Composition)
Ray Serve 最强大的功能之一,就是能用 Python 代码自然地把多个模型和业务逻辑组合起来的模型合成(Model Composition)。通过 DeploymentHandle,Deployment 之间的调用表现得就像普通函数调用一样,而在内部,分布式环境下的负载均衡与序列化会以最优方式完成。
顺序流水线模式
from ray import serve
@serve.deployment(num_replicas=2)
class Preprocessor:
def preprocess(self, text: str) -> dict:
# 文本预处理:分词、归一化
tokens = text.lower().split()
return {"tokens": tokens, "length": len(tokens)}
@serve.deployment(
num_replicas=3,
ray_actor_options={"num_gpus": 1},
)
class Classifier:
def __init__(self):
from transformers import pipeline
self.model = pipeline("text-classification", device=0)
def classify(self, preprocessed: dict) -> dict:
text = " ".join(preprocessed["tokens"])
result = self.model(text)
return {"label": result[0]["label"], "score": result[0]["score"]}
@serve.deployment(num_replicas=1)
class Postprocessor:
def format_response(self, classification: dict) -> dict:
return {
"result": classification["label"],
"confidence": round(classification["score"], 4),
"status": "success",
}
@serve.deployment(num_replicas=1)
class Pipeline:
def __init__(self, preprocessor, classifier, postprocessor):
self.preprocessor = preprocessor
self.classifier = classifier
self.postprocessor = postprocessor
async def __call__(self, request):
data = await request.json()
text = data["text"]
# 依次执行流水线
preprocessed = await self.preprocessor.preprocess.remote(text)
classified = await self.classifier.classify.remote(preprocessed)
response = await self.postprocessor.format_response.remote(classified)
return response
# 构建部署图
preprocessor = Preprocessor.bind()
classifier = Classifier.bind()
postprocessor = Postprocessor.bind()
app = Pipeline.bind(preprocessor, classifier, postprocessor)
Model Multiplexing 模式
这是在一个 Deployment 中动态加载多个模型进行服务的模式。在需要为数百个客户各自服务一个专属模型时很有用。
from ray import serve
@serve.deployment(
num_replicas=2,
max_ongoing_requests=10,
)
class MultiplexedModel:
def __init__(self):
self.models = {}
def _load_model(self, model_id: str):
if model_id not in self.models:
# 动态加载模型(缓存管理)
import joblib
self.models[model_id] = joblib.load(f"/models/{model_id}.pkl")
# 内存管理:以 LRU 缓存最多保留 10 个模型
if len(self.models) > 10:
oldest = next(iter(self.models))
del self.models[oldest]
return self.models[model_id]
async def __call__(self, request):
data = await request.json()
model_id = data["model_id"]
features = data["features"]
model = self._load_model(model_id)
prediction = model.predict([features])
return {"model_id": model_id, "prediction": prediction.tolist()}
批量推理优化
使用 @serve.batch 装饰器,可以自动把多个独立请求打包成一批来处理。这是最大化 GPU 利用率的核心技巧。
import numpy as np
from ray import serve
@serve.deployment(
num_replicas=2,
ray_actor_options={"num_gpus": 1},
)
class BatchClassifier:
def __init__(self):
from transformers import AutoTokenizer, AutoModel
import torch
self.tokenizer = AutoTokenizer.from_pretrained(
"bert-base-uncased"
)
self.model = AutoModel.from_pretrained(
"bert-base-uncased"
).cuda().eval()
@serve.batch(max_batch_size=32, batch_wait_timeout_s=0.1)
async def handle_batch(self, texts: list[str]) -> list[dict]:
import torch
# 批量分词
inputs = self.tokenizer(
texts,
padding=True,
truncation=True,
max_length=512,
return_tensors="pt",
).to("cuda")
# 批量推理
with torch.no_grad():
outputs = self.model(**inputs)
embeddings = outputs.last_hidden_state[:, 0, :].cpu().numpy()
# 将结果拆分为各自的响应
results = []
for i, emb in enumerate(embeddings):
results.append({
"embedding": emb.tolist()[:10], # 仅返回部分
"batch_size": len(texts),
})
return results
async def __call__(self, request):
data = await request.json()
return await self.handle_batch(data["text"])
批量推理调优要点:
max_batch_size:根据 GPU 内存调整。过大会 OOM,过小则 GPU 利用率下降batch_wait_timeout_s:即使批次没有填满,超过这个时间也会开始处理。是延迟与吞吐量之间的权衡- 把
max_ongoing_requests设置得比max_batch_size更大,让批次能够充分形成
Kubernetes 部署(KubeRay Operator)
KubeRay 架构
KubeRay 是用于在 Kubernetes 中管理 Ray 工作负载的 Operator,提供三种 CRD。
- RayCluster:管理 Ray 集群的生命周期
- RayJob:执行一次性批处理作业
- RayService:管理 Ray Serve 应用的服务(支持无中断升级)
RayService 部署 YAML
apiVersion: ray.io/v1
kind: RayService
metadata:
name: llm-serving
namespace: ml-serving
spec:
# Serve 应用配置
serveConfigV2: |
applications:
- name: llm-app
route_prefix: /
import_path: serve_app:app
runtime_env:
working_dir: "https://github.com/my-org/serve-apps/archive/main.zip"
pip:
- transformers>=4.40.0
- torch>=2.2.0
- vllm>=0.6.0
- ray[serve]>=2.40.0
deployments:
- name: LLMDeployment
num_replicas: auto
autoscaling_config:
min_replicas: 1
max_replicas: 4
target_ongoing_requests: 5
upscale_delay_s: 30
downscale_delay_s: 300
max_ongoing_requests: 10
ray_actor_options:
num_gpus: 1
num_cpus: 4
# Ray 集群配置
rayClusterConfig:
rayVersion: '2.40.0'
headGroupSpec:
rayStartParams:
dashboard-host: '0.0.0.0'
num-gpus: '0'
template:
spec:
containers:
- name: ray-head
image: rayproject/ray-ml:2.40.0-py310-gpu
ports:
- containerPort: 6379
name: gcs-server
- containerPort: 8265
name: dashboard
- containerPort: 8000
name: serve
resources:
limits:
cpu: '4'
memory: '16Gi'
requests:
cpu: '2'
memory: '8Gi'
workerGroupSpecs:
- replicas: 2
minReplicas: 1
maxReplicas: 8
groupName: gpu-workers
rayStartParams:
num-gpus: '1'
template:
spec:
containers:
- name: ray-worker
image: rayproject/ray-ml:2.40.0-py310-gpu
resources:
limits:
cpu: '8'
memory: '32Gi'
nvidia.com/gpu: '1'
requests:
cpu: '4'
memory: '16Gi'
nvidia.com/gpu: '1'
tolerations:
- key: 'nvidia.com/gpu'
operator: 'Exists'
effect: 'NoSchedule'
nodeSelector:
cloud.google.com/gke-accelerator: nvidia-tesla-a100
KubeRay 部署命令
# 安装 KubeRay Operator
helm repo add kuberay https://ray-project.github.io/kuberay-helm/
helm repo update
helm install kuberay-operator kuberay/kuberay-operator \
--namespace kuberay-system \
--create-namespace \
--version 1.2.2
# 部署 RayService
kubectl apply -f ray-service-llm.yaml
# 检查状态
kubectl get rayservice llm-serving -n ml-serving
kubectl describe rayservice llm-serving -n ml-serving
# 查看服务日志
kubectl logs -l ray.io/serve=llm-serving -n ml-serving --tail=100
# 通过端口转发进行本地访问
kubectl port-forward svc/llm-serving-serve-svc 8000:8000 -n ml-serving
监控、日志与指标设置
Prometheus 指标采集
Ray Serve 会在每个节点上自动暴露 Prometheus 格式的指标。核心指标如下。
| 指标 | 说明 | 用途 |
|---|---|---|
ray_serve_num_ongoing_requests | 当前进行中的请求数 | 自动扩缩容的判断依据 |
ray_serve_request_latency_ms | 请求延迟直方图 | p50/p95/p99 延迟监控 |
ray_serve_num_replicas | 当前活跃的 Replica 数 | 确认扩缩容状态 |
ray_serve_request_counter | 请求总数(成功/失败) | 错误率告警 |
ray_component_rss_mb | 进程 RSS 内存 | 内存泄漏检测 |
在 Kubernetes 中集成 Prometheus
# 通过 ServiceMonitor 采集 Ray 指标
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: ray-serve-monitor
namespace: ml-serving
spec:
selector:
matchLabels:
ray.io/serve: llm-serving
endpoints:
- port: metrics
interval: 15s
path: /metrics
namespaceSelector:
matchNames:
- ml-serving
日志设置
Ray Serve 的日志默认保存在 /tmp/ray/session_latest/logs/serve/ 目录下。在 Kubernetes 环境中,建议用 Loki + Promtail 组合构建集中式日志管理。
import logging
from ray import serve
logger = logging.getLogger("ray.serve")
@serve.deployment
class MonitoredModel:
def __init__(self):
self.request_count = 0
async def __call__(self, request):
self.request_count += 1
data = await request.json()
logger.info(
f"Request #{self.request_count} received",
extra={
"request_id": request.headers.get("X-Request-ID", "unknown"),
"model": "monitored-model",
},
)
try:
result = self._predict(data)
logger.info(f"Request #{self.request_count} completed successfully")
return result
except Exception as e:
logger.error(
f"Request #{self.request_count} failed: {str(e)}",
exc_info=True,
)
raise
def _predict(self, data):
# 推理逻辑
return {"status": "ok"}
内存分析
当怀疑存在内存泄漏时,可以使用 memray 进行分析。
# 启用内存分析
export RAY_SERVE_ENABLE_MEMORY_PROFILING=1
# 查看分析结果
ls /tmp/ray/session_latest/logs/serve/*.bin
# 自定义延迟直方图分桶
export RAY_SERVE_REQUEST_LATENCY_BUCKETS_MS="5,10,25,50,100,250,500,1000,2500,5000"
故障排查与运维注意事项
常见问题与解决方案
1. OOM(内存溢出)错误引发的连锁故障
这是 24/7 服务环境中最致命的问题。当内存不足时,Ray 会重启 Deployment,但随着重启间隔越来越短,可能会发生大部分请求失败的故障连锁反应(failure spiral)。
# 预防策略:显式声明资源限制
@serve.deployment(
ray_actor_options={
"num_gpus": 1,
"memory": 16 * 1024 * 1024 * 1024, # 显式声明为 16GB
},
# 通过健康检查提早发现异常状态
health_check_period_s=10,
health_check_timeout_s=30,
)
class SafeModel:
def __init__(self):
import torch
# 记录 GPU 内存使用量
if torch.cuda.is_available():
allocated = torch.cuda.memory_allocated() / 1e9
reserved = torch.cuda.memory_reserved() / 1e9
logger.info(
f"GPU memory - allocated: {allocated:.2f}GB, "
f"reserved: {reserved:.2f}GB"
)
2. GPU 内存碎片化
在集成 vLLM 时,默认会预留 GPU 显存的 90%。如果系统级预留内存超过 10%,就需要调低 gpu_memory_utilization 的值。
# 调整 gpu_memory_utilization
llm_config = LLMConfig(
model_id="meta-llama/Llama-3.1-8B-Instruct",
engine_kwargs={
"gpu_memory_utilization": 0.80, # 从 90% 降到 80%
"max_model_len": 2048, # 限制上下文长度
},
)
3. 多节点 GPU 服务时的通信错误
在多节点张量并行处理时,节点间网络配置问题经常发生。
# 检查 NCCL 环境变量
export NCCL_DEBUG=INFO
export NCCL_SOCKET_IFNAME=eth0
export NCCL_IB_DISABLE=1 # 在没有 InfiniBand 的环境中
4. RayService 终止失败状态
当 RayService 陷入终止失败状态(terminal failed state)时,即便更新 spec 也不会触发重试。这种情况下必须删除 RayService 后重新创建。
# 检查失败状态
kubectl get rayservice llm-serving -n ml-serving -o jsonpath='{.status.serviceStatus}'
# 恢复:删除后重新创建
kubectl delete rayservice llm-serving -n ml-serving
kubectl apply -f ray-service-llm.yaml
5. 自动扩缩容器震荡(Oscillation)
当流量剧烈变化时,扩容/缩容可能反复发生,导致性能不稳定。
# 防止震荡的设置
autoscaling_config={
"upscale_delay_s": 60, # 增加扩容等待时间
"downscale_delay_s": 600, # 大幅增加缩容等待时间
"smoothing_factor": 0.3, # 降低对指标变化的敏感度
"downscaling_factor": 0.3, # 每次只缩容 30%
}
故障案例与恢复流程
案例 1:模型更新期间服务中断
情况:更新了 RayService 的 serveConfigV2,但新模型超出了 GPU 内存,导致所有 Replica 陷入 CrashLoopBackOff。
恢复流程:
- 立即回滚到之前的配置:
kubectl apply -f ray-service-llm-previous.yaml - 确认所有 Replica 恢复正常:
kubectl get pods -l ray.io/serve=llm-serving - 在独立的 RayCluster 中对新模型进行内存分析
- 调整
gpu_memory_utilization和max_model_len后重新部署
案例 2:OOM 引发的连锁故障
情况:流量激增导致 Worker 节点内存耗尽。Ray 的内存监控器反复 kill 进程,引发整个服务的故障。
恢复流程:
- 调低
min_replicas,阻断新请求流入 - 通过
kubectl scale或 Cluster Autoscaler 增加 Worker 节点 - 分析内存占用原因:查看
ray_component_rss_mb指标 - 调低
max_ongoing_requests值,减少每个 Replica 的负载 - 恢复正常后逐步恢复流量
案例 3:网络分区导致 Head 节点断连
情况:Head 节点与 Worker 节点之间网络断开,无法访问 GCS(Global Control Store)。所有 Deployment 都无法响应。
恢复流程:
- 检查 Head 节点状态:
kubectl exec -it ray-head-0 -- ray status - 确认网络连接恢复后,等待 Worker 节点自动重新连接(默认超时:300 秒)
- 若重连失败,重启 Worker Pod:
kubectl delete pod <worker-pod> - 若 GCS 数据丢失,需要重新创建整个 RayCluster
生产部署检查清单
这是部署前必须确认的项目清单。
资源设置
- 是否事先对 GPU 内存使用量做过分析
- 是否在
ray_actor_options中明确指定了num_gpus、num_cpus、memory gpu_memory_utilization是否设置得当(推荐:0.80~0.85)- 使用 Fractional GPU 时,是否验证过模型间内存冲突的可能性
自动扩缩容
min_replicas是否按实际最小流量设置max_replicas是否相对峰值流量预留了 20% 的余量downscale_delay_s是否设置得足够保守(推荐至少 300 秒)- 是否测试过 Worker 节点自动扩缩容器(KubeRay/Cluster Autoscaler)与 Serve 自动扩缩容器之间的交互
稳定性
- 是否设置了
health_check_period_s和health_check_timeout_s graceful_shutdown_timeout_s是否大于模型卸载所需时间- 是否已将 OOM 发生时的恢复流程文档化
- 是否为 RayService 终止失败状态准备了应对 runbook
监控
- 是否搭建了 Prometheus + Grafana 仪表盘
- 是否设置了
ray_serve_request_latency_ms的 p99 延迟告警 - 是否设置了
ray_serve_request_counter的错误率告警 - 是否设置了内存使用量(
ray_component_rss_mb)的阈值告警
部署流程
- 是否在预发布(staging)环境中对新模型做过负载测试
- 是否测试过 RayService 的无中断升级(rolling update)
- 是否保留了回滚流程和此前的 YAML 配置
- 多节点部署时是否验证过 NCCL 通信设置
结语
Ray Serve 凭借 Python 原生编程模型、强大的多模型合成、内置自动扩缩容这三大核心优势,成为生产级模型服务平台的有力选项。尤其是通过 vLLM 集成实现的 LLM 服务、通过 KubeRay 实现的 Kubernetes 原生部署,让 MLOps 团队能够高效运维复杂的推理基础设施。
不过在运维中,也存在 OOM 连锁故障、自动扩缩容器震荡、RayService 终止失败状态等特有的陷阱,因此提前进行充分的负载测试与故障模拟、并将恢复流程清晰地文档化是必不可少的。希望本文介绍的配置与模式,能成为生产环境 Ray Serve 运维的一个起点。
参考资料
- Ray Serve 官方文档 - 架构、API 参考、教程
- Ray Serve LLM 部署指南 - vLLM 集成、OpenAI 兼容 API 设置
- Ray Serve 自动扩缩容指南 - 自动扩缩容设置与调优
- KubeRay RayService 部署 - Kubernetes 部署 YAML、无中断升级
- Ray Serve 监控 - Prometheus 指标、Grafana 仪表盘
- RayService 故障排查 - 故障诊断与恢复
- KubeRay GitHub 仓库 - Helm chart、CRD 定义、示例
- Ray Serve 多节点 GPU 故障排查 - NCCL 设置、网络诊断