工作流编排工具
简介
实施数据科学项目不是一件简单的任务。至少,数据分析工作流程必须定期运行,以产生最新的结果。比如,一份上周数据的报告,或者由于概念发生变化而重新训练机器学习模型。在某些情况下,这类工作流的输出需要作为 API 公开,例如,一个经过训练的机器学习模型,通过点击 REST 端点来生成预测结果。
这就需要开发实践允许工作流 (也称为 pipeline) 是可重现、可重复,并且可以很容易地部署。近年来,涌现了大量开源工作流管理工具。由于有太多的选择,团队很难选择最适合他们需求的工具
Apache Airflow、Prefect 和 Dagster 等都属于编排工具。这些工具是数据工程团队的基础。
Apache Airflow 是三者中最老的一个,是经过实战考验且可靠的解决方案,它诞生于 Airbnb,由 Maxime Beauchemin 创建。那时,数据工程是一个不同的世界,主要关注定期安排的批处理作业,这些作业通常涉及带有 Hive 和 Druid 之类的丑陋系统。今天你仍然可以在 Airflow 中看到很多这样的传统。
Prefect 和 Dagster 是较新的产品,均受其云产品 Prefect Cloud 和 Dagster Cloud 的支持。Prefect Cloud 可免费启动并托管调度程序,而混合架构允许您在本地或基础架构上运行任务。
文档参考:
- 开源机器学习流水线(Pipeline)工具调研(MLOps)https://ploomber.io/blog/survey/#ploomber
评价标准
评估部分 | 说明 |
---|---|
易用性 | API设计有多么的易于使用 |
开发实践 | 支持增量构建和本地执行 |
调试 | 与现有的 Python 调试工具(如 pdb)集成 |
测试 | 支持集成测试和流水线(pipeline)测试 |
部署 | 在一个生产规模的系统中执行工作流,最好是开源的(例如 Kubernetes)。能够在生产中重用预处理训练代码,以消除训练服务偏差 |
编程语言 | Sql 兼容性。支持其他流行的编程语言,如 R 或 Julia |
可维护性 | 管道代码量(越少越好)和源代码组织。 还会考虑影响可维护性的特定工具的特性 |
Jupyter notebooks 支持 | 支持以交互方式开发流水线任务(即使用 jupiter notebooks/lab )和以编程方式运行 notebooks 以生成可视化报告 |
每个部分的评分标准为 1-3:
等级 | 说明 |
---|---|
NA | 不支持或者有主要的限制 |
1 | 支持但有一定限制 |
2 | 良好 |
3 | 优秀 |
1.易用性
对于任何软件工具来说,拥有一个允许用户快速入门的干净 API 都是必不可少的,而对于数据分析工具来说更是如此,因为用户的编程熟练程度各不相同
通常情况下,数据分析项目是实验性的 / 探索性的,并会经历初始的 “原型设计” 阶段。实践者往往远离 “生产工具”,因为他们通常有一个陡峭的学习曲线,这会减慢进度。因此,许多数据科学家将整个数据 pipelines 写在一个 notebook 上。这是一个糟糕的实践,因为它创建了不可维护的软件
避免这种糟糕实践的唯一方法是提供添加最小开销的生产工具,使它们更吸引人,并且实践者从第一天开始就使用它
- 开发实践
数据科学/机器学习是高度迭代的,特别是在”原型“阶段。
我们首先粗略地了解我们必须进行什么样的分析,并随着我们对数据了解的加深而进一步完善。数据科学家花费大量时间修改代码的一小部分,然后重新运行分析,看看这些更改如何影响结果(例如添加一个新特性)。
增量构建是促进这个迭代过程的关键。当数据科学家在单个任务中修改几行代码时,不需要从端到端(从头到尾)重新执行管道,只需要执行被修改/受影响的任务。
部署的数据流水线(Pipeline)通常由生产系统(如:Airflow 调度器或 Kubernetes)管理,然而,能够在不需要任何额外基础设施的情况下进行本地开发对于促进快速开发至关重要。
- 调试
众所周知,数据流水线(Pipeline)很难调试,因为错误可能来自不正确的代码或出乎意料的数据属性。能够启动一个交互式会话来调试我们的代码比查看一堆打印(或日志)语句更有效。
工作流管理器经常以特定的方式执行我们的代码(例如,multiprocessing、远程 workers 等),这可能会导致标准的 Python 调试工具无法工作。我们评估是否有可能使用标准工具来调试工作流。
- 测试
出乎意料的数据会以多种方式破坏流水线(Pipeline)。
最好的情况是,下游任务因为架构兼容性而崩溃,最坏的情况是:管道成功地运行,但产生不正确的结果(例如,一个性能很差的模型)。
为了防止这种隐蔽的 bug,在任务执行之后测试制品变得越来越普遍,这被称为集成测试或数据测试。例如,在对数据集应用转换后,检查数据集中是否没有 “NULL” 值。我们评估对集成测试的支持。
第二个(经常被忽略的)特性是测试我们的工作流的能力。工作流是复杂的,因为它们包含了多个阶段的过程,这些过程之间存在依赖关系。能够在测试环境中运行和检查我们的工作流有助于在开发而不是生产期间检测 bug。考虑使用诸如 “pytest” 之类的测试工具测试工作流的能力。
- 部署
数据工作流的两种最常见的部署模式是批处理和在线服务。
批量部署的一个例子是一个工作流,它处理新的观察结果(通常按计划进行),做出预测并将其上传到数据库。
在线服务场景涉及到将流水线公开为 API(例如:REST/RPC),该 API 接受输入数据并返回预测结果。
当服务期间的预处理代码与训练期间的预处理代码不同时,部署期间就会发生一个常见错误(训练服务倾斜)。我们评估重用现有训练代码的能力,以消除这个问题。
我们还评估了部署方案是否支持与其他开源部署工具(如:Airflow、Kubernetes)集成。
- 编程语言
虽然由于深度学习,使用非结构化数据训练 ML 模型变得越来越普遍,但表格数据和经典 ML 算法仍然是最常见的应用类型。
表格数据通常存储在 SQL 数据库中,这意味着我们的流水线通常是 SQL 和 Python 的组合。我们还评估了将 SQL 脚本作为工作流一部分的能力。最后,我们还评估了对其他流行的数据分析语言(如 R 和 Julia)的支持。
- 可维护性
主要评估项目的可维护性。
要考虑的第一个方面是声明流水线所需的代码量(更少的代码更好)。
第二个方面是代码组织,我们确定库是否施加了限制,从而限制了我们在单独的函数/模块中组织代码的能力。
还评估了可能影响代码可维护性的特定工具的特性。
- Jupyter notebooks 支持
在生产流水线(pipelines)中使用 notebooks 总是会引发激烈的争论,但我认为问题来自于糟糕的开发实践,而不是 notebooks 本身。
Notebooks 是探索工作的绝佳环境,这正是我们在学习数据时所需要的。能够交互式地开发工作流对生产力有很大的积极影响。
Notebooks 的第二个重要用途是作为输出格式。 .ipynb
格式支持在单个文件中嵌入表格和图表,无需任何额外的代码。这是一个巨大的时间节省,因为当我们可以用图表查看数据时,调试工作流会容易得多,而使用基于文本的日志查找错误严重限制了这个过程。
拥有一个 .ipynb
文件作为执行工作流任务的结果,类似于有丰富的日志,以方便调试。
可选方案
Airflow
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 1 | Airflow 是出了名的难以学会。 尽管有各种各样的任务类型可用,但在实践中,推荐的编写工作流的方法是专门使用 Kubernetes operator 以确保环境隔离。 |
开发实践 | 1 | 工作流可以使用本地执行程序在本地运行,但这需要完整的 Airflow 安装。 此外,一次性工作流执行并不简单,因为 Airflow 对工作流应该如何以及何时执行做出了强有力的假设。 不支持增量构建。 |
调试 | NA | 没有调试工具 |
测试 | 1 | Airflow 工作流是 Python 对象,您可以导入它们并检查其属性。 但是,以这种方式测试工作流似乎不是官方或推荐的做法。 |
部署 | 2 | 缩放和调度是 Airflow 的核心优势。 但是不支持将工作流公开为 API 端点。 |
编程语言 | 2 | 支持多种 SQL 后端 |
可维护性 | 2 | 由于每个任务都是一个 Python 对象,所以您可以在多个模块中组织大型项目,而不受任何限制。 有些任务是由社区贡献的,质量各不相同。 |
Jupyter notebooks 支持 | 1 | 有一个以编程方式执行 notebooks 的任务,但是不建议使用它,因为代码是在全局环境中执行的。 不支持交互式开发 |
Dagster
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 1 | 工作流是用 Python 编写的。这个 API 有很多特性,但是很难理解和阅读。例如,很多功能都隐藏在 “context” 参数中 即使对于执行 SQL 查询这样看似简单的任务,也必须熟悉一些概念并输入大量代码 |
开发实践 | 1 | 为在本地执行工作流并部署到分布式系统(例如 Airflow、Kubernetes)提供了极大的灵活性。 不支持增量构建。 |
调试 | NA | 没有调试工具。 |
测试 | 3 | 对测试的大力支持,使用 hooks 可以执行集成测试。 可以使用测试框架导入和测试工作流。 |
部署 | 2 | Dagster 带有全功能的执行程序和调度程序。 但是,这意味着您必须维护只能执行 dagster 工作流的 dagster 安装。 没有关于将工作流公开为 API 的文档,但这似乎是可能的,因为工作流可以作为任何其他 Python 对象导入和使用。 |
编程语言 | 1 | 仅支持 postgres 和 snowflake,不支持 R/Julia |
可维护性 | 1 | 配置机制极其冗长,有几个可选包可以与其他系统集成,但它们都有不同的API。 |
Jupyter notebooks 支持 | 1 | 支持以编程方式执行notebooks。 不支持交互式开发。 |
Prefect
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 2 | 基于函数的工作流是用干净的 Python API 编写的。 任务库包含各种各样的任务,然而,只有少数与数据科学 / 机器学习项目相关。 |
开发实践 | 2 | 工作流可以在本地执行。不支持增量构建。 |
调试 | 3 | 您可以查看每个任务的输出和状态。还有使工作流调试变得简单的工具。 |
测试 | NA | 不支持集成测试。不支持 pipeline 测试。 |
部署 | 1 | 可以使用 web 界面部署 (调度) 工作流,但该界面只能执行 Prefect 工作流。 不支持在其他系统中运行工作流。 Prefect server 有一个非标准开源许可证 |
编程语言 | 1 | 虽然支持一些 SQL 数据库 (例如 postgres, snowflake, sqlite),但每个模块都有不同的 API。 不支持 R 和 Julia。 |
可维护性 | 3 | 很棒的 API 和最小的样板代码。 |
Jupyter notebooks 支持 | 1 | 支持以编程方式执行 notebooks。 不支持以交互方式开发任务。 |
Ploomber
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 3 | 使用约定优于配置的方法,开始时,您只需在脚本 /notebooks 中包含两个特殊变量,Ploomber 将编排执行。 为了获得更大的灵活性,您可以使用 YAML 指定您的 pipeline,对于高级用例,请使用 Python API。 |
开发实践 | 3 | 工作流可以在单个进程或多个进程(并行)中本地执行。 提供增量构建。 |
调试 | 3 | 与 pdb 和 ipdb 集成,您可以在任何任务上启动逐行调试会话,或者让管道运行并在任务崩溃时启动事后分析会话。 |
测试 | 3 | 在任务执行时使用 on_finish 钩子执行集成测试。 管道是常规的 Python 对象,您可以导入它们并使用您选择的测试框架进行测试。 |
部署 | 1 | 支持将工作流导出到 Airflow 和 Argo (Kubernetes),然而,此功能仅提供基本功能,但正在积极开发中。 Python 批处理管道可以导出到内存中审阅观察结果,此对象可与任何 Web 框架一起使用,以创建 API 端点。 |
编程语言 | 2 | 支持任何带有 Python 连接的数据库。 完整的 R 支持。 对具有 Jupyter 内核的语言(例如 Julia)的支持有限。 |
可维护性 | 3 | 任务可以是脚本 (Python/R/SQL)、notebook 或 Python 函数,您可以选择对您的项目更有用的任何内容。 任务库(公开统一的 API)为常见任务(例如运行 SQL 脚本、转储数据库表)提供了功能。以减少样板代码。 |
Jupyter notebooks 支持 | 3 | 可以使用 Jupyter 以交互方式开发脚本和 notebooks,然后以编程方式执行。 |
Kubeflow pipelines
部分 | 分数 | 评论 |
---|---|---|
使用的简易性 | 3 | 管道可视化编辑器使得将一组笔记本转换成管道变得非常简单。 |
开发体验 | 1 | 可以在本地执行管道。不支持增量构建。 |
调试 | NA | 没有调试工具。 |
测试 | NA | 不支持集成测试。不支持管道测试。 |
部署 | 1 | 通过Kubeflow管道在Kubernetes中运行工作流程。不支持安排工作流程。由于其独占的基于笔记本的性质,没有简单的方法将工作流程转换为API端点。 |
编程语言 | NA | 仅支持Python。 |
可维护性 | 2 | 可视化编辑器很好地促进了工作流程的作者,但是一些人可能更喜欢对管道定义拥有更多的控制权。定义以JSON格式编写,但是不清楚手动编辑这样的文件是否推荐。限制在于任务必须是笔记本。 |
Jupyter笔记本支持 | 3 | Elyra是一个以笔记本为中心的工具,其中每个任务都是一个笔记本,因此可以交互式地开发任务。当执行管道时,笔记本会以编程方式执行。 |
Metaflow
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 3 | 开发工作流是使用 Python 类定义的,decorator 可用于多种任务,例如在执行任务之前重试任务或安装依赖项。 |
开发实践 | 2 | 工作流可以在本地执行,可以从失败的任务中恢复执行。不支持增量构建。 |
调试 | 1 | 如果工作流失败,您可以检查数据来确定是什么出错了。 尽管如此,您只能在工作流失败后进行调试,不支持启动交互式的事后调试会话,并且您必须使用 print 语句进行调试,这并不理想。 |
测试 | 2 | 可以导入工作流以检查其定义和属性。 虽然没有明确提及,但似乎没有任何限制,您可以将此测试工具与 pytest 等测试框架一起使用。 |
部署 | 1 | Metaflow 带有一个内置的 AWS 工具 来执行工作流,也可以调度使用 AWS Step Functions 的工作流程。 然而,Netflix 使用了一个内部 (封闭源代码) DAG 调度器。 没有部署到其他云的选项。 工作流似乎可以作为 api 公开,但不清楚这是否是开源包的一部分。 |
编程语言 | 1 | 它支持 R 工作流,尽管它是一个使用 Python 库作为后端的独立工具,但你不能在同一个工作流中混合使用 R 和 Python。 不支持 SQL。 |
可维护性 | 1 | 要求将工作流定义为单个类对于协作和代码组织是有问题的。 此外,由于任务不是无状态的(由于实例变量的存在),它可能会导致隐藏的 bug。 |
Jupyter notebooks 支持 | NA | 不支持交互式开发。不支持以编程方式执行 notebooks。 |
DVC (Data pipelines)
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 3 | 使用 YAML 文件 指定工作流,其中每个任务由要执行的命令指定,文件依赖项和输出文件。 |
开发实践 | 3 | 可以(完全)本地运行工作流,并提供增量构建。 |
调试 | NA | 没有调试工具。 |
测试 | NA | 不支持集成测试。不支持管道测试。 |
部署 | NA | 不支持导出到大规模系统。不支持将工作流公开为 API 端点 |
编程语言 | NA | 框架与语言无关,任务是使用命令指定的。但是,这意味着您必须为每个脚本提供一个命令行接口来传递参数。 不直接支持 SQL。 |
可维护性 | 2 | 工作流是用 YAML 指定的,这对于小项目来说很好,但是对于大项目来说不是很好。 一旦项目增长,YAML 文件变得冗余,因为你必须指定相同的值多次 (即一个脚本 train.py 出现在 cmd 节和 deps 节)。对于几十个任务,这就变得冗长且容易出错。 |
Jupyter notebooks 支持 | 1 | 任务是用一个命令指定的,这意味着您可以自由地使用 notebooks 作为流水线任务,以交互式地编辑它们,然后以编程方式运行它。 但是,您必须手动指定用于以编程方式执行 notebook 的命令和参数,因为 DVC 不知道它的内容。 |
- 文档:https://dvc.org/doc/start/data-pipelines
- 源码:https://ploomber.io/posts/survey/github.com/iterative/dvc/
Tensorflow Extended (TFX)
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 1 | 该 API 非常复杂,它要求您在开始使用之前熟悉几个模块。 |
开发实践 | 2 | 可以在本地执行管道。 不支持增量构建。 |
调试 | NA | 没有调试工具。 |
测试 | 1 | 技术上可行,因为您可以在本地运行工作流,但是,本地工作流必须显式启用交互模式,当在测试框架下运行时,这是否会引起麻烦还不清楚。 不支持管道测试。 |
部署 | 3 | 共有三个部署选项:Airflow、Kubeflow Pipelines 和 Apache Beam,但是,示例仅针对 Google Cloud 提供。 可以使用 Tensorflow serving 将工作流公开为 API。 |
编程语言 | NA | 仅限 Python。 |
可维护性 | NA | TFX 是一个 Tensorflow 独有的框架,这意味着你不能带其他库,比如 numpy 或 pandas。 还有大量样板代码使不熟悉 Tensorflow 生态系统的人难以理解管道。 |
Jupyter notebooks 支持 | 1 | 您可以交互式地开发 pipelines,并在单个 notebook 中运行它们。 不支持以编程方式执行 notebooks。 |
Elyra
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 3 | 管道可视化编辑器非常容易将一组 notebooks 转换为管道。 |
开发实践 | 1 | 管道可以在本地执行。 不支持增量构建。 |
调试 | NA | 没有调试工具。 |
测试 | NA | 不支持集成测试。不支持管道测试。 |
部署 | 1 | 通过 Kubeflow 管道在 Kubernetes 中运行工作流。 不支持调度工作流。 由于其独有的基于 notebook 的特性,没有简单的方法可以将工作流转换为 API 端点。 |
编程语言 | NA | 仅支持 Python。 |
可维护性 | 2 | 可视化编辑器非常有助于工作流的编写,但是,有些人可能更喜欢对管道定义进行更多的控制。 该定义是以 JSON 格式编写的,但不清楚是否建议手动编辑此类文件。 它限制了任务必须是 notebooks。 |
Jupyter notebooks 支持 | 3 | Elyra 是一个以 notebook 为中心的工具,其中每个任务都是一个 notebook,因此,您可以交互式地开发任务。 当您执行您的管道时,notebooks 将以编程方式执行。 |
Flyte
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 2 | API 是干净的。任务是用带有少量装饰器的 Python 函数定义的。 |
开发实践 | NA | 工作流不能在本地执行,只能在 Kubernetes 中执行。不支持增量构建。 |
调试 | NA | 没有调试工具。 |
测试 | NA | 不支持集成测试。不支持管道测试。 |
部署 | 2 | 运行在 Kubernetes 上,支持调度。不清楚是否有可能将工作流公开为 API 端点 |
编程语言 | 1 | 支持一些与 SQL 兼容的系统,比如 Hive 和 Presto。也支持 Spark。不支持 R/Julia |
可维护性 | 1 | API 是干净的,但文档仍在进行中,只有几个代码示例。 |
Jupyter notebooks 支持 | NA | 不支持交互式开发,也不支持以编程方式执行 notebooks。 |
Kale
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 3 | 在 Kale 中部署管道只需要向 Jupyter notebook 单元添加标签。 |
开发实践 | 1 | 工作流可以本地执行。不支持增量构建。 |
调试 | NA | 没有调试工具。 |
测试 | NA | 不支持集成测试。不支持管道测试。 |
部署 | 2 | 批量处理的部署是无缝的,一旦您标注了您的笔记本,您就可以将工作流提交到 Kubernetes 集群。 但是,不支持对 API 端点重新使用特性工程代码。 |
编程语言 | NA | 仅支持 Python。 |
可维护性 | NA | 管道必须在单个笔记本文件中声明,这可能会导致很多麻烦,因为单元格副作用难以跟踪。 在解决版本控制冲突时,让多人编辑同一个文件会导致很多麻烦。 最后,您编写的代码不是执行的代码(它们使用 jinja 生成 Kubeflow 代码),这可能会导致调试问题。 |
Jupyter notebooks 支持 | 2 | Kale 是一个 notebook 优先的框架。 您可以交互式地开发管道,而 notebook 本身也成为管道,但是,它在执行之前已经经历了一些预处理步骤。 |
Kedro
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 1 | 管道使用 Python API 定义,其中每个任务都是一个函数。 虽然工作流 API 是干净的,但一些额外的模块具有复杂的 API。 此外,它是非常固执的,并期望您的项目遵循一个特定的文件夹布局,其中包括几个特定 kedro 的配置文件。 |
开发实践 | 1 | 工作流可以本地执行。不支持增量构建。 |
调试 | 2 | 支持调试节点和管道,尽管API看起来很复杂。 |
测试 | 2 | 支持在执行时测试任务(钩子),但是,与调试 API 类似,它看起来很复杂。 |
部署 | 2 | 支持部署到 Kubernetes(Argo 和 Kubeflow)、Prefect 和 AWS Batch。 目前尚不清楚是否可以将批处理管道转换为在线 API。 |
编程语言 | NA | 仅支持 Python。 |
可维护性 | 1 | 期望您的项目具有特定的文件夹布局和配置文件。对于简单的项目来说,这是一种限制和过度的做法。 |
Jupyter notebooks 支持 | 1 | 您可以启动 Jupyter notebook 并将定义的函数导出为 kedro 节点(任务),但由于导出的代码必须是一个函数,所以交互性受到限制。 不支持以编程方式执行 notebooks。 |
Luigi
评估部分 | 分数 | 评价 |
---|---|---|
易用性 | 3 | 要熟悉 API 才能开始使用,但是它没有其他 API 那么复杂。 它有一套一致的概念:任务、目标和参数。任务(定义为 Python 类)的结构基本相同。 |
开发实践 | 1 | 可以在本地运行工作流。 不支持增量构建(一旦执行任务,即使输入文件发生更改,再次运行它也没有效果)。 |
调试 | NA | 没有调试工具 |
测试 | 1 | 虽然不是专门为这个目的设计的,但是回调可以用于集成测试。 不支持检查管道来测试其属性/定义。 |
部署 | 2 | 部署到中央监控工具非常简单。可伸缩性有限,没有内置的调度程序。 只有批处理,不转换为 API 端点。 |
编程语言 | 2 | 支持一些 SQL 后端。 |
可维护性 | 1 | 要求将工作流定义为单个类对于协作和代码组织是有问题的。 此外,由于任务不是无状态的(由于实例变量的存在),它可能会导致隐藏的 bug。 |
Jupyter notebooks 支持 | NA | 不支持交互式开发。不支持以编程方式执行 notebooks。 |