深入探索云原生流水线的架构设计

简介: 目前,市面上的流水线/工作流产品层出不穷,有没有一款工作流引擎,能够同时满足各种繁复的需求?一起来看!

目前,市面上的流水线/工作流产品层出不穷,有没有一款工作流引擎,能够同时满足:

  • 支持各种任务运行时,包括 K8s Job、K8s Flink、K8s Spark、DC/OS Job、Docker、InMemory 等?
  • 支持快速对接其他任务运行时?
  • 支持任务逻辑抽象,并且快速地开发自己的 Action?
  • 支持嵌套流水线,在流水线层面进行逻辑复用?
  • 支持灵活的上下文参数传递,有好用的 UI 以及简单明确的工作流定义?
  • ······

那么,不妨试试 Erda Pipeline 吧~

Erda Pipeline 是一款自研、用 Go 编写的工作流引擎。作为基础服务,它在 Erda 内部支撑了许多产品:

  • CI/CD
  • 快数据平台
  • 自动化测试平台
  • SRE 运维链路
  • ……

Erda Pipeline 之所以选择自研,其中最重要的三点是:

  1. 自研能更快地响应业务需求,进行定制化开发;
  2. 时至今日,开源社区还没有一个实质上的流水线标准,各种产品百花齐放;
  3. K8s、DC/OS 等的 Job 实现都偏弱、上下文传递缺失,无法满足我们的需求,更不用说灵活好用的 Flow 了,例如:嵌套流水线。

本文我们将主要从架构层面对 Pipeline 进行剖析,和大家一起来深入探索 Pipeline 的架构设计。主要内容包括以下几个部分:

  • 整体架构
  • 内部架构
  • 水平扩展
  • 分布式架构
  • 功能特性
  • 实现细节

整体架构

image.png

Pipeline 支持灵活的使用方式,目前支持 UI 可视化操作、OPENAPI 开放接口、CLI 命令行工具几种方式。

协议层面,在 Erda-Infra 微服务框架的加持下,以 HTTP 和 gRPC 形式对外提供服务:在早期的时候,我们只提供了 HTTP 服务,由于 Erda 平台本身内部是微服务架构,服务间调用就需要手动编写 HTTP 客户端,不好自动生成,麻烦且容易出错。后来我们改为使用 Protobuf 作为 IDL(Interface Define Language),在 Erda-Infra 中自动生成 gRPC 的客户端调用代码和服务端框架代码,内部服务间的调用都改为使用 gRPC 调用。

在中间件依赖层面,我们使用 ETCD 做分布式协调,用 MySQL 做数据持久化。ETCD 我们也有计划把它替换掉,使用 MySQL 来做分布式协调。

最关键的任务运行时(Task Runtime)层面,我们支持任务可以运行在 K8s、DC/OS(分布式云 OS,在 2017-2019 年非常火)、用户本地 Docker 环境等。

我们还有开放的任务扩展市场,在平台级别内置了非常多的流水线任务扩展,开箱即用。同时,用户也可以开发企业/项目/应用/个人级别的任务扩展,这部分功能在代码层面已经完全支持,产品层面正在开发中,在后续迭代中很快就可以和大家见面。

内部架构

image.png

使用 Erda-Infra 微服务框架开发,功能模块划分清晰:

  • Server 层包括业务逻辑,对 Pipeline 来说业务就是创建、执行、重试、重试失败节点等。
  • Modules 层提供不含业务逻辑的公共模块,其余两层均可调用,包括预校验机制、定时守护进程、YAML 解析器、配置管理、事件管理等。

Erda Pipeline 一直在践行:“把复杂留给自己,把简单留给别人”。

在 Pipeline 中,我们对一个任务执行的抽象是 ActionExecutor。

  • Engine 层负责流水线的推进,包括:

    • Queue Manager 队列管理器,支持队列内工作流的优先级动态调整、资源检查、依赖检查等
    • Dispatcher 任务分发器,用于将满足出队条件的流水线分发给合适的 Worker 进行推进
    • Reconciler 协调器,负责将一条完整的流水线解析为 DAG 结构后进行推进,直至终态。

  • 模块内部使用插件机制,对接各种任务运行时。
  • AOP 扩展点机制(借鉴 Spring),把代码关键节点进行暴露,方便开发同学在不修改核心代码的前提下定制流水线行为。

    • 目前已经有的一些扩展点插件,譬如自动化测试报告嵌套生成、队列弹出前检查、接口测试自动登录保持等。
    • 这个能力后续我们还会开放给调用方,包括用户,去做一些有意思的事情。
  • 统一使用 Event,封装了 WebHook / WebSocket / Metrics。

水平扩展

image.png

随着 Pipeline 需要支撑的业务场景和数据量与日俱增,单实例的 Pipeline 稳定性和推进性能面临着挑战。

Pipeline 的多实例方案如下:

  • Leader & Worker 模式,两者在部署上不区分状态,仅为 Replicas 多实例:

    • 使用 ETCD 选举,每个实例都可以是 Leader。
    • 只有 Leader 开启 Queue Manager 和 Dispatcher,分发任务给 Worker。
    • Leader 本身也可以作为 Worker,支持单节点部署模式,Leader 必须开启 Reconciler。
  • Dispatch 使用有界负载的一致性哈希算法:

    • 使用一致性哈希来分配任务。但一致性哈希会超载,例如某些热点内容会持续打到一个节点上。
    • 有界负载算法(The bounded-load algorithm):

      1. 只要服务器未过载,请求的分配策略与一致性哈希相同。
      2. 过载服务器的溢出负载将在可用服务器之间分配。
    • Pipeline 实例增减时,已经被分配的流水线不重新分配,尽可能减少切换成本,防止重复推进;新增的流水线使用一致性 Hash 算法进行分配。

在之前,我们使用过分布式锁的方案来做多实例协调。和选举比起来,竞争会更大,在具体实现里,如果想要多实例同时推进流水线,那竞争的资源就是流水线 ID。每个 ID 会请求一个分布式锁,而且这个分布式锁是阻塞性的,保证每个流水线 ID 一定会被推进,天然做了多节点重试。

分布式架构

image.png

该分布式架构是典型的 AP 模型,数据层面遵循最终一致性。

这套分布式架构的核心目标(典型场景)是在网络分区的情况下,保证边缘集群的定时任务正常执行。

我们来对比一下原有部署架构(运行时以 K8s 为例):

  • 中心 Pipeline 直接负责流程推进,调用边缘 K8s 创建 Job。
  • 当网络分区时,原有部署架构下,定时任务无法正常执行。

分布式架构

  • 中心下发任务定义,由 Edge Pipeline 负责推进,直连 K8s,更加稳定。
  • 在网络分区恢复时,主动上报执行数据,实现数据最终一致性。

在代码层面,我们使用同一份代码构建出同一个镜像,通过配置(不同的部署模式)使得各个实例各司其职。

另外,在数据同步时,我们遇到了前端 JavaScript Number 类型 53位最大值问题,对 SnowFlake ID 进行了 64bit -> 53bit 的改造,在保证唯一性的前提下,尽可能地增加可用性(生命周期约为 10 年,同时支持 4096 个分布式节点,每 10ms 可生成 64 个 ID)。

功能特性

这里简单列举一些比较常见的功能特性:

  • 配置即代码
  • 扩展市场丰富
  • 可视化编辑
  • 支持嵌套流水线
  • 灵活的执行策略,支持 OnPush / OnMerge 等触发策略
  • 支持工作流优先队列
  • 多维度的重试机制
  • 定时流水线及定时补偿功能
  • 动态配置,支持“值”和“文件”两种类型,均支持加密存储,确保数据安全性
  • 上下文传递,后置任务可以引用前置任务的“值”和“文件”
  • 开放的 OpenAPI 接口,方便第三方系统快速接入

一些实现细节

如何实现上下文传递(值引用)

在一条流水线中,节点间除了有依赖顺序之外,一定会有数据传递的需求。

值引用:

  • 每个节点的特殊输出(按格式写入指定文件或者打印到标准输出)会被保存在 Pipeline 数据库中;
  • 后续节点通过 outputs 语法声明的表达式会在节点开始执行前被替换为真正的值。

举例:

image.png

如图所示:第一个节点 repo 拉取代码;第二个节点 build erda 则是构建 Erda 项目。

在例子中,第二步构建时同时用到了 “值引用” 和 “文件引用” 两种引用类型,是进依次入代码仓库,指定 GIT_COMMIT 进行构建。

image.png

如何实现上下文传递(文件引用)

  • 文件引用比值引用复杂,因为文件的数据量比值大得多,不能存储在数据库中,而是存储在卷中。
  • 这里又根据是否使用共享存储而分为两种情况,两者的区别在于申请的卷的类型和个数。
  • 对于流水线使用者而言,没有任何区别。

image.png
使用共享存储

image.png
不使用共享存储

如何实现缓存加速

在许多流水线场景中,同一条流水线的多次执行之间是有关联的。如果能够用到上一次的执行结果,则可以大幅缩短执行时间。

典型场景是 CI/CD 构建,我们以 Java 应用 Maven 构建举例:不但同一条流水线不同的多次执行可以复用 ${HOME}/.m2 目录(缓存目录),甚至同一个应用下的多个分支之间都可以使用同一个缓存目录,就像本地构建一样~

举例:

image.png

仍然使用前面的例子,在第二步 build erda 里加上 cache 即可。

image.png

Action Agent

在 Pipeline 里,每个节点都会对应一个 Action 类型,并且在扩展市场中都可以找到。为了更加方便 Action 开发者进行开发,我们提供了很多便捷的机制。如:

  • 对敏感日志进行脱敏处理,保证数据安全
  • 无感知的错误分析和数据上报
  • 文件变动监听及实时上报
  • ……

上述所有机制都是由 Action Agent 程序完成的,它是一个静态编译的 Go 程序,可以运行在任意 Action 镜像中。Agent 完整的执行链路如下:

image.png

Action Executor 扩展机制

Pipeline 之所以好用,是因为它提供了灵活一致的流程编排能力,并且可以很方便地对接其他单任务执行平台,这个平台本身不需要有流程编排的能力。

在 Pipeline 中,我们对一个任务执行的抽象是 ActionExecutor。一个执行器只要实现单个任务的创建、启动、更新、状态查询、删除等基础方法,就可以注册成为一个 ActionExecutor:

  • 恰当的任务执行器抽象,使得 Batch/Streaming/InMemory Job 的配置方式和使用方式完全一致,流批一体,对使用者屏蔽底层细节,做到无感知切换。
  • 在同一条流水线中,可以混用各种 ActionExecutor。

调度时,根据任务类型智能调度到对应的任务执行器上,包括 K8sJob、Metronome Job、Flink Job、Spark Job 等等。

image.png
Go 接口定义

image.png

AOP 扩展点机制

AOP 扩展点机制是借鉴 Java 里 Spring 的概念应运而生的。

我们把代码关键节点进行暴露,方便开发同学在不修改核心代码的前提下定制流水线行为。AOP 扩展点机制已经使用 Erda Infra 的模块化思想重构,整个扩展点的插件开发和编排更为灵活,如下图所示:

image.png
AOP 在 Pipeline 内部的使用

这个能力后续我们还会开放给用户,让用户可以在 pipeline.yaml 中使用编程语言声明和编排扩展点插件,更灵活地控制流水线行为。


我们致力于决社区用户在实际生产环境中反馈的问题和需求,
如果您有任何疑问或建议,
欢迎关注【尔达Erda】公众号给我们留言,
加入 Erda 用户群参与交流或在 Github 上与我们讨论!

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
23天前
|
运维 Cloud Native 持续交付
深入理解云原生架构及其在现代企业中的应用
随着数字化转型的浪潮席卷全球,企业正面临着前所未有的挑战与机遇。云计算技术的迅猛发展,特别是云原生架构的兴起,正在重塑企业的IT基础设施和软件开发模式。本文将深入探讨云原生的核心概念、关键技术以及如何在企业中实施云原生策略,以实现更高效的资源利用和更快的市场响应速度。通过分析云原生架构的优势和面临的挑战,我们将揭示它如何助力企业在激烈的市场竞争中保持领先地位。
|
21天前
|
Kubernetes Cloud Native 微服务
探索云原生技术:容器化与微服务架构的融合之旅
本文将带领读者深入了解云原生技术的核心概念,特别是容器化和微服务架构如何相辅相成,共同构建现代软件系统。我们将通过实际代码示例,探讨如何在云平台上部署和管理微服务,以及如何使用容器编排工具来自动化这一过程。文章旨在为开发者和技术决策者提供实用的指导,帮助他们在云原生时代中更好地设计、部署和维护应用。
|
1月前
|
Cloud Native Devops 云计算
云计算的未来:云原生架构与微服务的革命####
【10月更文挑战第21天】 随着企业数字化转型的加速,云原生技术正迅速成为IT行业的新宠。本文深入探讨了云原生架构的核心理念、关键技术如容器化和微服务的优势,以及如何通过这些技术实现高效、灵活且可扩展的现代应用开发。我们将揭示云原生如何重塑软件开发流程,提升业务敏捷性,并探索其对企业IT架构的深远影响。 ####
43 3
|
1月前
|
Cloud Native 持续交付 云计算
云原生架构的演进与挑战
随着云计算技术的不断发展,云原生架构已成为企业数字化转型的重要支撑。本文深入探讨了云原生架构的概念、发展历程、核心技术以及面临的挑战,旨在为读者提供一个全面了解云原生架构的视角。通过分析Kubernetes、Docker等关键技术的应用,以及微服务、持续集成/持续部署(CI/CD)等实践案例,本文揭示了云原生架构在提高应用开发效率、降低运维成本、增强系统可扩展性等方面的显著优势。同时,也指出了云原生架构在安全性、复杂性管理等方面所面临的挑战,并提出了相应的解决策略。
|
21天前
|
运维 Cloud Native 持续交付
云原生技术深度探索:重塑现代IT架构的无形之力####
本文深入剖析了云原生技术的核心概念、关键技术组件及其对现代IT架构变革的深远影响。通过实例解析,揭示云原生如何促进企业实现敏捷开发、弹性伸缩与成本优化,为数字化转型提供强有力的技术支撑。不同于传统综述,本摘要直接聚焦于云原生技术的价值本质,旨在为读者构建一个宏观且具体的技术蓝图。 ####
|
24天前
|
弹性计算 运维 Cloud Native
云原生架构的崛起与未来展望
在数字化转型的浪潮中,云原生架构凭借其高效、灵活和可扩展的特性,正逐渐成为企业IT战略的核心。本文旨在探讨云原生架构的定义、关键特性、实施优势以及面临的挑战,同时展望未来的发展趋势。通过深入分析,我们期望为读者提供一个关于云原生架构全面而深入的视角,助力企业在云计算时代做出更明智的决策。
32 3
|
25天前
|
Cloud Native API 持续交付
云原生时代的微服务架构设计
随着云计算的蓬勃发展,云原生概念逐渐成为IT行业的热点。本文将通过深入浅出的方式,介绍在云原生环境下,如何设计一个高效、可扩展的微服务架构。文章不仅涉及理论概念,还将结合实际代码示例,帮助读者理解微服务架构的核心要素和设计原则,以及如何在云平台上实现这些设计。
|
1月前
|
Kubernetes Cloud Native Docker
云原生之旅:从传统架构到容器化服务的演变
随着技术的快速发展,云计算已经从简单的虚拟化服务演进到了更加灵活和高效的云原生时代。本文将带你了解云原生的概念、优势以及如何通过容器化技术实现应用的快速部署和扩展。我们将以一个简单的Python Web应用为例,展示如何利用Docker容器进行打包和部署,进而探索Kubernetes如何管理这些容器,确保服务的高可用性和弹性伸缩。
|
28天前
|
Cloud Native 持续交付 云计算
云原生技术在现代IT架构中的转型力量####
本文深入剖析了云原生技术的精髓,探讨其在现代IT架构转型中的关键作用与实践路径。通过具体案例分析,展示了云原生如何赋能企业实现更高效的资源利用、更快的迭代速度以及更强的系统稳定性,为读者提供了一套可借鉴的实施框架与策略。 ####
24 0
|
1月前
|
消息中间件 运维 Cloud Native
云原生架构下的微服务优化策略####
本文深入探讨了云原生环境下微服务架构的优化路径,针对服务拆分、通信效率、资源管理及自动化运维等核心环节提出了具体的优化策略。通过案例分析与最佳实践分享,旨在为开发者提供一套系统性的解决方案,以应对日益复杂的业务需求和快速变化的技术挑战,助力企业在云端实现更高效、更稳定的服务部署与运营。 ####