添加端到端容错功能#
本节将帮助你
为你的 Serve 应用提供额外的容错能力
理解 Serve 的恢复过程
在你的 Serve 应用中模拟系统错误
指南:Serve 应用的端到端容错#
Serve 提供了一些开箱即用的容错功能。获得端到端容错的两个选项如下:
调优这些功能并在KubeRay之上运行 Serve
使用Anyscale 平台,一个托管的 Ray 平台
副本健康检查#
默认情况下,Serve 控制器会定期对每个 Serve 部署副本进行健康检查,并在失败时重启它。
你可以定义自定义的应用级健康检查,并调整其频率和超时。要定义自定义健康检查,请在你的部署类中添加一个名为 check_health
的方法。此方法不应接受任何参数或返回值,如果 Ray Serve 认为副本不健康,则应引发异常。如果健康检查失败,Serve 控制器会记录异常,杀死不健康的副本,并重新启动它们。你还可以使用部署选项来自定义 Serve 运行健康检查的频率以及 Serve 将副本标记为不健康的超时时间。
@serve.deployment(health_check_period_s=10, health_check_timeout_s=30)
class MyDeployment:
def __init__(self, db_addr: str):
self._my_db_connection = connect_to_db(db_addr)
def __call__(self, request):
return self._do_something_cool()
# Called by Serve to check the replica's health.
def check_health(self):
if not self._my_db_connection.is_connected():
# The specific type of exception is not important.
raise RuntimeError("uh-oh, DB connection is broken.")
工作节点恢复#
默认情况下,Serve 可以从某些故障中恢复,例如不健康的 Actor。当 Serve 在Kubernetes 上与 KubeRay 一起运行时,它还可以从某些集群级故障中恢复,例如死亡的工作节点或头节点。
当工作节点发生故障时,在其上运行的 Actor 也会随之失败。Serve 检测到 Actor 已失败,并尝试在剩余的健康节点上重新生成这些 Actor。同时,KubeRay 检测到节点本身已失败,因此尝试在另一个正在运行的节点上重启工作 Pod,并启动一个新的健康节点来替换它。一旦节点启动,如果 Pod 仍在等待中,则可以在该节点上重新启动。类似地,Serve 也可以在该节点上重新生成任何待处理的 Actor。在整个恢复期间,在健康节点上运行的部署副本可以继续提供流量。
头节点恢复:Ray GCS 容错#
在本节中,你将学习如何为 Ray 的全局控制存储 (GCS) 添加容错功能,这使得你的 Serve 应用即使在头节点崩溃时也能继续服务流量。
默认情况下,Ray 头节点是单点故障:如果它崩溃,整个 Ray 集群将崩溃,你必须重新启动它。在 Kubernetes 上运行时,RayService
控制器会健康检查 Ray 集群并在发生故障时重新启动它,但这会引入一些停机时间。
从 Ray 2.0+ 开始,KubeRay 支持全局控制存储 (GCS) 容错,可防止头节点宕机时 Ray 集群崩溃。头节点恢复期间,Serve 应用仍可使用工作节点处理流量,但你无法更新或从 Actor 或工作节点崩溃等其他故障中恢复。GCS 恢复后,集群将恢复正常行为。
你可以通过添加外部 Redis 服务器并按照以下步骤修改 RayService
Kubernetes 对象,在 KubeRay 上启用 GCS 容错
步骤 1:添加外部 Redis 服务器#
GCS 容错需要外部 Redis 数据库。你可以选择自托管 Redis 数据库,或通过第三方供应商使用。使用高可用性 Redis 数据库可获得弹性。
出于开发目的,你也可以在与 Ray 集群相同的 Kubernetes 集群上托管一个小型 Redis 数据库。例如,你可以通过在 Kubernetes YAML 前添加这三个 Redis 对象来添加一个 1 节点 Redis 集群
kind: ConfigMap
apiVersion: v1
metadata:
name: redis-config
labels:
app: redis
data:
redis.conf: |-
port 6379
bind 0.0.0.0
protected-mode no
requirepass 5241590000000000
---
apiVersion: v1
kind: Service
metadata:
name: redis
labels:
app: redis
spec:
type: ClusterIP
ports:
- name: redis
port: 6379
selector:
app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
labels:
app: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:5.0.8
command:
- "sh"
- "-c"
- "redis-server /usr/local/etc/redis/redis.conf"
ports:
- containerPort: 6379
volumeMounts:
- name: config
mountPath: /usr/local/etc/redis/redis.conf
subPath: redis.conf
volumes:
- name: config
configMap:
name: redis-config
---
此配置不适用于生产环境,但它适用于开发和测试。迁移到生产环境时,强烈建议你将此 1 节点 Redis 集群替换为高可用性 Redis 集群。
步骤 2:向 RayService 添加 Redis 信息#
添加 Redis 对象后,你还需要修改 RayService
配置。
首先,你需要更新 RayService
元数据的注解
...
apiVersion: ray.io/v1alpha1
kind: RayService
metadata:
name: rayservice-sample
spec:
...
...
apiVersion: ray.io/v1alpha1
kind: RayService
metadata:
name: rayservice-sample
annotations:
ray.io/ft-enabled: "true"
ray.io/external-storage-namespace: "my-raycluster-storage-namespace"
spec:
...
注解包括
ray.io/ft-enabled
必需:设置为 true 时启用 GCS 容错ray.io/external-storage-namespace
可选:设置外部存储命名空间
接下来,你需要将 RAY_REDIS_ADDRESS
环境变量添加到 headGroupSpec
apiVersion: ray.io/v1alpha1
kind: RayService
metadata:
...
spec:
...
rayClusterConfig:
headGroupSpec:
...
template:
...
spec:
...
env:
...
apiVersion: ray.io/v1alpha1
kind: RayService
metadata:
...
spec:
...
rayClusterConfig:
headGroupSpec:
...
template:
...
spec:
...
env:
...
- name: RAY_REDIS_ADDRESS
value: redis:6379
RAY_REDIS_ADDRESS
的值应该是你的 Redis 数据库的 redis://
地址。它应包含你的 Redis 数据库的主机和端口。一个示例 Redis 地址是 redis://user:secret@localhost:6379/0?foo=bar&qux=baz
。
在上面的示例中,Redis 部署名称 (redis
) 是 Kubernetes 集群内的主机,Redis 端口是 6379
。该示例与上一节的示例配置兼容。
在你应用 Redis 对象以及更新后的 RayService
后,你的 Ray 集群可以在头节点崩溃时恢复,而无需重启所有工作节点!
另请参阅
查看 KubeRay 的GCS 容错指南,了解 Serve 如何利用外部 Redis 集群提供头节点容错的更多信息。
将副本分散到不同节点上#
提高 Serve 应用可用性的一种方法是将部署副本分散到多个节点上,这样即使在一定数量的节点发生故障后,你仍有足够的运行副本可以处理流量。
默认情况下,Serve 会软分散所有部署副本,但这有一些限制
分散是软性的且尽力而为,不保证完全均匀。
Serve 会尽可能将副本分散到现有节点上,而不是启动新节点。例如,如果你有一个足够大的单节点集群,Serve 会假定该单节点有足够的资源,将所有副本调度到该节点上。然而,该节点就成为了单点故障。
你可以使用 max_replicas_per_node
部署选项更改部署的分散行为,该选项硬性限制了给定部署可以在单个节点上运行的副本数量。如果将其设置为 1,则实际上是严格分散部署副本。如果未设置,则没有硬性分散约束,Serve 使用前面段落中提到的默认软分散。max_replicas_per_node
选项是按部署配置的,只影响同一部署内副本的分散。不同部署的副本之间没有分散。
以下代码示例展示了如何设置 max_replicas_per_node
部署选项
import ray
from ray import serve
@serve.deployment(max_replicas_per_node=1)
class Deployment1:
def __call__(self, request):
return "hello"
@serve.deployment(max_replicas_per_node=2)
class Deployment2:
def __call__(self, request):
return "world"
此示例有两个 Serve 部署,其 max_replicas_per_node
不同:Deployment1
在每个节点上最多可有一个副本,Deployment2
在每个节点上最多可有两个副本。如果你调度两个 Deployment1
的副本和两个 Deployment2
的副本,Serve 将运行一个至少有两个节点的集群,每个节点上运行一个 Deployment1
的副本。Deployment2
的两个副本可以在单个节点上运行,也可以跨两个节点运行,因为这两种情况都满足 max_replicas_per_node
约束。
Serve 的恢复过程#
本节解释了 Serve 如何从系统故障中恢复。它使用以下 Serve 应用和配置作为工作示例。
# File name: sleepy_pid.py
from ray import serve
@serve.deployment
class SleepyPid:
def __init__(self):
import time
time.sleep(10)
def __call__(self) -> int:
import os
return os.getpid()
app = SleepyPid.bind()
# File name: config.yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: redis-config
labels:
app: redis
data:
redis.conf: |-
port 6379
bind 0.0.0.0
protected-mode no
requirepass 5241590000000000
---
apiVersion: v1
kind: Service
metadata:
name: redis
labels:
app: redis
spec:
type: ClusterIP
ports:
- name: redis
port: 6379
selector:
app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
labels:
app: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:5.0.8
command:
- "sh"
- "-c"
- "redis-server /usr/local/etc/redis/redis.conf"
ports:
- containerPort: 6379
volumeMounts:
- name: config
mountPath: /usr/local/etc/redis/redis.conf
subPath: redis.conf
volumes:
- name: config
configMap:
name: redis-config
---
apiVersion: ray.io/v1alpha1
kind: RayService
metadata:
name: rayservice-sample
annotations:
ray.io/ft-enabled: "true"
spec:
serviceUnhealthySecondThreshold: 300
deploymentUnhealthySecondThreshold: 300
serveConfig:
importPath: "sleepy_pid:app"
runtimeEnv: |
working_dir: "https://github.com/ray-project/serve_config_examples/archive/42d10bab77741b40d11304ad66d39a4ec2345247.zip"
deployments:
- name: SleepyPid
numReplicas: 6
rayActorOptions:
numCpus: 0
rayClusterConfig:
rayVersion: '2.3.0'
headGroupSpec:
replicas: 1
rayStartParams:
num-cpus: '2'
dashboard-host: '0.0.0.0'
redis-password: "5241590000000000"
template:
spec:
containers:
- name: ray-head
image: rayproject/ray:2.3.0
imagePullPolicy: Always
env:
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: RAY_REDIS_ADDRESS
value: redis:6379
resources:
limits:
cpu: 2
memory: 2Gi
requests:
cpu: 2
memory: 2Gi
ports:
- containerPort: 6379
name: redis
- containerPort: 8265
name: dashboard
- containerPort: 10001
name: client
- containerPort: 8000
name: serve
workerGroupSpecs:
- replicas: 2
minReplicas: 2
maxReplicas: 2
groupName: small-group
rayStartParams:
node-ip-address: $MY_POD_IP
template:
spec:
containers:
- name: machine-learning
image: rayproject/ray:2.3.0
imagePullPolicy: Always
env:
- name: RAY_DISABLE_DOCKER_CPU_WARNING
value: "1"
- name: TYPE
value: "worker"
- name: CPU_REQUEST
valueFrom:
resourceFieldRef:
containerName: machine-learning
resource: requests.cpu
- name: CPU_LIMITS
valueFrom:
resourceFieldRef:
containerName: machine-learning
resource: limits.cpu
- name: MEMORY_LIMITS
valueFrom:
resourceFieldRef:
containerName: machine-learning
resource: limits.memory
- name: MEMORY_REQUESTS
valueFrom:
resourceFieldRef:
containerName: machine-learning
resource: requests.memory
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
ports:
- containerPort: 80
name: client
lifecycle:
preStop:
exec:
command: ["/bin/sh","-c","ray stop"]
resources:
limits:
cpu: "1"
memory: "2Gi"
requests:
cpu: "500m"
memory: "2Gi"
按照KubeRay 快速入门指南进行操作,以
安装
kubectl
和Helm
准备一个 Kubernetes 集群
部署一个 KubeRay operator
$ kubectl apply -f config.yaml
工作节点故障#
你可以在工作示例中模拟工作节点故障。首先,查看你的 Kubernetes 集群中正在运行的节点和 Pod
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-serve-demo-default-pool-ed597cce-nvm2 Ready <none> 3d22h v1.22.12-gke.1200
gke-serve-demo-default-pool-ed597cce-m888 Ready <none> 3d22h v1.22.12-gke.1200
gke-serve-demo-default-pool-ed597cce-pu2q Ready <none> 3d22h v1.22.12-gke.1200
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ervice-sample-raycluster-thwmr-worker-small-group-bdv6q 1/1 Running 0 3m3s 10.68.2.62 gke-serve-demo-default-pool-ed597cce-nvm2 <none> <none>
ervice-sample-raycluster-thwmr-worker-small-group-pztzk 1/1 Running 0 3m3s 10.68.2.61 gke-serve-demo-default-pool-ed597cce-m888 <none> <none>
rayservice-sample-raycluster-thwmr-head-28mdh 1/1 Running 1 (2m55s ago) 3m3s 10.68.0.45 gke-serve-demo-default-pool-ed597cce-pu2q <none> <none>
redis-75c8b8b65d-4qgfz 1/1 Running 0 3m3s 10.68.2.60 gke-serve-demo-default-pool-ed597cce-nvm2 <none> <none>
打开一个单独的终端窗口,并将端口转发到其中一个工作节点
$ kubectl port-forward ervice-sample-raycluster-thwmr-worker-small-group-bdv6q 8000
Forwarding from 127.0.0.1:8000 -> 8000
Forwarding from [::1]:8000 -> 8000
在 port-forward
运行时,你可以在另一个终端窗口中查询应用
$ curl localhost:8000
418
输出是处理请求的部署副本的进程 ID。应用启动了 6 个部署副本,所以如果你多次运行查询,应该会看到不同的进程 ID
$ curl localhost:8000
418
$ curl localhost:8000
256
$ curl localhost:8000
385
现在你可以模拟工作节点故障了。你有两个选项:杀死一个工作 Pod 或杀死一个工作节点。让我们先从工作 Pod 开始。确保杀死你没有进行端口转发的那个 Pod,这样当另一个 Pod 重新启动时,你可以继续查询存活的工作节点。
$ kubectl delete pod ervice-sample-raycluster-thwmr-worker-small-group-pztzk
pod "ervice-sample-raycluster-thwmr-worker-small-group-pztzk" deleted
$ curl localhost:8000
6318
当 Pod 崩溃并恢复时,存活的 Pod 可以继续处理流量!
提示
杀死节点并等待其恢复通常比杀死 Pod 并等待其恢复耗时更长。对于此类调试,在 Pod 级别而非节点级别模拟故障会更快。
你也可以类似地杀死一个工作节点,并看到其他节点可以继续处理流量
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ervice-sample-raycluster-thwmr-worker-small-group-bdv6q 1/1 Running 0 65m 10.68.2.62 gke-serve-demo-default-pool-ed597cce-nvm2 <none> <none>
ervice-sample-raycluster-thwmr-worker-small-group-mznwq 1/1 Running 0 5m46s 10.68.1.3 gke-serve-demo-default-pool-ed597cce-m888 <none> <none>
rayservice-sample-raycluster-thwmr-head-28mdh 1/1 Running 1 (65m ago) 65m 10.68.0.45 gke-serve-demo-default-pool-ed597cce-pu2q <none> <none>
redis-75c8b8b65d-4qgfz 1/1 Running 0 65m 10.68.2.60 gke-serve-demo-default-pool-ed597cce-nvm2 <none> <none>
$ kubectl delete node gke-serve-demo-default-pool-ed597cce-m888
node "gke-serve-demo-default-pool-ed597cce-m888" deleted
$ curl localhost:8000
385
头节点故障#
你可以通过杀死头 Pod 或头节点来模拟头节点故障。首先,查看集群中正在运行的 Pod
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ervice-sample-raycluster-thwmr-worker-small-group-6f2pk 1/1 Running 0 6m59s 10.68.2.64 gke-serve-demo-default-pool-ed597cce-nvm2 <none> <none>
ervice-sample-raycluster-thwmr-worker-small-group-bdv6q 1/1 Running 0 79m 10.68.2.62 gke-serve-demo-default-pool-ed597cce-nvm2 <none> <none>
rayservice-sample-raycluster-thwmr-head-28mdh 1/1 Running 1 (79m ago) 79m 10.68.0.45 gke-serve-demo-default-pool-ed597cce-pu2q <none> <none>
redis-75c8b8b65d-4qgfz 1/1 Running 0 79m 10.68.2.60 gke-serve-demo-default-pool-ed597cce-nvm2 <none> <none>
将端口转发到你的一个工作 Pod。确保此 Pod 与头节点位于不同的节点上,这样你可以在杀死头节点时不会影响工作节点。
$ kubectl port-forward ervice-sample-raycluster-thwmr-worker-small-group-bdv6q
Forwarding from 127.0.0.1:8000 -> 8000
Forwarding from [::1]:8000 -> 8000
在另一个终端中,你可以向 Serve 应用发送请求
$ curl localhost:8000
418
你可以杀死头 Pod 来模拟杀死 Ray 头节点
$ kubectl delete pod rayservice-sample-raycluster-thwmr-head-28mdh
pod "rayservice-sample-raycluster-thwmr-head-28mdh" deleted
$ curl localhost:8000
如果在集群上配置了GCS 容错,则当头 Pod 崩溃并恢复时,你的工作 Pod 可以继续处理流量而无需重启。如果没有 GCS 容错,KubeRay 会在头 Pod 崩溃时重启所有工作 Pod,因此你需要等待工作节点重启和部署重新初始化后才能进行端口转发并发送更多请求。
Serve 控制器故障#
你可以通过手动杀死 Serve Actor 来模拟 Serve 控制器故障。
如果你正在运行 KubeRay,请 exec
进入你的一个 Pod
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
ervice-sample-raycluster-mx5x6-worker-small-group-hfhnw 1/1 Running 0 118m
ervice-sample-raycluster-mx5x6-worker-small-group-nwcpb 1/1 Running 0 118m
rayservice-sample-raycluster-mx5x6-head-bqjhw 1/1 Running 0 118m
redis-75c8b8b65d-4qgfz 1/1 Running 0 3h36m
$ kubectl exec -it rayservice-sample-raycluster-mx5x6-head-bqjhw -- bash
ray@rayservice-sample-raycluster-mx5x6-head-bqjhw:~$
你可以使用Ray State API来检查你的 Serve 应用
$ ray summary actors
======== Actors Summary: 2022-10-04 21:06:33.678706 ========
Stats:
------------------------------------
total_actors: 10
Table (group by class):
------------------------------------
CLASS_NAME STATE_COUNTS
0 ProxyActor ALIVE: 3
1 ServeReplica:SleepyPid ALIVE: 6
2 ServeController ALIVE: 1
$ ray list actors --filter "class_name=ServeController"
======== List: 2022-10-04 21:09:14.915881 ========
Stats:
------------------------------
Total: 1
Table:
------------------------------
ACTOR_ID CLASS_NAME STATE NAME PID
0 70a718c973c2ce9471d318f701000000 ServeController ALIVE SERVE_CONTROLLER_ACTOR 48570
然后你可以通过 Python 解释器杀死 Serve 控制器。请注意,你需要使用 ray list actor
输出中的 NAME
来获取 Serve 控制器的句柄。
$ python
>>> import ray
>>> controller_handle = ray.get_actor("SERVE_CONTROLLER_ACTOR", namespace="serve")
>>> ray.kill(controller_handle, no_restart=True)
>>> exit()
你可以使用 Ray State API 检查控制器的状态
$ ray list actors --filter "class_name=ServeController"
======== List: 2022-10-04 21:36:37.157754 ========
Stats:
------------------------------
Total: 2
Table:
------------------------------
ACTOR_ID CLASS_NAME STATE NAME PID
0 3281133ee86534e3b707190b01000000 ServeController ALIVE SERVE_CONTROLLER_ACTOR 49914
1 70a718c973c2ce9471d318f701000000 ServeController DEAD SERVE_CONTROLLER_ACTOR 48570
在控制器恢复期间,你应该仍然能够查询你的部署
# If you're running KubeRay, you
# can do this from inside the pod:
$ python
>>> import requests
>>> requests.get("http://localhost:8000").json()
347
注意
控制器宕机期间,副本健康检查和部署自动扩缩容将无法工作。控制器恢复后,它们将继续工作。
部署副本故障#
你可以通过手动杀死部署副本来模拟副本故障。如果你正在运行 KubeRay,请确保在运行这些命令之前 exec
进入一个 Ray Pod。
$ ray summary actors
======== Actors Summary: 2022-10-04 21:40:36.454488 ========
Stats:
------------------------------------
total_actors: 11
Table (group by class):
------------------------------------
CLASS_NAME STATE_COUNTS
0 ProxyActor ALIVE: 3
1 ServeController ALIVE: 1
2 ServeReplica:SleepyPid ALIVE: 6
$ ray list actors --filter "class_name=ServeReplica:SleepyPid"
======== List: 2022-10-04 21:41:32.151864 ========
Stats:
------------------------------
Total: 6
Table:
------------------------------
ACTOR_ID CLASS_NAME STATE NAME PID
0 39e08b172e66a5d22b2b4cf401000000 ServeReplica:SleepyPid ALIVE SERVE_REPLICA::SleepyPid#RlRptP 203
1 55d59bcb791a1f9353cd34e301000000 ServeReplica:SleepyPid ALIVE SERVE_REPLICA::SleepyPid#BnoOtj 348
2 8c34e675edf7b6695461d13501000000 ServeReplica:SleepyPid ALIVE SERVE_REPLICA::SleepyPid#SakmRM 283
3 a95405318047c5528b7483e701000000 ServeReplica:SleepyPid ALIVE SERVE_REPLICA::SleepyPid#rUigUh 347
4 c531188fede3ebfc868b73a001000000 ServeReplica:SleepyPid ALIVE SERVE_REPLICA::SleepyPid#gbpoFe 383
5 de8dfa16839443f940fe725f01000000 ServeReplica:SleepyPid ALIVE SERVE_REPLICA::SleepyPid#PHvdJW 176
你可以使用 ray list actor
输出中的 NAME
来获取其中一个副本的句柄
$ python
>>> import ray
>>> replica_handle = ray.get_actor("SERVE_REPLICA::SleepyPid#RlRptP", namespace="serve")
>>> ray.kill(replica_handle, no_restart=True)
>>> exit()
在副本重启期间,其他副本可以继续处理请求。最终副本会重启并继续服务请求
$ python
>>> import requests
>>> requests.get("http://localhost:8000").json()
383
代理故障#
你可以通过手动杀死 ProxyActor
Actor 来模拟代理故障。如果你正在运行 KubeRay,请确保在运行这些命令之前 exec
进入一个 Ray Pod。
$ ray summary actors
======== Actors Summary: 2022-10-04 21:51:55.903800 ========
Stats:
------------------------------------
total_actors: 12
Table (group by class):
------------------------------------
CLASS_NAME STATE_COUNTS
0 ProxyActor ALIVE: 3
1 ServeController ALIVE: 1
2 ServeReplica:SleepyPid ALIVE: 6
$ ray list actors --filter "class_name=ProxyActor"
======== List: 2022-10-04 21:52:39.853758 ========
Stats:
------------------------------
Total: 3
Table:
------------------------------
ACTOR_ID CLASS_NAME STATE NAME PID
0 283fc11beebb6149deb608eb01000000 ProxyActor ALIVE SERVE_CONTROLLER_ACTOR:SERVE_PROXY_ACTOR-91f9a685e662313a0075efcb7fd894249a5bdae7ee88837bea7985a0 101
1 2b010ce28baeff5cb6cb161e01000000 ProxyActor ALIVE SERVE_CONTROLLER_ACTOR:SERVE_PROXY_ACTOR-cc262f3dba544a49ea617d5611789b5613f8fe8c86018ef23c0131eb 133
2 7abce9dd241b089c1172e9ca01000000 ProxyActor ALIVE SERVE_CONTROLLER_ACTOR:SERVE_PROXY_ACTOR-7589773fc62e08c2679847aee9416805bbbf260bee25331fa3389c4f 267
你可以使用 ray list actor
输出中的 NAME
来获取其中一个副本的句柄
$ python
>>> import ray
>>> proxy_handle = ray.get_actor("SERVE_CONTROLLER_ACTOR:SERVE_PROXY_ACTOR-91f9a685e662313a0075efcb7fd894249a5bdae7ee88837bea7985a0", namespace="serve")
>>> ray.kill(proxy_handle, no_restart=False)
>>> exit()
在代理重启期间,其他代理可以继续接收请求。最终代理会重启并继续接收请求。你可以使用 ray list actor
命令查看代理何时重启
$ ray list actors --filter "class_name=ProxyActor"
======== List: 2022-10-04 21:58:41.193966 ========
Stats:
------------------------------
Total: 3
Table:
------------------------------
ACTOR_ID CLASS_NAME STATE NAME PID
0 283fc11beebb6149deb608eb01000000 ProxyActor ALIVE SERVE_CONTROLLER_ACTOR:SERVE_PROXY_ACTOR-91f9a685e662313a0075efcb7fd894249a5bdae7ee88837bea7985a0 57317
1 2b010ce28baeff5cb6cb161e01000000 ProxyActor ALIVE SERVE_CONTROLLER_ACTOR:SERVE_PROXY_ACTOR-cc262f3dba544a49ea617d5611789b5613f8fe8c86018ef23c0131eb 133
2 7abce9dd241b089c1172e9ca01000000 ProxyActor ALIVE SERVE_CONTROLLER_ACTOR:SERVE_PROXY_ACTOR-7589773fc62e08c2679847aee9416805bbbf260bee25331fa3389c4f 267
请注意,第一个 ProxyActor 的 PID 已经改变,表明它已重启。