Maestro:Netflix 开源工作流编排器

我们很高兴地宣布,Maestro 源代码现已向公众开放!请访问Maestro GitHub 存储库开始使用。

什么是 Maestro
Maestro 是一种通用的、可水平扩展的工作流编排器,旨在管理大规模工作流,例如:数据管道和机器学习模型训练管道。

  • 它监督工作流的整个生命周期,从开始到结束,包括重试、排队、将任务分配给计算引擎等。
  • 用户可以将其业务逻辑打包成各种格式,例如 Docker 镜像、笔记本、bash 脚本、SQL、Python 等。
  • 与仅支持有向无环图 (DAG) 的传统工作流编排器不同,Maestro 支持无环和有环工作流,还包括多种可重用模式,包括 foreach 循环、子工作。

在过去一年中,我们的执行任务数量显著增加了 87.5%。Maestro 现在每天平均启动数千个工作流程实例并运行 50 万个任务,在特别繁忙的日子里完成了约 200 万个任务。

可扩展性和多功能性
Maestro 是一个完全托管的工作流编排器,它为 Netflix 的数千名最终用户、应用程序和服务提供工作流即服务。它支持各种工作流用例,包括 ETL 管道、ML 工作流、AB 测试管道、在不同存储之间移动数据的管道等。Maestro 的水平可扩展性确保它可以管理大量工作流和单个工作流中的大量作业。

在 Netflix,工作流程错综复杂地联系在一起。将它们分成更小的组并跨不同的集群进行管理会增加不必要的复杂性并降低用户体验。这种方法还需要额外的机制来协调这些分散的工作流程。由于 Netflix 的数据表位于单个数据仓库中,我们认为单个编排器应该处理访问它的所有工作流程。

Maestro架构
Netflix Maestro 提供了一套全面的功能,旨在满足工程师和非工程师的多样化需求。它包括以松散耦合的方式适用于各种用例的常用功能和可重用模式。

工作流定义采用 JSON 格式。Maestro 将用户提供的字段与 Maestro 管理的字段相结合,形成灵活而强大的编排定义。可以在Maestro 存储库 wiki中找到一个示例。

Maestro 工作流定义包含两个主要部分:

  1. 属性
  2. 版本化工作流(包括其元数据)。

下面详细介绍:
1、属性包括作者和所有者信息以及执行设置。
Maestro 保留了工作流各个版本的关键属性,例如作者和所有者信息、运行策略和并发设置。这种一致性简化了管理并有助于故障排除。如果当前工作流的所有权发生变化,新所有者可以声明对工作流的所有权,而无需创建新的工作流版本。

用户还可以通过属性为给定的工作流启用触发或警报功能。

2、版本化工作流包括诸如唯一标识符、名称、描述、标签、超时设置和关键性级别(低、中、高)等属性,用于确定优先级。每次工作流更改都会创建一个新版本,从而实现跟踪和轻松恢复,默认情况下使用活动版本或最新版本。

  • 工作流由步骤组成,这些步骤是用户定义的工作流图中的节点。
  • 步骤可以表示作业、使用子工作流步骤的另一个工作流或使用 foreach 步骤的循环。
  • 步骤由唯一标识符、步骤类型、标签、输入和输出步骤参数、步骤依赖关系、重试策略和故障模式、步骤输出等组成。
  • Maestro 支持基于错误类型的可配置重试策略,以增强步骤弹性。

本文对 Netflix Maestro 的工作流定义和属性进行了高层次概述,重点介绍了其定义复杂工作流的灵活性。接下来,我们将在以下部分中深入介绍一些有用的功能。

工作流运行策略
用户希望在保持对执行顺序的控制的同时实现数据管道的自动化。当工作流无法并行运行或必须在出现新执行时暂停当前执行时,这一点至关重要。Maestro 使用预定义的运行策略来决定工作流实例是否应该运行。以下是 Maestro 提供的预定义运行策略列表。

顺序运行策略
这是 maestro 使用的默认策略,它根据先进先出 (FIFO) 顺序一次运行一个工作流。使用此运行策略,Maestro 按照触发的顺序运行工作流。请注意,执行不依赖于先前的状态。一旦工作流实例达到其中一个终端状态,无论成功与否,Maestro 都会启动队列中的下一个工作流。

严格顺序运行策略
使用此运行策略,Maestro 将按触发顺序运行工作流,但如果工作流实例历史记录中出现阻塞错误,则会阻止执行。新触发的工作流实例将排队,直到通过手动重新启动失败的实例或将失败的实例标记为未阻塞来解决错误。

run5 在凌晨 5 点失败,然后后面的运行被排队但不会运行。当有人手动将 run5 标记为未阻止或重新启动它时,工作流执行将恢复。

此运行策略对于时间不敏感但业务关键的工作流很有用。这使工作流所有者可以选择稍后查看失败并在验证正确性后取消阻止执行。

仅第一个运行策略
使用此运行策略,Maestro 可确保正在运行的工作流在排队新的工作流实例之前完成。如果在当前工作流实例仍在运行时将新工作流实例排队,Maestro 将删除排队的实例。Maestro 仅在当前没有工作流实例运行时才会执行新的工作流实例,从而有效地使用此运行策略关闭排队。此方法通过不排队新的工作流实例来帮助避免幂等性问题。

仅最后一个运行策略
使用此运行策略,Maestro 可确保正在运行的工作流是最新触发的工作流,并且仅保留最后一个实例。如果在现有工作流实例正在运行的情况下将新工作流实例排队,Maestro 将停止正在运行的实例并执行新触发的实例。如果工作流设计为始终处理最新数据(例如每次处理整个表的最新快照),则这很有用。

并行并发限制运行策略
使用此运行策略,Maestro 将并行运行多个触发的工作流实例,并受预定义的并发限制约束。这有助于分散和分配执行,从而能够在时间限制内处理大量数据。此策略的一个常见用例是回填旧数据。

参数和表达式语言支持
在 Maestro 中,参数起着重要作用。Maestro 支持通过代码注入实现动态参数,这非常有用且功能强大。此功能显著增强了工作流的灵活性和动态性,允许使用参数来控制执行逻辑,并实现工作流及其步骤之间以及上游和下游步骤之间的状态共享。与其他 Maestro 功能相结合,它使工作流的定义变得动态,并允许用户为复杂的用例定义参数化工作流。

然而,代码注入会带来严重的安全隐患。例如,用户可能会无意中编写一个无限循环,创建一个数组并向其中附加项目,最终导致服务器因内存不足 (OOM) 问题而崩溃。虽然一种方法可能是要求用户将注入的代码嵌入到他们的业务逻辑中而不是工作流定义中,但这会给用户带来额外的工作,并将他们的业务逻辑与工作流紧密耦合。在某些情况下,这种方法会阻止用户设计一些复杂的参数化工作流。

为了降低这些风险并帮助用户构建参数化工作流,我们开发了自己的定制表达式语言解析器,一种简单、安全且可靠的表达式语言 (SEL)。SEL 支持代码注入,同时在语法树解析期间加入验证以保护系统。它利用 Java 安全管理器来限制访问,确保代码执行的环境安全且受控。

简单、安全、安全的表达语言 (SEL)
SEL 是一种自制的简单、安全、安全的表达语言 (SEL),用于解决 Maestro 参数化工作流中与代码注入相关的风险。它是一种简单的表达语言,其语法和句法遵循 JLS(Java 语言规范)。SEL 支持 JLS 的一个子集,专注于 Maestro 用例。例如,它支持所有 Maestro 参数类型的数据类型、引发错误、日期时间处理和许多预定义的实用程序方法。SEL 还包括额外的运行时检查,例如循环迭代限制、数组大小检查、对象内存大小限制等,以增强安全性和可靠性。有关 SEL 的更多详细信息,请参阅Maestro GitHub 文档

输出参数为了进一步增强参数支持,Maestro 允许可调用步骤执行,将用户执行的输出参数返回给系统。输出数据通过其 REST API 传输到 Maestro,确保步骤运行时无法直接访问 Maestro 数据库。这种方法大大减少了安全问题。

参数化工作流得益于强大的参数支持,用户可以轻松创建参数化工作流,而不仅仅是静态工作流。用户喜欢定义参数化工作流,因为它们易于管理和故障排除,同时功能强大,足以解决复杂的用例。

  • 静态工作流简单易用,但也有局限性。通常,用户必须多次复制相同的工作流才能适应细微的变化。此外,如果不使用参数,工作流和作业就无法共享状态。
  • 另一方面,完全动态的工作流管理和支持起来可能具有挑战性。它们很难调试或排除故障,也很难被他人重用。
  • 参数化工作流通过基于用户定义的参数在运行时逐步初始化来达到平衡。这种方法为用户提供了极大的灵活性,可以在运行时控制执行,同时保持易于管理和理解。

正如我们在之前的 Maestro 博客文章中所描述的,参数支持使得创建复杂的参数化工作流成为可能,例如回填数据管道。

工作流执行模式
Maestro 提供了多个有用的构建块,使用户可以轻松定义数据流模式或其他工作流模式。它直接在 Maestro 引擎中提供对常见模式的支持。直接引擎支持不仅使我们能够优化这些模式,而且还确保了实现它们的一致方法。接下来,我们将讨论 Maestro 提供的三个主要构建块。

Foreach 支持
在 Maestro 中,foreach 模式被建模为原始工作流定义中的专用步骤。foreach 循环的每次迭代在内部被视为一个单独的工作流实例,该实例的扩展方式与任何其他 Maestro 工作流类似,基于在 foreach 定义块中定义的步骤执行(即子图)。foreach 步骤中的子图的执行被委托给单独的工作流实例。然后,Foreach 步骤监视并收集这些 foreach 工作流实例的状态,每个实例管理单个迭代的执行。有关更多详细信息,请参阅我们之前的 Maestro 博客文章。

foreach 模式经常用于重复运行具有不同参数的相同作业,例如数据回填或机器学习模型调整。要求用户在工作流定义中明确定义每次迭代(可能数十万次迭代)会非常繁琐且耗时。此外,如果 foreach 范围发生变化,用户将需要创建新的工作流,这进一步增加了流程的复杂性。

条件分支支持
条件分支功能允许仅在满足上游步骤中的特定条件时运行后续步骤。这些条件使用 SEL 表达式语言定义,并在运行时进行评估。结合其他构建块,用户可以构建强大的工作流程,例如,如果审计检查步骤失败,则进行一些补救,然后再次运行该作业。

子工作流支持
子工作流功能允许工作流步骤运行另一个工作流,从而实现跨多个工作流共享常用功能。这有效地实现了“工作流即功能”,并允许用户构建工作流图。例如,我们观察到复杂的工作流由数百个子工作流组成,用于处理数百个表中的数据,其中子工作流由多个团队提供。

这些模式可以组合在一起,为复杂的工作流用例构建复合模式。例如,我们可以循环遍历一组子工作流或运行嵌套的 foreach 循环。Maestro 用户开发的一个例子是自动恢复工作流,它利用条件分支和子工作流功能来处理错误并自动重试作业。

示例中:

  1. 子工作流“job1”运行另一个由提取-转换-加载 (ETL) 和审计作业组成的工作流。
  2. 接下来,状态检查作业利用 Maestro 参数和 SEL 支持来检索上一个作业的状态。
  3. 根据此状态,它可以决定是完成工作流还是运行恢复作业来解决任何数据问题。
  4. 解决问题后,它随后执行子工作流“job2”,该子工作流运行与子工作流“job1”相同的工作流。

步骤运行时和步骤参数
步骤运行时接口在 Maestro 中,我们使用步骤运行时来描述执行时的作业。步骤运行时接口定义了两条信息:

  1. 一组基本 API,用于控制执行运行时步骤实例的行为。

  2. 一些简单的数据结构来跟踪步骤运行时状态和执行结果。

Maestro 提供了几个步骤运行时实现,例如 foreach 步骤运行时、子工作流步骤运行时(上一节中提到)。每个实现都定义了自己的启动、执行和终止操作逻辑。在运行时,这些操作控制初始化步骤实例、执行业务逻辑以及在特定条件下(即用户手动干预)终止执行的方式。

此外,Maestro 步骤运行时会在内部跟踪运行时状态以及步骤的执行结果。运行时状态用于确定步骤的下一个状态转换,并判断步骤是否失败或终止。执行结果包含步骤工件和步骤执行历史记录的时间线,后续步骤可以访问这些工件和时间线。

步骤参数合并为了以动态方式控制步骤行为,Maestro 支持步骤运行时的运行时参数和标签注入。这使 Maestro 步骤在实际启动之前更灵活地吸收运行时更改(即覆盖的参数)。Maestro 内部维护一个步骤参数映射,该映射最初为空,并通过按以下顺序合并步骤参数进行更新:

  • 默认通用参数:参数合并从默认参数开始,这些参数通常每个步骤都应该具有。例如,workflow_instance_id、step_instance_uuid、step_attempt_id 和 step_id 是每个 maestro 步骤的必需参数。它们由 maestro 内部保留,用户无法传递。
  • 注入参数:Maestro 随后将注入参数(如果存在)合并到参数映射中。注入参数来自步骤运行时,这些参数是根据步骤架构动态生成的。每种类型的步骤都可以有自己的架构,并带有与此步骤相关的特定参数。步骤架构可以独立发展,无需更新 Maestro 代码。
  • 默认类型参数:注入运行时参数后,Maestro 会尝试合并与特定类型步骤相关的默认参数。例如,foreach 步骤具有 loop_params 和 loop_index 默认参数,这些参数由 maestro 内部设置,仅用于 foreach 步骤。
  • 工作流和步骤信息参数:这些参数包含有关步骤及其所属工作流的信息。这可以是身份信息,即workflow_id,如果存在,将合并到步骤参数映射中。
  • 未定义的新参数:启动或重新启动 Maestro 工作流实例时,用户可以指定初始步骤定义中不存在的新步骤参数。ParamsManager 会合并这些参数以确保它们在执行时可用。
  • 步骤定义参数:这些步骤参数由用户在定义时定义,如果它们不为空则合并。
  • 运行和重新启动参数:启动或重新启动 Maestro 工作流实例时,用户可以通过提供运行或重新启动参数来覆盖已定义的参数。这两种类型的参数在最后合并,以便步骤运行时可以看到最新且最准确的参数空间。

步骤依赖关系和信号
Maestro 执行工作流图中的步骤可以使用步骤依赖关系来表达执行依赖关系。步骤依赖关系指定步骤开始执行所需的数据相关条件。这些条件通常基于信号定义,信号是携带参数值等信息的消息片段,可以通过步骤输出或外部系统(如 SNS 或 Kafka 消息)发布。

Maestro 中的信号既服务于信号触发模式,也服务于信号依赖(发布者-订阅者)模式。一个步骤可以发布一个输出信号(示例),该信号可以解除对依赖于它的多个其他步骤的执行。信号定义包括映射参数列表,允许 Maestro 对字段子集执行“信号匹配”。此外,Maestro 还支持对信号参数值使用 <、> 等信号运算符。

Netflix 在信号概念的基础上构建了各种抽象。例如,ETL 工作流可以使用数据更新表并发送信号,以解锁依赖于该数据的下游工作流中的步骤。Maestro 支持“信号谱系”,允许用户浏览信号的所有历史实例以及与这些信号匹配(即发布或使用)的工作流步骤。信号触发可确保订阅信号或一组连接信号的工作流只执行一次。这种方法非常高效,因为它仅在满足信号中指定的条件时才执行工作流或步骤,从而节省资源。为这些高级抽象实现了信号服务。有关更多详细信息,请参阅Maestro 博客。

断点
Maestro 允许用户在工作流步骤上设置断点,其功能类似于 IDE 中的代码级断点。当工作流实例执行并到达带有断点的步骤时,该步骤将进入“暂停”状态。这会暂停工作流图的进程,直到用户手动从断点恢复。如果工作流步骤的多个实例在断点处暂停,则恢复一个实例只会影响该特定实例,而其他实例则处于暂停状态。删除断点将导致所有暂停的步骤实例恢复。

此功能在工作流程的初始开发期间特别有用,允许用户检查步骤执行和输出数据。当以“foreach”模式多次运行具有各种输入参数的步骤时,此功能也很有用。在步骤上设置单个断点将导致 foreach 循环的所有迭代在该步骤暂停以进行调试。此外,断点功能允许在工作流程执行期间进行人工干预,也可以用于其他目的,例如支持在工作流程运行时改变步骤状态。

时间线
Maestro 包含一个步骤执行时间线,可捕获所有重要事件,例如执行状态机更改及其背后的原因。此功能对于调试非常有用,可深入了解步骤的状态。例如,它记录“已创建”和“正在评估参数”等转换。此处包含一个时间线示例以供参考。实施的步骤运行时可以将时间线事件添加到时间线中,以向最终用户展示执行信息。

重试策略
Maestro 支持针对因失败而达到终止状态的步骤的重试策略。除了固定间隔重试之外,用户还可以指定重试次数并配置重试策略,包括重试之间的延迟和指数退避策略。Maestro 区分两种类型的重试:“平台”和“用户”。平台重试解决与用户逻辑无关的平台级错误,而用户重试则针对用户定义的条件。每种类型都可以有自己的一组重试策略。

自动重试有利于处理无需用户干预即可解决的瞬时错误。Maestro 可以灵活地将非幂等步骤的重试次数设置为零以避免重试。此功能可确保用户根据其特定需求控制如何管理重试。

聚合视图
由于工作流实例可以有多个运行,因此让用户看到工作流实例中所有步骤的聚合状态非常重要。聚合视图是通过将基本聚合视图与当前运行实例步骤状态合并而计算得出的。例如,如下图所示,模拟了一个简单案例,第一次运行,其中步骤 1 和步骤 2 成功,步骤 3 失败,步骤 4 和步骤 5 尚未启动。当用户重新启动运行时,运行从运行 2 中的步骤 3 开始,跳过上一次运行中成功的步骤 1 和步骤 2。所有步骤成功后,聚合视图将显示所有步骤的运行状态。

Rollup汇总
Rollup汇总 提供工作流实例的高级摘要,详细说明每个步骤的状态以及每个状态中的步骤数。它将当前实例和任何嵌套的非内联工作流(如子工作流或 foreach 步骤)中的步骤展平。例如,如果成功的工作流有三个步骤,其中一个是与五步工作流相对应的子工作流,则 Rollup 将显示七个步骤成功。只有叶步骤才会被计入 Rollup 中,因为其他步骤仅作为指向具体工作流的指针。

汇总还保留对任何未成功步骤的引用,提供步骤状态的清晰概览,并便于轻松导航到有问题的步骤,即使在嵌套工作流中也是如此。工作流实例的聚合汇总是通过将当前运行的运行时数据与基本汇总相结合来计算的。当前状态来自活动步骤的状态,包括 foreach 和子工作流步骤的聚合汇总。基本汇总是在工作流实例开始时建立的,包括上一次运行中不属于当前运行的内联步骤(不包括 foreach 和子工作流)的状态。

对于子工作流步骤,汇总仅反映子工作流实例的汇总。对于 foreach 步骤,汇总将 foreach 步骤的基本汇总与当前状态汇总相结合。基本汇总来自上一次运行的聚合汇总,不包括新运行中要重新启动的迭代。当前状态通过聚合正在运行的迭代的汇总定期更新,直到所有迭代都达到终止状态。

由于这些流程,汇总模型最终是一致的。虽然下图展示了汇总的一个简单示例,但计算可能会变得复杂且具有递归性,尤其是在多层嵌套 foreach 和子工作流的情况下。

Maestro 事件发布
当工作流定义、工作流实例或步骤实例发生更改时,Maestro 会生成一个事件,在内部进行处理,并将处理后的事件发布到外部系统。Maestro 有内部事件和外部事件。内部事件跟踪工作流、工作流实例或步骤实例生命周期内的变化。它被发布到内部队列并在 Maestro 内进行处理。内部事件处理后,其中一些将转换为外部事件并发送到外部队列(即 SNS、Kafka)。外部事件为下游服务携带 maestro 状态更改信息。

Maestro 事件处理器连接了前面提到的两个 Maestro 事件。它监听内部队列以获取已发布的内部事件。在处理器中,内部作业事件根据其类型进行处理,并在需要时转换为外部事件。最后的通知发布者发出外部事件,以便下游服务可以使用。

​​​​​​​下游服务大多是事件驱动的。Maestro 事件为下游服务提供了最有用的信息,以便捕获 Maestro 中的不同变化。一般来说,这些变化可以分为两类:工作流变化和实例状态变化。工作流变化事件与工作流级别的操作相关联,即工作流的定义或属性已发生更改。同时,实例状态变化跟踪工作流实例或步骤实例上的状态转换。

更多工作流:https://www.jdon.com/74867.html

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值