Prefect:生产工作流
目录
Prefect:生产工作流¶
我是谁?¶
我是 Chris White;我是 Prefect 的首席技术官 (CTO),Prefect 是一家为数据工程师和数据科学家构建下一代工作流自动化平台的公司。在这个职位上,我是我们开源引擎的核心开发者,该引擎允许用户构建、调度和执行强大的工作流。
我正试图解决的问题¶
大多数团队负责维护对团队使命至关重要的生产工作流。历史上,这些工作流主要由批处理 ETL 作业组成,但最近也包括部署参数化机器学习模型、即席报告和处理事件驱动进程等内容。
通常这意味着开发者需要一个工作流系统,该系统可以执行以下操作,例如:
重试失败的任务
自动调度作业运行
记录工作流的详细进度(和历史)
提供用于检查系统健康的仪表板/UI
在出现问题时提供通知钩子
以及许多其他事情。我们在 Prefect 喜欢将工作流系统视为一项技术保险——当事情顺利进行时,您不应该太注意到它,但当事情出错时,它应该发挥最大的作用。
Prefect 的目标是构建下一代工作流系统。像 Airflow 和 Luigi 这样的旧系统受其工作流模型的限制,这些模型认为工作流是缓慢的、定期调度的,且任务间通信有限。另一方面,Prefect 拥抱了这种新现实,对工作流的性质和需求做了极少的假设,从而支持了数据工程和数据科学中更动态的使用案例。
Dask 如何提供帮助¶
Prefect 的设计和构建考虑到了 Dask。历史上,像 Airflow 这样的工作流系统处理*所有*调度,包括工作流本身以及工作流中包含的单个任务。这种模式引入了许多问题:
这给中央调度器带来了巨大的负担(它调度系统中采取的每一个行动)
它给任务运行增加了不小的延迟
实际上,这限制了工作流可以拥有的动态性
它还倾向于限制任务可以共享的数据量,因为所有信息都通过中央调度器路由
它要求用户必须运行外部调度器服务才能运行他们的工作流!
相反,Prefect 处理*工作流*的调度,并让 Dask 处理每个工作流中*任务*的调度和资源管理。这带来了许多开箱即用的好处:
任务调度:Dask 处理工作流内的所有任务调度,允许 Prefect 鼓励更小的任务,这些任务 Dask 会以毫秒级延迟调度
“数据流”:由于 Dask 处理任务之间适当信息的序列化和通信,Prefect 可以将“数据流”作为一流模式来支持
分布式计算:Dask 处理将任务分配给集群中的 worker,允许用户立即以最小的开销实现分布式计算的好处
并行性:无论是在集群中还是在本地运行,Dask 都提供了开箱即用的并行任务执行
此外,由于 Dask 是用纯 Python 编写的并拥有活跃的开源社区,我们可以非常轻松地获得有关潜在错误的反馈,甚至可以自己为改进软件做出贡献。
为了实现运行包含许多任务的工作流的能力,我们发现 Dask 的 Futures 接口非常适合我们。为了支持动态任务(即可以生成其他任务的任务),我们依赖 Dask worker clients。我们偶尔也尝试使用 Dask Queues 来实现更复杂的行为,例如 future 共享和资源限制,但目前并未实际使用(主要是出于设计原因)。
使用 Dask 时的痛点¶
我们在使用 Dask 时最大的痛点主要围绕客户端之间共享 futures 的能力(或缺乏此能力)。举一个具体的例子,假设我们从一个数字列表开始,并使用两次 client.map
,我们对列表中的每个元素计算 x -> x + 1 -> x + 2
。当只使用 dask 原语和单个客户端时,这些计算是异步进行的,这意味着每条分支的最终计算可以在不等待其他中间计算的情况下开始,如下图所示:
然而,在 Prefect 中,我们不仅仅是传递由单个 Client
创建的 Dask futures——当发生 map
操作时,dask futures 实际上是由 worker_client
创建并附加到 Prefect State
对象上的。理想情况下,我们会在此阶段保持这些 futures 未解析,以便计算可以像上面那样进行。然而,由于在客户端之间共享 futures 并非易事,我们必须使用同一个客户端 gather
这些 futures,这使得我们的计算以“广度优先”的方式进行:
这并非最糟糕的事情,但对于较长的工作流,让较快的 pipeline 分支继续执行以便尽早产生最终结果以供检查将会非常好。
更新:自 Prefect 0.12.0 版本起,Prefect 在运行于 Dask 时现已支持深度优先执行 (Depth First Execution)。
我们围绕 Dask 使用的技术¶
我们首选的 Prefect Flows 部署方式是使用 dask-kubernetes 在 Kubernetes 中启动一个短时存在的 Dask 集群。
此外,Prefect 任务中包含的逻辑可以是任意的;系统中的许多任务与数据库、GCP 资源、AWS 等交互。