使用 Ray 调试器#
Ray 内置了一个调试器,允许您调试分布式应用程序。它允许您在 Ray 任务和 Actor 中设置断点,当触发断点时,您可以进入一个 PDB 会话,然后您可以使用它来
检查该上下文中的变量
在该任务或 Actor 中单步执行
在栈中向上或向下移动
警告
Ray Debugger 已弃用。请改用Ray 分布式调试器。从 Ray 2.39 开始,新的调试器是默认设置,您需要设置环境变量RAY_DEBUG=legacy
来使用旧的调试器(例如通过使用 runtime environment)。
入门#
以以下示例为例
import ray
ray.init(runtime_env={"env_vars": {"RAY_DEBUG": "legacy"}})
@ray.remote
def f(x):
breakpoint()
return x * x
futures = [f.remote(i) for i in range(2)]
print(ray.get(futures))
将程序放入名为debugging.py
的文件中,并使用以下命令执行
python debugging.py
执行的 2 个任务中的每一个在执行到breakpoint()
行时都会触发断点。您可以通过在集群的头节点上运行以下命令来连接到调试器
ray debug
雷 ray debug
命令将打印如下输出
2021-07-13 16:30:40,112 INFO scripts.py:216 -- Connecting to Ray instance at 192.168.2.61:6379.
2021-07-13 16:30:40,112 INFO worker.py:740 -- Connecting to existing Ray cluster at address: 192.168.2.61:6379
Active breakpoints:
index | timestamp | Ray task | filename:lineno
0 | 2021-07-13 23:30:37 | ray::f() | debugging.py:6
1 | 2021-07-13 23:30:37 | ray::f() | debugging.py:6
Enter breakpoint index or press enter to refresh:
您现在可以输入0
并按 Enter 跳转到第一个断点。您将被带到断点的 PDB 会话中,并可以使用help
查看可用操作。运行bt
查看执行的回溯
(Pdb) bt
/home/ubuntu/ray/python/ray/workers/default_worker.py(170)<module>()
-> ray.worker.global_worker.main_loop()
/home/ubuntu/ray/python/ray/worker.py(385)main_loop()
-> self.core_worker.run_task_loop()
> /home/ubuntu/tmp/debugging.py(7)f()
-> return x * x
您可以使用print(x)
检查x
的值。您可以使用ll
查看当前源代码,并使用up
和down
更改栈帧。现在我们使用c
继续执行。
继续执行后,按Control + D
返回断点列表。选择另一个断点并再次按c
继续执行。
Ray 程序debugging.py
现已完成,并应打印出[0, 1]
。恭喜您,您已完成您的第一个 Ray 调试会话!
在集群上运行#
Ray 调试器支持在 Ray 集群中运行的任务和 Actor 中设置断点。为了从集群头节点使用ray debug
连接到这些断点,您需要在启动集群时(可能在您的cluster.yaml
文件或 k8s Ray 集群规范中)确保将--ray-debugger-external
标志传递给ray start
命令。
请注意,此标志将导致工作节点侦听外部 IP 地址上的 PDB 命令,因此仅当您的集群位于防火墙后时才应使用此标志。
调试器命令#
Ray 调试器支持与 PDB 相同的命令。
在 Ray 任务之间单步执行#
您可以使用调试器在 Ray 任务之间单步执行。让我们以以下递归函数为例
import ray
ray.init(runtime_env={"env_vars": {"RAY_DEBUG": "legacy"}})
@ray.remote
def fact(n):
if n == 1:
return n
else:
n_ref = fact.remote(n - 1)
return n * ray.get(n_ref)
@ray.remote
def compute():
breakpoint()
result_ref = fact.remote(5)
result = ray.get(result_ref)
ray.get(compute.remote())
通过执行 Python 文件并调用ray debug
运行程序后,您可以按0
并按 Enter 选择断点。这将产生如下输出
Enter breakpoint index or press enter to refresh: 0
> /home/ubuntu/tmp/stepping.py(16)<module>()
-> result_ref = fact.remote(5)
(Pdb)
您可以使用 Ray 调试器中的remote
命令跳转到调用。在函数内部,使用n
打印p(n)
的值,产生如下输出
-> result_ref = fact.remote(5)
(Pdb) remote
*** Connection closed by remote host ***
Continuing pdb session in different process...
--Call--
> /home/ubuntu/tmp/stepping.py(5)fact()
-> @ray.remote
(Pdb) ll
5 -> @ray.remote
6 def fact(n):
7 if n == 1:
8 return n
9 else:
10 n_ref = fact.remote(n - 1)
11 return n * ray.get(n_ref)
(Pdb) p(n)
5
(Pdb)
现在再次使用remote
单步执行到下一个远程调用并打印n
。您现在可以继续通过多次调用remote
递归进入函数,或者您可以使用get
调试器命令跳转到对结果调用ray.get
的位置。再次使用get
跳回原始调用点,并使用p(result)
打印结果
Enter breakpoint index or press enter to refresh: 0
> /home/ubuntu/tmp/stepping.py(14)<module>()
-> result_ref = fact.remote(5)
(Pdb) remote
*** Connection closed by remote host ***
Continuing pdb session in different process...
--Call--
> /home/ubuntu/tmp/stepping.py(5)fact()
-> @ray.remote
(Pdb) p(n)
5
(Pdb) remote
*** Connection closed by remote host ***
Continuing pdb session in different process...
--Call--
> /home/ubuntu/tmp/stepping.py(5)fact()
-> @ray.remote
(Pdb) p(n)
4
(Pdb) get
*** Connection closed by remote host ***
Continuing pdb session in different process...
--Return--
> /home/ubuntu/tmp/stepping.py(5)fact()->120
-> @ray.remote
(Pdb) get
*** Connection closed by remote host ***
Continuing pdb session in different process...
--Return--
> /home/ubuntu/tmp/stepping.py(14)<module>()->None
-> result_ref = fact.remote(5)
(Pdb) p(result)
120
(Pdb)
事后调试#
通常我们无法事先知道错误发生在哪里,因此无法设置断点。在这种情况下,当发生错误或抛出异常时,我们可以自动进入调试器。这称为事后调试。
将以下代码复制到名为post_mortem_debugging.py
的文件中。标志RAY_DEBUG_POST_MORTEM=1
的作用是,如果发生异常,Ray 将进入调试器,而不是进一步传播异常。
import ray
ray.init(runtime_env={"env_vars": {"RAY_DEBUG": "legacy", "RAY_DEBUG_POST_MORTEM": "1"}})
@ray.remote
def post_mortem(x):
x += 1
raise Exception("An exception is raised.")
return x
ray.get(post_mortem.remote(10))
让我们开始程序
python post_mortem_debugging.py
现在运行ray debug
。运行后,我们将看到如下输出
Active breakpoints:
index | timestamp | Ray task | filename:lineno
0 | 2024-11-01 20:14:00 | /Users/pcmoritz/ray/python/ray/_private/workers/default_worker.py --node-ip-address=127.0.0.1 --node-manager-port=49606 --object-store-name=/tmp/ray/session_2024-11-01_13-13-51_279910_8596/sockets/plasma_store --raylet-name=/tmp/ray/session_2024-11-01_13-13-51_279910_8596/sockets/raylet --redis-address=None --metrics-agent-port=58655 --runtime-env-agent-port=56999 --logging-rotate-bytes=536870912 --logging-rotate-backup-count=5 --runtime-env-agent-port=56999 --gcs-address=127.0.0.1:6379 --session-name=session_2024-11-01_13-13-51_279910_8596 --temp-dir=/tmp/ray --webui=127.0.0.1:8265 --cluster-id=6d341469ae0f85b6c3819168dde27cceda12e95c8efdfc256e0fd8ce --startup-token=12 --worker-launch-time-ms=1730492039955 --node-id=0d43573a606286125da39767a52ce45ad101324c8af02cc25a9fbac7 --runtime-env-hash=-1746935720 | /Users/pcmoritz/ray/python/ray/_private/worker.py:920
Traceback (most recent call last):
File "python/ray/_raylet.pyx", line 1856, in ray._raylet.execute_task
File "python/ray/_raylet.pyx", line 1957, in ray._raylet.execute_task
File "python/ray/_raylet.pyx", line 1862, in ray._raylet.execute_task
File "/Users/pcmoritz/ray-debugger-test/post_mortem_debugging.py", line 8, in post_mortem
raise Exception("An exception is raised.")
Exception: An exception is raised.
Enter breakpoint index or press enter to refresh:
我们现在按0
然后按 Enter 进入调试器。使用ll
我们可以查看上下文,使用print(x)
我们可以打印x
的值。
以类似上述的方式,您也可以调试 Ray Actors。祝您调试愉快!
调试 API#
请参阅调试。