技术概述
确保在深入以下技术细节之前已阅读Metaflow基础知识。您可以在Metaflow管理员指南中找到更多基础设施层面的技术细节。本文档专注于Metaflow代码库。
我们想建立一个数据科学平台,使数据科学代码可用、可扩展、可复制和适合生产,如为什么选择Metaflow部分所述。实现这些高层次目标有许多方法。我们采取了一种围绕以下四个核心功能设计的方法:
- 提供一个高度可用的API,用于将代码结构化为工作流,即作为一个步骤的有向图(可用性)。
- 持久化每个步骤所需的数据、代码和外部依赖的不可变快照 (可重复性)。
- 促进在各种环境中执行步骤,从开发到生产 (可扩展性, 生产就绪性).
- 记录有关之前执行的元数据,并使其容易访问(可用性,可重现性)。
本文件概述了核心功能的实现方式。
架构
这里是Metaflow的高层架构图:

下面,我们将详细描述组件。为了突出图中缺失的时间维度,我们按照开发生命周期中的以下阶段对描述进行分组:
- 开发时间,即代码编写时。
- 运行时,即代码被执行的时刻。
- 结果时间,即运行结果被使用的时间。
每个组件都包含指向实现功能的源文件的链接。
开发时组件
Metaflow中的核心开发时间概念是一个flow。它代表了需要计算的业务逻辑。
如何以最可用的方式将业务逻辑与框架交织在一起是Metaflow的一个核心设计问题。我们希望鼓励用户以一种能够实现可重复性和可扩展性的方式来构建代码。
相比之下,我们希望在开发过程中尽量减少与生产就绪相关的顾虑。理想情况下,用户可以编写符合惯例的Python代码,专注于逻辑本身,而框架的守护机制将自动使代码具备生产就绪状态。
流程
流是可以调度执行的最小计算单元。通常,流定义了一个工作流程,该工作流程从外部源提取数据作为输入,在多个步骤中处理这些数据,并生成输出数据。
用户通过继承 FlowSpec 并将步骤实现为方法来实现一个流程。除了步骤,流程还可以定义与调度相关的其他属性,例如参数和数据触发器。
图
Metaflow 根据步骤函数之间的转换推断出一个有向(通常是无环的)图。
Metaflow 要求定义转换,以便图可以从流的源代码静态解析。这使得能够将图转换为仅支持静态定义图的运行时执行,例如 Meson。
步骤
步骤是可恢复计算的最小单元。它由用户作为一个方法实现,该方法在流程类中用@step装饰器装饰。
一个步骤是 一个检查点。 Metaflow 对由步骤生成的数据进行快照,然后将其用作后续步骤的输入。因此,如果一个步骤失败,它可以在不重新运行前面的步骤的情况下恢复。
能够恢复执行是一个强大的功能。能够在任意代码行恢复执行将是非常方便的。进行检查点的主要原因是以步骤级别而不是行级别进行的,是因为保存状态的开销。建议用户保持步骤小,但又不要小到开销变得明显。
装饰器
步骤的行为可以通过装饰器进行修改。标签是扩展Metaflow的主要机制。例如,装饰器可以捕获异常,实现超时或为步骤定义资源要求。
一个步骤可以有任意多个装饰器,作为Python装饰器实现。
步骤代码
步骤代码是一个步骤的主体。它实现了流程的实际业务逻辑。
可以为Metaflow实现各种语言绑定,例如R,这样只有步骤代码的语言发生变化,而所有核心功能(用Python实现)保持不变。
所有实例变量,例如 self.x,在步骤代码中使用的都会成为 数据工件,并被自动持久化。栈变量,例如 x,不会被持久化。这个对比允许用户通过在步骤代码中明确选择持久化与非持久化变量来控制检查点的开销。
运行时组件
Metaflow中的核心运行概念是运行,即用户定义的流的执行。当用户在命令行上执行python myflow.py run时,就会发生一次运行。
Metaflow 的一个关键设计决策是使框架与运行时无关。同一段代码应该能够在各种环境中运行,例如在开发期间的笔记本电脑上或在生产期间的 生产就绪 工作流调度器上。
类似地,我们希望通过允许相同的代码在多个进程的笔记本电脑上并行运行或在多个批处理任务的云中运行,从而提供无缝的可扩展性。
任务
步骤的运行时对应是一个 任务。在运行时,一个正常的步骤会生成一个任务进行执行。一个 foreach 分裂步骤可能生成多个任务,这些任务由一个唯一的 foreach 堆栈 识别。
代码包
为了能够重现运行的结果,我们需要快照运行的代码。
代码包是工作目录中相关代码的不可变快照,存储在数据存储中,时间是在运行开始时。快照的一个便利副作用是,它也作为发生在云中的运行的代码分发机制。
环境
不幸的是,仅仅快照流程代码的工作目录不足以实现可重现性。代码通常依赖于外部库,这些库也需要包含在快照中。
环境的概念与代码包密切相关。环境封装了流代码及其外部依赖项,以便可以在远程系统上准确重现确切的执行环境。
运行时
流的运行是通过按照拓扑顺序执行步骤中定义的任务来完成的。 运行时的任务是协调这一执行过程。“运行时”的更好名称可能是调度器。
为了快速的本地迭代,Metaflow 配备了一个内置的运行时,它将任务作为独立进程执行。然而,这并不打算作为一个生产级的调度器。
对于生产运行,应该使用支持重试、错误报告、日志记录、高可用、可扩展,并且最好具有用户友好界面的运行时。在Netflix,Meson就是这样一个运行时。它得到了Metaflow的良好支持。
Metaflow 的一个关键特性是它与运行时无关。相同的代码可以同时在本地运行时和生产运行时执行,这使得快速的开发-部署-调试循环成为可能。
数据存储
Metaflow 需要一个对象存储,以便保存代码快照和数据工件。这个数据存储应该对所有执行 Metaflow 代码的环境可访问。AWS S3 是满足这一需求的完美解决方案。其次,Metaflow 支持使用本地磁盘作为数据存储,这在 Metaflow 自身的开发期间特别有用。
Metaflow 的一个重要特性是数据存储被用作内容寻址存储。代码和数据都通过其内容的哈希值进行识别,类似于 Git,因此相等的数据副本会自动去重。然而,需注意这种去重的范围是有限的;不同流程之间的数据将不会被去重。
元数据提供者
一个集中式元数据提供者跟踪运行。从严格意义上讲,这个功能并不是Metaflow所必需的,但它使系统变得更加可用。该服务还帮助在结果时使数据工件和其他关于运行的元数据更加可发现,如下所述。
结果时间组件
流程是为了它们的结果而定义和运行的。Metaflow 支持多种不同的方式来消费运行的输出:结果可以写入 Hive 表,以供下游系统和仪表板使用,它们可以在笔记本中访问以进行进一步分析,或者在托管的 Web 服务中访问(最后这个功能在开源版本中尚不可用)。
Metaflow 客户端
Metaflow 提供了一个高度 可用 的 Python API 来访问之前运行的结果,称为 metaflow.client。使用 metaflow.client 的一种典型方法是在 Jupyter notebook 中访问过去运行的数据工件。能够检查生产运行的内部状态或在 notebook 中进行进一步的临时分析结果是非常方便的。