拓冰建站拓冰建站
首页 / 资讯中心 / 正文

RayService 高可用实战指南:基于 GCS 容错实现 Ray Serve 在 Head Pod 故障下的持续服务

RayService 高可用实战指南基于 GCS 容错实现 Ray Serve 在 Head Pod 故障下的持续服务【免费下载链接】rayRay is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.项目地址: https://gitcode.com/gh_mirrors/ra/ray导读在 Kubernetes 上运行 RayService 时Ray head Pod 是集群的大脑一旦它发生故障默认情况下整个 Ray 集群会随之宕机依赖 Ray Serve 的在线服务也会中断。本文以 doc/source/cluster/kubernetes/user-guides/rayservice-high-availability.md 为骨架完整讲解如何通过GCS fault toleranceGCS 容错为 RayService 提供高可用能力在 head Pod 被删除或崩溃后Ray Serve 应用仍能持续对外提供服务。读完本文你将掌握基于 Kind KubeRay 搭建高可用 RayService 的完整步骤、Serve 副本调度策略num_replicas、max_replicas_per_node、num-cpus的作用以及 GCS 容错与零宕机升级zero-downtime upgrade协同工作的底层原理。背景为什么需要高可用GCS 的默认行为是命运共享GCSGlobal Control Service是 Ray 集群中负责管理集群级元数据的核心服务。默认情况下GCS 把所有数据保存在内存中不具备容错能力——一旦 GCS 进程故障整个 Ray 集群都可能失败。在未启用 GCS 容错时Ray 集群、GCS 进程与 Ray head Pod 三者是命运共享fate-sharing关系如果 GCS 进程死亡head Pod 会在RAY_gcs_rpc_server_reconnect_timeout_s秒后随之死亡如果 head Pod 按照 Pod 的restartPolicy重启worker Pod 会尝试重连新的 head Pod但由于集群状态已经丢失新 head 会把这些 worker Pod 视为未知 worker并终止它们。这种模式对大多数 Ray 应用是够用的但对于把高可用视为核心诉求的 Ray Serve 场景则并不理想。因此官方推荐在 RayService 自定义资源上启用 GCS 容错以保证服务连续性。高可用的核心原理把元数据备份到 Redis要让 GCS 具备容错能力必须有一个高可用的 Redis 实例作为外部存储当 GCS 重启时它会从 Redis 中取回全部集群元数据恢复原有的集群视图节点列表、Actor 信息、任务状态等。这相当于给大脑做了一个持久化备份使 head Pod 故障不再是集群的单点故障。如果希望 Redis 本身也具备故障转移能力可以参考 Tuning Redis for a Persistent Fault Tolerant GCS如果不想运行 Redis还可以使用 alpha 阶段的嵌入式 RocksDB 后端。前提条件使用KubeRay 1.3.0 或更高版本gcsFaultToleranceOptions字段在 KubeRay 1.3.0 引入在 RayService 中启用 GCS fault tolerance一个可用的 Redis单分片 Redis Cluster 或 Redis Sentinel一个或多个副本本机已安装kind、kubectl与helm。快速开始搭建一个高可用的 RayService下面的快速开始共 8 步完整演示从零搭建head Pod 故障后服务不中断的验证环境。Step 1用 Kind 创建 Kubernetes 集群kind create cluster --imagekindest/node:v1.26.0Step 2安装 KubeRay operatorKubeRay operator 负责监听 RayService / RayCluster 等自定义资源并协调底层 Pod、Service。推荐用 Helm 安装到独立的ray-system命名空间helm repo add kuberay https://ray-project.github.io/kuberay-helm/ helm repo update kubectl create namespace ray-system helm install kuberay-operator kuberay/kuberay-operator --version 1.7.0 -n ray-system安装完成后可用kubectl get pods -n ray-system确认 operator 处于Running状态。更完整的安装说明见 KubeRay Operator Installation。Step 3安装带 GCS 容错的 RayServicekubectl apply -f https://raw.githubusercontent.com/ray-project/kuberay/master/ray-operator/config/samples/ray-service.high-availability.yaml这份ray-service.high-availability.yaml样例同时定义了多个 Kubernetes 对象各司其职对象作用Redis提供外部存储是 GCS 容错的前提。详见 KubeRay 中的 GCS fault toleranceRayService自定义资源主体包含一个3 节点1 head 2 worker的 RayCluster和一组简单的 Ray Serve 应用fruit stand 应用ray-podPod独立的客户端 Pod用于向 RayService 持续发送请求Step 4验证 Kubernetes Serve 服务先确认 RayService 已经进入可服务状态# Step 4.1等待 RayService 准备好对外提供服务。 kubectl describe rayservices.ray.io rayservice-ha# [示例输出] # Conditions: # Last Transition Time: 2025-02-13T21:36:18Z # Message: Number of serve endpoints is greater than 0 # Observed Generation: 1 # Reason: NonZeroServeEndpoints # Status: True # Type: Ready注意Type: Ready且Status: TrueReason为NonZeroServeEndpoints表示 Serve 端点数量大于 0RayService 已就绪。这与 RayService 部署指南 中描述的就绪条件一致。再检查 Serve 服务rayservice-ha-serve-svc的端点# Step 4.2rayservice-ha-serve-svc 应该有 3 个端点包含 Ray head 和两个 Ray worker。 kubectl describe svc rayservice-ha-serve-svc# [示例输出] # Endpoints: 10.244.0.29:8000,10.244.0.30:8000,10.244.0.32:8000Step 5验证 Serve 应用与副本调度在ray-service.high-availability.yaml中serveConfigV2为每个 Ray Serve deployment 指定了num_replicas: 2和max_replicas_per_node: 1同时 YAML 通过rayStartParams把 head Pod 的num-cpus设置为0确保系统不会把任何 Ray Serve 副本调度到 head Pod 上。由此产生的结果是每个 Ray Serve deployment 共有 2 个副本每个 Ray 节点最多承载这 2 个副本中的 1 个Ray Serve 副本不会调度到 head Pod 上因此每个 worker 节点上恰好有每个 Ray Serve deployment 的 1 个副本。关于 HTTPProxyActorRay head 无论是否承载 Serve 副本永远有一个 HTTPProxyActor而 worker 节点只有在承载 Serve 副本时才拥有 HTTPProxyActor。这就是上一步中rayservice-ha-serve-svc恰好有 3 个端点1 个 head 2 个 worker的原因——它是 Serve 流量入口流量经由 head 上的代理分发到各 worker 上的副本。可以通过 Ray Dashboard 直观验证上述调度结果# 端口转发 Ray Dashboard。 kubectl port-forward svc/rayservice-ha-head-svc 8265:8265 # 在浏览器中访问 ${YOUR_IP}:8265例如 127.0.0.1:8265 # 检查 # (1) head 与 worker 节点都拥有 HTTPProxyActor。 # (2) 只有 worker 节点拥有 Ray Serve 副本。 # (3) 每个 worker 节点上有且仅有每个 Ray Serve deployment 的 1 个副本。Step 6向 RayService 发送请求# 登录到独立的客户端 Pod。 kubectl exec -it ray-pod -- bash # 向 RayService 发送请求。 python3 samples/query.py # 该脚本连续向 RayService 发送相同请求确保任意时刻最多只有一个 in-flight 请求。 # 请求等价于 curl -X POST -H Content-Type: application/json localhost:8000/fruit/ -d [PEAR, 12]。# [示例输出] # req_index : 2197, num_fail: 0 # response: 12 # req_index : 2198, num_fail: 0 # response: 12 # req_index : 2199, num_fail: 0num_fail: 0说明请求全部成功Serve 应用工作正常。Step 7删除 Ray head Pod验证服务不中断这是整个指南的核心验证环节# Step 7.1删除 Ray head Pod。 export HEAD_POD$(kubectl get pods --selectorray.io/node-typehead -o custom-columnsPOD:metadata.name --no-headers) kubectl delete pod $HEAD_POD在本例中query.py保证任意时刻最多只有一个 in-flight 请求且 head Pod 上没有任何 Ray Serve 副本。因此请求只有在正处于 head Pod 上的 HTTPProxyActor 中这一瞬间才可能失败——而在 head Pod 删除与恢复期间这种失败极难发生。你可以在 Ray 脚本中实现重试逻辑来处理这些偶发失败。# [预期输出]num_fail 极大概率是 0。 req_index : 32503, num_fail: 0 response: 12 req_index : 32504, num_fail: 0 response: 12head Pod 被删除后KubeRay 会创建一个新的 head Pod新 head 从 Redis 中恢复集群元数据worker Pod 不会被当作未知 worker终止Ray Serve 应用状态得以保留服务持续可用。Step 8清理环境kind delete clusterGCS 容错与零宕机升级协同工作的关键RayService 的零宕机升级zero-downtime upgrade机制是当你修改spec.rayClusterConfig触发升级时RayService 会临时创建一个新的 RayCluster等待新集群 ready 后通过更新 head service 的 selector 把流量切换到新集群最后删除旧集群详见 RayService 部署指南 的 Step 8。GCS 容错与零宕机升级可以无额外配置地协同工作但有一个关键约束不要设置gcsFaultToleranceOptions.externalStorageNamespace。高可用样例ray-service.high-availability.yaml正是让它保持未设置状态。单一默认值产生两种正确行为当externalStorageNamespace未设置时KubeRay 会用metadata.uidKubernetes 分配给 RayCluster 的唯一标识符推导 Redis 中的存储命名空间。这个默认值同时保证了两个行为同一个 RayCluster 内head Pod 重启或漂移到其他节点时metadata.uid不变因此新 head 能沿用同一个命名空间从 Redis 恢复集群元数据——这正是 Step 7 演示的恢复过程跨零宕机升级KubeRay 会创建第二个 RayClusterKubernetes 会给它分配不同的metadata.uid因此新集群拥有独立的命名空间无法读取旧集群的元数据operator 会等待新集群真正 ready 后才切换流量。为什么手动固定externalStorageNamespace是危险的如果自行设置externalStorageNamespace上述两个行为都会被替换为一个固定值。一个被钉死的命名空间只在一种场景下有用删除 RayCluster 后重建并希望新集群继承之前的元数据因为重建资源会分配新的 UID只有固定命名空间才能跨资源携带元数据。但在RayService 升级期间新旧两个集群是重叠共存的共享命名空间会把旧集群的 Serve 元数据暴露给新 headoperator 会把这些应用当作新集群自己的应用从而可能在新集群真正 ready 之前就切换流量导致服务中断。这个问题被记录在 RayService 故障排查指南 的 Issue 10Upgrade RayService with GCS fault tolerance enabled without downtime中其根因与早期ray.io/external-storage-namespace注解导致的同名问题kuberay#1296、kuberay#1297一脉相承推荐的解决方案就是移除固定命名空间让 KubeRay 自动为每个 RayCluster 生成独立的 UID 命名空间。升级与 head Pod 恢复的另一个区别零宕机升级还会替换 worker Pod因为 KubeRay 创建的是包含新 head 和新 worker 的全新 RayCluster而 head Pod 恢复如 Step 7保留现有的 worker Pod只是换了一个 head。两种场景下 Serve 应用都通过 Redis 中的元数据无缝衔接。关键配置与运行机制深入gcsFaultToleranceOptions配置项启用 GCS 容错需要在 RayCluster / RayService 中配置gcsFaultToleranceOptions字段要求 KubeRay 1.3.0。以下配置来自 KubeRay 中的 GCS fault tolerance1. 启用 GCS 容错——只需在 spec 中加入该字段kind: RayCluster metadata: spec: gcsFaultToleranceOptions: # - 添加此字段以启用 GCS 容错。2. 连接外部 Redis——redisAddress指定 Redis 服务地址通常用 Kubernetes ClusterIP 服务名redisPassword指定密码从 Kubernetes Secret 读取kind: RayCluster metadata: spec: gcsFaultToleranceOptions: redisAddress: redis:6379 # - 填写 Redis 地址。 redisPassword: # - 从 Kubernetes secret 加载 Redis 密码。 valueFrom: secretKeyRef: name: redis-password-secret key: password3. 外部存储命名空间可选默认不建议设置——KubeRay 用该值设置 head Pod 的环境变量RAY_external_storage_namespacekind: RayCluster metadata: spec: gcsFaultToleranceOptions: externalStorageNamespace: my-raycluster-storage # - 指定存储命名空间大多数情况下无需设置externalStorageNamespaceKubeRay 会自动将其设为 RayCluster 的 UID。只有完全理解 GCS 容错与 RayService 的行为后才建议修改以避免上文所述的 Issue 10 问题。验证元数据确实写入了 Redis可以参考 GCS 容错文档中的验证手段确认 head Pod 的环境变量与 Redis 中的数据# 查看 RayCluster 的 UID。 kubectl get rayclusters.ray.io raycluster-external-redis -ojsonpath{.metadata.uid} # [示例输出]864b004c-6305-42e3-ac46-adfa8eb6f752 # 查看 head Pod 的环境变量 RAY_external_storage_namespace其值等于上面的 UID。 kubectl get pods $HEAD_POD -ojsonpath{.spec.containers[0].env} | jq # 进入 Redis Pod用 redis-cli 查看 key示例密码 5241590000000000 定义在 redis-config ConfigMap 中。 export REDIS_POD$(kubectl get pods --selectorappredis -o custom-columnsPOD:metadata.name --no-headers) kubectl exec -it $REDIS_POD -- env REDISCLI_AUTH5241590000000000 redis-cli KEYS * # [示例输出] # 1) RAY864b004c-6305-42e3-ac46-adfa8eb6f752INTERNAL_CONFIG # 2) RAY864b004c-6305-42e3-ac46-adfa8eb6f752KV # 3) RAY864b004c-6305-42e3-ac46-adfa8eb6f752NODE注意Redis 中的 key 结构在 Ray 2.38.0 发生了变化——此前使用单个 HASH 表现在使用带公共前缀的多个 HASH 表前缀即RAYuid。如果你的 Ray 版本早于 2.38.0看到的将是单个以 UID 命名的 HASH key。重连超时环境变量head 与 worker 的差异GCS 容错场景下KubeRay 会自动注入RAY_gcs_rpc_server_reconnect_timeout_s环境变量worker Pod被注入值600head Pod不注入使用默认值60。worker 的超时值必须大于head 的超时值这样 head Pod 从故障中重启完成之前worker Pod 不会先行终止。这也是 head 删除后集群能整体恢复的关键细节之一。小结通过本文的 8 步快速开始你已经可以亲手搭建一个具备 GCS 容错的高可用 RayService并验证删除 head Pod 后服务零中断这一核心能力。需要记住的要点启用 GCS 容错需要KubeRay 1.3.0和高可用 Redis不要设置gcsFaultToleranceOptions.externalStorageNamespace让 KubeRay 用 RayCluster 的metadata.uid自动派生存储命名空间这是head 恢复与零宕机升级两种场景同时正确工作的前提通过num_replicas、max_replicas_per_node与rayStartParams的num-cpus: 0组合可以把 Serve 副本均匀分布到 worker 节点上并让 head Pod 只承担 HTTPProxyActor 的代理职责从而把 head 故障的影响面降到最低。延伸阅读Deploy Ray Serve ApplicationsRayService 部署指南GCS fault tolerance in KubeRayGCS 容错详解与配置KubeRay Operator Installationoperator 安装RayService 故障排查指南含 Issue 10【免费下载链接】rayRay is an AI compute engine. Ray consists of a core distributed runtime and a set of AI Libraries for accelerating ML workloads.项目地址: https://gitcode.com/gh_mirrors/ra/ray创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门