常见问题#

分布式应用程序功能强大,但也增加了复杂性。Ray 的一些行为可能最初会让用户感到惊讶,但这些设计选择在分布式计算环境中服务于重要目的。

本文档概述了在集群中运行 Ray 时遇到的常见问题,并重点介绍了与本地运行 Ray 相比的关键差异。

环境变量未从 Driver 进程传递到 Worker 进程#

问题:在 Driver 上设置环境变量后,它不会传播到 Worker 进程。

示例:假设在运行 Ray 的目录中有一个文件 baz.py,并且您执行了以下命令

import ray
import os

ray.init()


@ray.remote
def myfunc():
    myenv = os.environ.get("FOO")
    print(f"myenv is {myenv}")
    return 1


ray.get(myfunc.remote())
# this prints: "myenv is None"

预期行为:用户可能期望在 Driver 上设置环境变量就像在单台机器上运行一样,将它们发送到所有 Worker 进程,但这并不会发生。

修复:启用 Runtime Environments 以显式传递环境变量。调用 ray.init(runtime_env=...) 时,它会将指定的环境变量发送到 Worker。或者,您可以将环境变量设置为集群设置配置的一部分。

ray.init(runtime_env={"env_vars": {"FOO": "bar"}})


@ray.remote
def myfunc():
    myenv = os.environ.get("FOO")
    print(f"myenv is {myenv}")
    return 1


ray.get(myfunc.remote())
# this prints: "myenv is bar"

文件名有时有效,有时无效#

问题:在 Task 或 Actor 中按名称引用文件有时会成功,有时会失败。这种不一致性源于 Task 或 Actor 在 Head Node 上运行时可以找到该文件,但在其他机器上可能不存在。

示例:考虑以下场景

% touch /tmp/foo.txt

以及这段代码

import os
import ray

@ray.remote
def check_file():
    foo_exists = os.path.exists("/tmp/foo.txt")
    return foo_exists

futures = []
for _ in range(1000):
    futures.append(check_file.remote())

print(ray.get(futures))

在这种情况下,您可能会收到 True 和 False 的混合结果。如果 check_file() 在 Head Node 或本地运行,它会找到文件;然而,在 Worker Node 上,它找不到。

预期行为:用户通常期望文件引用要么一致工作,要么可靠失败,而不是表现出不一致的行为。

修复

— 仅对此类应用程序使用共享文件路径。例如,网络文件系统或 S3 存储可以提供所需的一致性。— 避免依赖跨机器一致的本地文件。

Placement Groups 不可组合#

问题:如果您从 Placement Group 中运行的任务或 Actor 调度新任务,系统可能无法正确分配资源,导致操作挂起。

示例:假设您正在使用 Ray Tune(它会创建 Placement Groups),并希望将其应用于一个反过来使用 Ray Tasks 的目标函数。例如

import ray
from ray import tune
from ray.util.placement_group import PlacementGroupSchedulingStrategy

def create_task_that_uses_resources():
    @ray.remote(num_cpus=10)
    def sample_task():
        print("Hello")
        return

    return ray.get([sample_task.remote() for i in range(10)])

def objective(config):
    create_task_that_uses_resources()

tuner = tune.Tuner(objective, param_space={"a": 1})
tuner.fit()

这段代码会报错,提示消息为

ValueError: Cannot schedule create_task_that_uses_resources.<locals>.sample_task with the placement group
because the resource request {'CPU': 10} cannot fit into any bundles for the placement group, [{'CPU': 1.0}].

预期行为:代码成功执行,没有资源分配问题。

修复:确保在 create_task_that_uses_resources() 中调用的任务的 @ray.remote 声明中,包含参数 scheduling_strategy=PlacementGroupSchedulingStrategy(placement_group=None)

def create_task_that_uses_resources():
+     @ray.remote(num_cpus=10, scheduling_strategy=PlacementGroupSchedulingStrategy(placement_group=None))
-     @ray.remote(num_cpus=10)

过时的函数定义#

由于 Python 的一些微妙之处,重新定义远程函数可能并不总是能让 Ray 使用最新版本。例如,假设您定义了一个远程函数 f,然后重新定义它;Ray 应该使用新的定义

import ray

@ray.remote
def f():
    return 1

@ray.remote
def f():
    return 2

print(ray.get(f.remote()))  # This should print 2.
2

然而,在某些情况下,修改远程函数而不重启集群可能无效

导入的函数问题:如果 f 定义在外部文件(例如 file.py)中,并且您修改了它的定义,重新导入该文件可能会被忽略,因为 Python 将第二次导入视为无操作。一种解决方案是使用 from importlib import reload; reload(file) 而不是第二次导入。

辅助函数依赖:如果 f 依赖于定义在外部文件中的辅助函数 h,那么对 h 的更改可能不会传播。最简单的解决方案是重启 Ray 集群。或者,您可以重新定义 f 以在调用 h 之前重新加载 file.py

@ray.remote
def f():
    from importlib import reload
    reload(file)
    return file.h()

这会强制外部模块在 Worker 上重新加载。请注意,在 Python 3 中,您必须使用 from importlib import reload

捕获任务和 Actor 的调用站点#

Ray 在您调用任务、创建 Actor 或调用 Actor 方法时会捕获并显示堆栈跟踪。

要启用调用站点捕获,请设置环境变量 RAY_record_task_actor_creation_sites=true。启用后

— Ray 在创建任务、Actor 或调用 Actor 方法时会捕获堆栈跟踪。— 捕获的堆栈跟踪可在 Ray Dashboard(在任务和 Actor 详细信息下)、state CLI 命令 ray list task --detail 的输出以及 state API 响应中找到。

请注意,Ray 默认关闭堆栈跟踪捕获,因为它可能对性能有影响。仅在调试时需要时启用它。

示例

import ray

# Enable stack trace capture
ray.init(runtime_env={"env_vars": {"RAY_record_task_actor_creation_sites": "true"}})

@ray.remote
def my_task():
    return 42

# Capture the stack trace upon task invocation.
future = my_task.remote()
result = ray.get(future)

@ray.remote
class Counter:
    def __init__(self):
        self.value = 0

    def increment(self):
        self.value += 1
        return self.value

# Capture the stack trace upon actor creation.
counter = Counter.remote()

# Capture the stack trace upon method invocation.
counter.increment.remote()

本文档概述了使用 Ray 时遇到的常见问题以及潜在的解决方案。如果您遇到其他问题,请报告。