我的 worker 为什么会死?
目录
我的 worker 为什么会死?¶
Dask worker 可能因多种原因停止工作。这些原因可归为以下几类:
worker 选择退出
worker 内部发生不可恢复的异常
worker 进程被外部操作关闭
下面将详细描述这些情况。当这些情况发生时,您将体验到的症状包括工作停止完成,到与本地客户端交互时出现各种异常,例如 KilledWorker
、TimeoutError
和 CommClosedError
。
请注意 KilledWorker
的特殊情况:这意味着某个特定任务在一个 worker 上尝试执行,该 worker 死了,然后同一个任务被发送到另一个 worker,它也死了。在达到可配置的死亡次数(配置项 distributed.scheduler.allowed-failures
)后,Dask 决定将问题归咎于任务本身,并返回此异常。请注意,任务有可能被不公平地归咎——worker 可能在任务激活时恰好死亡,原因可能是另一个线程的问题——这使得诊断变得复杂。
在任何情况下,查找进一步信息的首要地方是给定 worker 的日志,其中很可能完整描述了发生的情况。这些日志由 worker 打印到其“标准错误”,可能出现在您启动 worker 的文本控制台中,或者由集群基础设施维护的某个日志系统中。观察诊断仪表板以查看内存峰值也很有帮助,当然这只有在 worker 仍然存活时才可能。
在所有情况下,调度器都会注意到 worker 已经消失,无论是由于显式注销,还是因为 worker 不再产生心跳,并且应该可以将任务重新路由到其他 worker,使系统保持运行。
场景¶
Worker 选择退出¶
Worker 在正常功能下可能会退出,因为它们被要求这样做,例如,它们收到了键盘中断 (^C),或者调度器缩减了集群。在这种情况下,如果还有其他 worker 存在,该 worker 正在进行的工作将被重定向到其他 worker。
您应该期望在 worker 日志的末尾看到以下消息:
distributed.dask_worker - INFO - End worker
在这些情况下,通常您不需要做任何事情,因为这是预期的行为。
不可恢复的异常¶
Worker 是一个 Python 进程,与其他任何代码一样,可能会发生导致进程退出的异常。一个典型的例子可能是客户端和 worker 的包版本不匹配,导致发送给 worker 的消息在解包时出错。有很多需要匹配的包,不仅是 dask
和 distributed
。
在这种情况下,您应该期望在 worker 的日志中看到完整的 Python traceback。在版本不匹配的情况下,这可能会抱怨导入错误或缺少属性。然而,其他致命异常也可能发生,例如尝试分配比系统可用内存更多的内存,或者在没有适当权限的情况下写入临时文件。
为确保版本匹配,您应该运行(较新版本的 distributed 可能会自动完成):
client.get_versions(check=True)
对于其他错误,如果可能的话,您可能希望在本地客户端运行计算,或者尝试获取出错的任务并使用 recreate_error_locally()
,就像处理任务执行期间发生的普通异常一样。
特别是对于连接问题(例如,worker 日志中的超时异常),您需要诊断您的网络基础设施,这比这里能描述的要复杂。通常,这可能涉及登录运行受影响 worker 的机器。
被 Nanny 终止¶
Dask 的“nanny”是一个监视 worker 并根据需要重新启动它的进程。它还跟踪 worker 的内存使用情况,如果使用量超过总内存的给定比例,worker 也将被重启,中断任何正在进行的工作。日志将显示类似以下的消息:
Worker exceeded X memory budget. Restarting
其中 X 是内存比例。您可以使用配置设置此临界比例,请参阅Worker 内存管理。如果您有集群基础设施(HPC、kubernetes 等)提供的外部系统来监视内存使用情况,那么关闭此内存限制可能是合理的。实际上,在这些情况下,重启也可能由外部系统为您处理,这样您就可以完全不需要 nanny 了(CLI 选项 --no-nanny
或等效配置)。
突然退出¶
Worker 进程可能会在没有通知的情况下停止工作。这可能是由于 worker 内部的问题,例如内存违规(与编译代码交互时常见),或由于外部原因,例如 kill
命令,或 worker 运行所在的容器或机器停止。
在最好的情况下,您可能会在日志中看到操作系统显示 worker 被关闭的行,例如一个简单的词“killed”或更具描述性的内容。在这些情况下,问题很可能出在您的代码中,您可以使用与上一节相同的调试工具。
然而,如果操作是由某个外部框架启动的,那么 worker 将没有时间留下日志消息,并且死亡可能与 worker 当时正在做的事情无关。例如,如果 kubernetes 决定驱逐一个 pod,或者您的 ec2 实例因维护而关闭,worker 并无过错。希望系统在进程输出中提供关于发生情况的合理消息。然而,如果内存分配(或其他资源)超出容忍范围,那么就是代码的问题——尽管您可以通过更好地配置 Dask 自身的限制,或者仅仅通过更大的集群来解决。无论如何,您的部署框架有自己的日志系统,您应该在那里查找 Dask worker 被终止的原因。
特别对于内存问题,请参阅最佳实践的内存部分。