常见问题#
分布式应用程序功能强大,但也增加了复杂性。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 时遇到的常见问题以及潜在的解决方案。如果您遇到其他问题,请报告。