配置 Ray#
注意
有关运行 Java 应用程序,请参阅Java 应用程序。
本文档讨论了通过 Python API 和命令行配置 Ray 的各种方法。有关配置的完整概述,请参阅ray.init文档。
重要提示
对于多节点设置,您必须先在命令行上运行ray start以在机器上启动 Ray 集群服务,然后才能在 Python 中运行ray.init以连接到集群服务。在单台机器上,您可以运行ray.init()而无需ray start,它会同时启动 Ray 集群服务并连接到它们。
集群资源#
Ray 默认会检测可用资源。
import ray
# This automatically detects available resources in the single machine.
ray.init()
如果未运行集群模式,您可以通过 ray.init 指定集群资源覆盖,如下所示。
# If not connecting to an existing cluster, you can specify resources overrides:
ray.init(num_cpus=8, num_gpus=1)
# Specifying custom resources
ray.init(num_gpus=1, resources={'Resource1': 4, 'Resource2': 16})
从命令行启动 Ray 时,将 --num-cpus 和 --num-gpus 标志传递给 ray start。您还可以指定自定义资源。
# To start a head node.
$ ray start --head --num-cpus=<NUM_CPUS> --num-gpus=<NUM_GPUS>
# To start a non-head node.
$ ray start --address=<address> --num-cpus=<NUM_CPUS> --num-gpus=<NUM_GPUS>
# Specifying custom resources
ray start [--head] --num-cpus=<NUM_CPUS> --resources='{"Resource1": 4, "Resource2": 16}'
如果使用命令行,请按以下方式连接到 Ray 集群
# Connect to ray. Notice if connected to existing cluster, you don't specify resources.
ray.init(address=<address>)
日志和调试#
每个 Ray 会话都有一个唯一的名称。默认情况下,名称为 session_{timestamp}_{pid}。timestamp 的格式为 %Y-%m-%d_%H-%M-%S_%f(有关详细信息,请参阅Python 时间格式);pid 属于启动进程(调用 ray.init() 的进程或在 ray start 中由 shell 执行的 Ray 进程)。
对于每个会话,Ray 会将其所有临时文件放在*会话目录*下。*会话目录*是*根临时路径*(默认情况下为 /tmp/ray)的一个子目录,因此默认的会话目录是 /tmp/ray/{ray_session_name}。您可以按名称对其进行排序以找到最新的会话。
通过将 --temp-dir={your temp path} 传递给 ray start 来更改*根临时目录*。
目前没有一种稳定可靠的方法可以在调用 ray.init() 时更改根临时目录,但如果需要,您可以向 ray.init() 提供 _temp_dir 参数。
有关详细信息,请参阅日志目录结构。
端口配置#
Ray 需要在其集群的节点之间进行双向通信。每个节点都打开特定的端口以接收传入的网络请求。
所有节点#
--node-manager-port:用于节点管理器的 Raylet 端口。默认值:随机值。--object-manager-port:用于对象管理器的 Raylet 端口。默认值:随机值。--runtime-env-agent-port:用于运行时环境代理的 Raylet 端口。默认值:随机值。
节点管理器和对象管理器作为单独的进程运行,它们有自己的端口用于通信。
以下选项指定了仪表板代理进程使用的端口。
--dashboard-agent-grpc-port:用于收听 grpc 的端口。默认值:随机值。--dashboard-agent-listen-port:用于收听 http 的端口。默认值:52365。--metrics-export-port:用于公开 Ray 指标的端口。默认值:随机值。
以下选项指定了跨机器的工作进程使用的端口范围。范围内的所有端口都应打开。
--min-worker-port:工作进程绑定到的最小端口号。默认值:10002。--max-worker-port:工作进程绑定的最大端口号。默认值:19999。
端口号是 Ray 在单个节点上区分来自多个工作进程的输入和输出的方式。每个工作进程在一个端口号上接收输入并输出。因此,默认情况下,每个节点最多有 10,000 个工作进程,无论 CPU 数量如何。
通常,您应该为 Ray 提供一个宽泛的可能的工作进程端口范围,以防其中任何端口恰好被您机器上的其他程序使用。然而,在调试时,显式指定一个短列表的工作进程端口(例如 --worker-port-list=10000,10001,10002,10003,10004)会很有用。请注意,这种做法会像指定一个狭窄的范围一样限制工作进程的数量。
头节点#
除了前一节指定的端口外,头节点还需要打开几个额外的端口。
--port:Ray GCS 服务器的端口。头节点启动一个监听此端口的 GCS 服务器。默认值:6379。--ray-client-server-port:Ray Client Server 的监听端口。默认值:10001。--redis-shard-ports:非主 Redis 分片的端口的逗号分隔列表。默认值:随机值。--dashboard-grpc-port:(已弃用)不再使用。仅为向后兼容而保留。如果
--include-dashboard为 true(默认值),则头节点必须打开--dashboard-port。默认值:8265。
如果 --include-dashboard 为 true 但头节点上未打开 --dashboard-port,则您将无法访问仪表板,并且会反复收到
WARNING worker.py:1114 -- The agent on node <hostname of node that tried to run a task> failed with the following error:
Traceback (most recent call last):
File "/usr/local/lib/python3.8/dist-packages/grpc/aio/_call.py", line 285, in __await__
raise _create_rpc_error(self._cython_call._initial_metadata,
grpc.aio._call.AioRpcError: <AioRpcError of RPC that terminated with:
status = StatusCode.UNAVAILABLE
details = "failed to connect to all addresses"
debug_error_string = "{"description":"Failed to pick subchannel","file":"src/core/ext/filters/client_channel/client_channel.cc","file_line":4165,"referenced_errors":[{"description":"failed to connect to all addresses","file":"src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc","file_line":397,"grpc_status":14}]}"
如果您看到该错误,请使用 nc、nmap 或您的浏览器检查 --dashboard-port 是否可访问。
$ nmap -sV --reason -p 8265 $HEAD_ADDRESS
Nmap scan report for compute04.berkeley.edu (123.456.78.910)
Host is up, received reset ttl 60 (0.00065s latency).
rDNS record for 123.456.78.910: compute04.berkeley.edu
PORT STATE SERVICE REASON VERSION
8265/tcp open http syn-ack ttl 60 aiohttp 3.7.2 (Python 3.8)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
请注意,仪表板作为单独的子进程运行,该子进程可能会在后台不可见地崩溃,因此即使您之前检查了端口 8265,该端口现在也可能已关闭(原因很简单,即不再有服务在该端口上运行)。这也意味着,如果您停止 Ray(ray stop)然后重新启动(ray start),并且端口不可达,则由于仪表板重新启动,端口可能会再次变得可达。
如果您不想要仪表板,请将 --include-dashboard=false 设置为 false。
TLS 身份验证#
您可以配置 Ray 在其 gRPC 通道上使用 TLS。这意味着连接到 Ray 头需要一套适当的凭据,并且在各种进程(客户端、头、工作进程)之间交换的数据也会被加密。
TLS 使用私钥和公钥进行加密和解密。所有者将私钥保密,TLS 将公钥与另一方共享。这种模式确保只有预期的接收者才能读取消息。
证书颁发机构 (CA) 是一个受信任的第三方,它认证公钥所有者的身份。CA 颁发的数字证书包含公钥本身、公钥所有者的身份以及证书的到期日期。请注意,如果公钥所有者不想从 CA 获取数字证书,他们可以使用 OpenSSL 等工具生成自签名证书。
要获取数字证书,公钥的所有者必须生成证书签名请求 (CSR)。CSR 包含有关公钥所有者和公钥本身的信息。Ray 需要额外的步骤才能实现成功的 TLS 加密。
以下是使用自签名证书为静态 Kubernetes Ray 集群添加 TLS 身份验证的分步指南。
步骤 1:为 CA 生成私钥和自签名证书#
openssl req -x509 \
-sha256 -days 3650 \
-nodes \
-newkey rsa:2048 \
-subj "/CN=*.ray.io/C=US/L=San Francisco" \
-keyout ca.key -out ca.crt
使用以下命令对私钥文件和自签名证书文件进行编码,然后将编码后的字符串粘贴到 secret.yaml 中。
cat ca.key | base64
cat ca.crt | base64
# 或者,此命令会自动编码并为 CA 密钥对创建 secret。.. code-block:: bash
kubectl create secret generic ca-tls –from-file=ca.crt=<path-to-ca.crt> –from-file=ca.key=<path-to-ca.key>
步骤 2:为 Ray 头和工作进程生成单独的私钥和自签名证书#
该YAML 文件包含一个名为 tls 的 ConfigMap,其中包含两个 shell 脚本:gencert_head.sh 和 gencert_worker.sh。这些脚本在每个部署的 initContainer 中为头 Pod 和工作进程 Pod 生成私钥和自签名证书文件(tls.key 和 tls.crt)。通过使用 initContainer,我们可以动态地将 POD_IP 检索到 [alt_names] 部分。
这些脚本执行以下步骤:首先,它生成一个 2048 位 RSA 私钥并将该密钥保存为 /etc/ray/tls/tls.key。然后,使用 tls.key 文件和 csr.conf 配置文件生成证书签名请求 (CSR)。最后,使用证书颁发机构 (CA) 的密钥对(ca.key and ca.crt)和 CSR(ca.csr)创建自签名证书(tls.crt)。
步骤 3:为 Ray 头和工作进程设置环境变量以启用 TLS#
通过设置环境变量来启用 TLS。
RAY_USE_TLS:1 或 0,用于使用/不使用 TLS。如果设置为 1,则必须设置以下环境变量。默认值:0。RAY_TLS_SERVER_CERT:指向 Ray 向其他端点呈现以实现双向身份验证的*证书文件*(tls.crt)的位置。RAY_TLS_SERVER_KEY:指向*私钥文件*(tls.key)的位置,这是向其他端点证明您是给定证书的授权用户的加密手段。RAY_TLS_CA_CERT:指向*CA 证书文件*(ca.crt)的位置,它允许 TLS 决定端点的证书是否由正确的主管签名。
步骤 4:验证 TLS 身份验证#
# Log in to the worker Pod
kubectl exec -it ${WORKER_POD} -- bash
# Since the head Pod has the certificate of the full qualified DNS resolution for the Ray head service, the connection to the worker Pods
# is established successfully
ray health-check --address service-ray-head.default.svc.cluster.local:6379
# Since service-ray-head hasn't added to the alt_names section in the certificate, the connection fails and an error
# message similar to the following is displayed: "Peer name service-ray-head is not in peer certificate".
ray health-check --address service-ray-head:6379
# After you add `DNS.3 = service-ray-head` to the alt_names sections and deploy the YAML again, the connection is able to work.
启用 TLS 会由于双向身份验证和加密的额外开销而导致性能下降。测试表明,对于小型工作负载,此开销很大,而对于大型工作负载,则相对较小。确切的开销取决于您的工作负载的性质。
Java 应用程序#
重要提示
对于多节点设置,您必须先在命令行上运行ray start以在机器上启动 Ray 集群服务,然后才能在 Java 中运行ray.init()以连接到集群服务。在单台机器上,您可以运行ray.init()而无需ray start。它会同时启动 Ray 集群服务并连接到它们。
代码搜索路径#
如果您想在多节点集群中运行 Java 应用程序,则必须在驱动程序中指定代码搜索路径。代码搜索路径告诉 Ray 在启动 Java 工作进程时从何处加载 jar。在运行代码之前,您必须将 jar 文件分发到 Ray 集群的所有节点上的相同路径。
$ java -classpath <classpath> \
-Dray.address=<address> \
-Dray.job.code-search-path=/path/to/jars/ \
<classname> <args>
该/path/to/jars/指向一个包含 jar 的目录。工作进程会加载该目录中的所有 jar。您也可以为此参数提供多个目录。
$ java -classpath <classpath> \
-Dray.address=<address> \
-Dray.job.code-search-path=/path/to/jars1:/path/to/jars2:/path/to/pys1:/path/to/pys2 \
<classname> <args>
如果您在单节点集群中运行 Java 应用程序,则不需要配置代码搜索路径。
有关更多信息,请参阅ray.job.code-search-path在驱动程序选项下。
注意
目前在单机模式下运行 Java 应用程序时,没有办法配置 Ray。如果需要配置 Ray,请先运行ray start来启动 Ray 集群。
驱动程序选项#
Java 驱动程序有一组有限的选项。这些选项不是用于配置 Ray 集群,而是仅用于配置驱动程序。
Ray 使用Typesafe Config来读取选项。有几种方法可以设置选项:
系统属性。您可以通过在驱动程序命令行中以
-Dkey=value格式添加选项,或者在Ray.init()之前调用System.setProperty("key", "value");来配置系统属性。HOCON 格式(HOCON format)的配置文件。默认情况下,Ray 会尝试读取类路径根目录中名为
ray.conf的文件。您可以通过将系统属性ray.config-file设置为文件的路径来定制文件的位置。
注意
通过系统属性配置的选项比配置文件中配置的选项具有更高的优先级。
可用驱动程序选项列表
ray.address驱动程序连接到现有 Ray 集群时的集群地址。如果为空,Ray 会创建一个新的 Ray 集群。
类型:
String默认值:空字符串。
ray.job.code-search-pathJava 工作进程从中加载代码的路径。目前,Ray 只支持目录。您可以使用
:分隔一个或多个目录。如果您在单机模式或本地模式下运行 Java 应用程序,则无需配置代码搜索路径。如果指定了,Ray 也会使用代码搜索路径加载 Python 代码。对于跨语言编程,此参数是必需的。如果指定了代码搜索路径,则只能运行您在代码搜索路径中找到的 Python 远程函数。类型:
String默认值:空字符串。
示例:
/path/to/jars1:/path/to/jars2:/path/to/pys1:/path/to/pys2
ray.job.namespace此作业的命名空间。Ray 使用它来隔离作业。不同命名空间中的作业无法相互访问。如果未指定,Ray 将使用随机值。
类型:
String默认值:随机 UUID 字符串值。