云原生工作流引擎 Argo Workflows 概述
Argo Workflows(以下简称 Argo) 是一款开源的云原生工作流引擎,专为实现 Kubernetes(以下简称 K8s) 环境下的并行作业编排与管理而设计。它允许用户通过声明式的方式来定义工作流程,这些流程不仅包括简单的顺序执行任务(步骤工作流),还能支持复杂的并行处理和条件分支逻辑(DAG 工作流)。
文章目录
一、引言
工作流是系统开发中频繁提及的核心概念,其在机器学习平台中扮演着串联建模、训练到预测全过程的角色,在 CI/CD
系统中则是连接开发、构建与部署环节的桥梁,同时在办公自动化领域中也广泛应用,以实现逐级审批等流程。因此,为项目引入一个既简单易用,又具备适当抽象层和扩展能力的工作流框架显得尤为重要。
Argo Workflows(以下简称 Argo
) 是一款开源的云原生工作流引擎,专为实现 Kubernetes(以下简称 K8s
) 环境下的并行作业编排与管理而设计。它允许用户通过声明式的方式来定义工作流程,这些流程不仅包括简单的顺序执行任务(步骤工作流),还能支持复杂的并行处理和条件分支逻辑(DAG
工作流)。借助 K8s
的资源管理机制,Argo
将工作流程的每个步骤作为独立的 Pod
进行调度和执行,确保与 K8s
生态系统的深度集成,并展现出卓越的可扩展性。Argo
现已晋升为 CNCF 的毕业项目,这一成就标志着其在云原生领域的显著地位和广泛认可。
本文旨在为您提供 Argo
的关键特性、核心概念、总体架构以及基本使用方法的全面概述。
二、关键特性
- 声明式工作流:
Argo
使用声明式YAML
文件来定义工作流程,这使得工作流程的定义、版本控制和复用变得极其简单。 - 可扩展性:通过自定义资源定义(
CRDs
),Argo
可以轻松扩展以支持新的工作流步骤类型。 - 并行执行:
Argo
支持并行执行工作流步骤,这可以显著提高工作流的效率。 - 条件逻辑:工作流可以包含条件逻辑,
Argo
允许根据前一步骤的结果动态选择执行路径。 - 参数化和模板:
Argo
支持参数化和模板,这使得创建可重用的工作流程变得更加容易。 - 持久化卷和输出参数:工作流步骤之间可以通过持久化卷和输出参数进行数据交换。
- 监控和日志记录:
Argo
提供了对工作流执行的监控和日志记录功能,使用户能够跟踪工作流的进度和状态。
三、核心概念
在使用 Argo
之前,我们需要先了解以下几个核心概念:
Workflow
:工作流是一个定义了一系列任务及其执行顺序的YAML
文件,其中工作流中的每个步骤都是一个Pod
,可以在K8s
集群上执行。Task
:任务是工作流中的单个步骤,通常由一个Pod
负责执行。每个任务都会定义一个容器镜像和相关的参数,以及该任务所需的其他配置。Template
:模板是工作流中可重用的任务定义。在模板中,可以定义任务的输入、输出、参数、容器镜像、命令等。Step
:步骤是工作流中的序列任务,它们按照定义的顺序依次执行。每个步骤可以引用一个任务模板或者直接定义一个内联任务。DAG
:DAG
是一种用于表示任务之间依赖关系的有向无环图。在Argo
中,可以使用DAG
来定义任务之间的并行和依赖关系。Arguments
:参数是工作流中的变量,可以在任务之间传递。参数可以用来动态地传递配置信息或者充当任务输出。Artifact
:Artifact
是任务输出的数据,可以是文件或者目录。Argo
支持在任务之间传递Artifact
,以便后续任务可以使用它们。Inputs/Outputs
:定义了任务的输入和输出数据。输入可以来自前一个任务的输出或者其他外部源,输出则可以被后续任务使用或者存储。Retries
:任务可以配置重试次数,如果任务失败,Argo
将尝试重新执行任务,直到达到最大重试次数或者任务成功。Resource Limits
:可以为任务设置资源限制,如CPU
、内存请求和限制,以确保任务在指定的资源约束下运行。
以下是一个简单的 Argo
工作流定义 YAML
文件:
# hello-world.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow # 新的 K8s 规格
metadata:
generateName: hello-world- # 工作流规格名称(后期运行时将在其后自动填充 5 位随机字符)
spec:
entrypoint: whalesay # 工作流入口,调用 whalesay 这个模板
templates:
- name: whalesay # 定义模板
container:
image: docker/whalesay
command: [ cowsay ]
args: [ "hello world" ]
resources: # 资源限制声明
limits:
memory: 32Mi
cpu: 100m
四、总体架构
如上图所示,可以看出 Argo
总体架构由以下几个核心组件组成:
Argo CLI
:命令行工具,可以更加简洁方便地对Workflow
实例进行创建、查看、删除等操作。Argo UI
:提供Web GUI
页面工具及一些运维工具,以可视化的方式观测Workflow
实例的运行动态。Workflow Controller
:Workflow
调度与生命周期管理控制器,负责监视Workflow CR
的创建、更新与删除,并控制Workflow
实例及每个实例下节点的运行。Workflow CRD
:Workflow
资源描述文件,文件定义了Workflow
内各执行节点的依赖关系、串/并行行为、入参出参、退出回调等。
总的来说,Argo UI
和 Argo CLI
作为客户端为用户提供了更便捷的访问入口,而 Workflow CRD
和 Workflow Controller
作为服务端提供了控制 Workflow
实例调度、运转与管理的能力,最终 Workflow
内每个节点将作为核心的业务执行单元,以 K8s
原生 Pod
的形式运行。
五、基本使用
5.1 安装 Argo
在安装
Argo
之前,请确保您拥有一套K8
集群环境:
K8s
安装指南官方地址:https://kubernetes.io/zh-cn/docs/tasks/tools/Argo
安装指南官方地址:https://github.com/argoproj/argo-workflows/releases/tag/v3.5.5
-
安装
Argo Server
# 创建 argo 命名空间 kubectl create namespace argo # 应用配置文件 kubectl apply -n argo -f https://github.com/argoproj/argo-workflows/releases/download/v3.5.5/quick-start-minimal.yaml # 验证 Argo Pod 部署,部署成功将产生 argo-server-* 和 workflow-controller-* 两个 Pod [root@k8s-master-100 argo]# kubectl get pod -n argo NAME READY STATUS RESTARTS AGE argo-server-5cc5664fdb-9dns5 1/1 Running 0 27s workflow-controller-8795b75df-68s5l 1/1 Running 0 27s
-
安装
Argo CLI
# 下载二进制文件 curl -sLO https://github.com/argoproj/argo-workflows/releases/download/v3.5.5/argo-linux-amd64.gz # 解压 gunzip argo-linux-amd64.gz # 赋予可执行权限 chmod +x argo-linux-amd64 # 移动可执行文件到 bin 目录下 mv ./argo-linux-amd64 /usr/local/bin/argo # 验证版本 [root@k8s-master-100 argo]# argo version argo: v3.5.5 BuildDate: 2024-02-29T21:37:24Z GitCommit: c80b2e91ebd7e7f604e88442f45ec630380effa0 GitTreeState: clean GitTag: v3.5.5 GoVersion: go1.21.7 Compiler: gc Platform: linux/amd64
-
安装
Argo UI
# 非生产环境为方便学习、体验使用,设置 argo-server 无需认证登录 kubectl patch deployment \ argo-server \ --namespace argo \ --type='json' \ -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/args", "value": [ "server", "--auth-mode=server" ]}]' # 将 argo-server 端口暴露类型修改为 NodePort 方便访问 kubectl patch svc argo-server -n argo -p '{"spec": {"type": "NodePort"}}' # 查看服务暴露的端口,此处我的为 31469 [root@k8s-master-100 argo]# kubectl get svc -n argo NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE argo-server NodePort 10.96.169.131 <none> 2746:31469/TCP 80s # 物理机浏览器通过 https://K8sMasterIp:31469 访问,此处我的 K8s 主节点 IP 为 192.168.192.100,请注意务必使用 https 协议进行访问 https://192.168.192.100:31469
5.2 Hello World
下面将基于
Argo
构建国际知名程序Hello World
,使用Argo
通常涉及以下步骤:
定义工作流:使用
YAML
文件定义工作流,包括步骤、依赖关系和参数。提交工作流:通过
Argo CLI
或UI
将工作流提交到K8s
集群。执行工作流:
Workflow Controller
将工作流步骤作为Pod
调度到K8s
集群中执行。监控工作流:通过
Argo UI
或CLI
监控工作流的执行状态。查看输出:工作流完成后,可以查看输出参数和日志。
-
Argo CLI
命令行工具方式:定义工作流:
vim hello-world.yaml
apiVersion: argoproj.io/v1alpha1 kind: Workflow # 新的 k8s 规格 metadata: generateName: hello-world- # 工作流规格名称(后期运行时将在其后自动填充 5 位随机字符) spec: entrypoint: whalesay # 工作流入口,调用 whalesay 这个模板 templates: - name: whalesay # 定义模板 container: image: docker/whalesay command: [ cowsay ] args: [ "hello world" ] resources: # 资源限制声明 limits: memory: 32Mi cpu: 100m
# 提交工作流 [root@k8s-master-100 argo]# argo submit hello-world.yaml Name: hello-world-msmtq Namespace: default ServiceAccount: unset (will run with the default ServiceAccount) Status: Pending Created: Mon Jun 24 18:59:21 +0800 (now) Progress: # 列出默认命名空间下的所有工作流 [root@k8s-master-100 argo]# argo list NAME STATUS AGE DURATION PRIORITY MESSAGE hello-world-msmtq Running 16s 16s 0 # 查看工作流执行日志 [root@k8s-master-100 argo]# argo logs hello-world-msmtq hello-world-msmtq: time="2024-06-24T10:59:40.603Z" level=info msg="capturing logs" argo=true hello-world-msmtq: _____________ hello-world-msmtq: < hello world > hello-world-msmtq: ------------- hello-world-msmtq: \ hello-world-msmtq: \ hello-world-msmtq: \ hello-world-msmtq: ## . hello-world-msmtq: ## ## ## == hello-world-msmtq: ## ## ## ## === hello-world-msmtq: /""""""""""""""""___/ === hello-world-msmtq: ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~ hello-world-msmtq: \______ o __/ hello-world-msmtq: \ \ __/ hello-world-msmtq: \____\______/ hello-world-msmtq: time="2024-06-24T10:59:41.605Z" level=info msg="sub-process exited" argo=true error="<nil>" # 删除工作流 argo delete hello-world-msmtq
-
Argo UI
可视化界面方式:提交新的工作流:
编辑工作流模板,内容同
hello-world.yaml
:点击
Logs
按钮查看执行日志:
5.3 经典案例
5.3.1 参数(Parameters)
下面,让我们一起看一个稍微复杂一点的带有参数的工作流规范。
# goodbye-world.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: hello-world-parameters-
spec:
# 调用名为 whalesay 的工作流模板并且传递名为 message 值为 hello world 的参数
entrypoint: whalesay
arguments:
parameters:
- name: message
value: hello world
templates:
- name: whalesay
inputs:
parameters:
- name: message # 模板参数声明
container:
# 运行 cowsay 命令,并且携带 inputs.parameters.message 参数
image: docker/whalesay
command: [cowsay]
args: ["{{inputs.parameters.message}}"] ## 参数需要用 {{}} 进行包裹,以转义 yaml 中的花括号
-
通过
-p
选项传参:Argo
控制台提供了更加方便的方式传递参数# 动态传参 argo submit goodbye-world.yaml -p message="goodbye world" # 此时运行结果将打印 "goodbye world" 而非 “hello world" [root@k8s-master-100 argo]# argo logs hello-world-parameters-4ccp2 hello-world-parameters-4ccp2: time="2024-06-24T11:21:40.165Z" level=info msg="capturing logs" argo=true hello-world-parameters-4ccp2: _______________ hello-world-parameters-4ccp2: < goodbye world > hello-world-parameters-4ccp2: --------------- hello-world-parameters-4ccp2: \ hello-world-parameters-4ccp2: \ hello-world-parameters-4ccp2: \ hello-world-parameters-4ccp2: ## . hello-world-parameters-4ccp2: ## ## ## == hello-world-parameters-4ccp2: ## ## ## ## === hello-world-parameters-4ccp2: /""""""""""""""""___/ === hello-world-parameters-4ccp2: ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~ hello-world-parameters-4ccp2: \______ o __/ hello-world-parameters-4ccp2: \ \ __/ hello-world-parameters-4ccp2: \____\______/ hello-world-parameters-4ccp2: time="2024-06-24T11:21:41.167Z" level=info msg="sub-process exited" argo=true error="<nil>"
-
通过
yaml
文件方式传参:如果需要覆写yaml
文件中定义的多个参数,Argo
控制台提交工作流时也支持通过文件的方式进行传参。通过文件覆写参数进行传参,适用参数很多的情况,更加灵活和方便# params.yaml message: goodbye world # 提交工作流 argo submit goodbye-world.yaml --parameter-file params.yaml
5.3.2 步骤(Steps)
在这个例子中,我们将看到如何创建多步骤工作流,如何在工作流规范中定义多个模板,以及如何创建嵌套工作流。
# steps.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: steps-
spec:
entrypoint: hello-hello-hello
# 工作流模板一:hello-hello-hello
templates:
- name: hello-hello-hello
# 定义多步骤工作流
steps:
- - name: hello1 # hello1 最先执行(外层串行、内层并行)
template: whalesay
arguments:
parameters:
- name: message
value: "hello1"
- - name: hello2a # 在 hello1 后执行
template: whalesay
arguments:
parameters:
- name: message
value: "hello2a"
- name: hello2b # 与 hello02a 并发执行
template: whalesay
arguments:
parameters:
- name: message
value: "hello2b"
# 工作流模板二:whalesay
- name: whalesay
inputs:
parameters:
- name: message
container:
image: docker/whalesay
command: [cowsay]
args: ["{{inputs.parameters.message}}"]
# 提交工作流并查看实时结果
argo submit steps.yaml --watch
5.3.3 有向无环图(DAG)
作为指定步骤序列的替代方案,您可以通过指定每个任务的依赖关系来将工作流定义为有向无环图(DAG
)。对于复杂的工作流程,DAG
可能更易于维护,并且在运行任务时允许实现最大程度的并行性。
# dag-diamond.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: dag-diamond-
spec:
entrypoint: diamond
templates:
- name: echo
inputs:
parameters:
- name: message
container:
image: alpine:3.7
command: [echo, "{{inputs.parameters.message}}"]
- name: diamond
dag:
tasks:
- name: A # 最先执行
template: echo
arguments:
parameters: [{name: message, value: A}]
- name: B # 在 A 后执行
dependencies: [A]
template: echo
arguments:
parameters: [{name: message, value: B}]
- name: C # 在 A 后与 B 并行执行
dependencies: [A]
template: echo
arguments:
parameters: [{name: message, value: C}]
- name: D # B && C 执行完成后执行
dependencies: [B, C]
template: echo
arguments:
parameters: [{name: message, value: D}]
依赖树可能有多个根,从 DAG
(有向无环图)或步骤模板调用的模板自身也可以是 DAG
或步骤模板,从而支持将复杂的工作流程拆分为可管理的部分。
# dag-multiroot.yaml
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: dag-multiroot-
spec:
entrypoint: multiroot
templates:
- name: echo
inputs:
parameters:
- name: message
container:
image: alpine:3.7
command: [echo, "{{inputs.parameters.message}}"]
- name: multiroot
dag:
tasks:
- name: A
template: echo
arguments:
parameters: [{name: message, value: A}]
- name: B
template: echo
arguments:
parameters: [{name: message, value: B}]
- name: C
depends: "A"
template: echo
arguments:
parameters: [{name: message, value: C}]
- name: D
depends: "A && B"
template: echo
arguments:
parameters: [{name: message, value: D}]
失败优先原则:
- 默认情况下,当一个任务失败后新的任务不会再被调度,当所有正在运行的任务完成后,
DAG
任务状态将会被标记为失败; - 通过将
failFast
设置为false
后,所有的分支任务都会运行,不管其它分支是成功或失败。
加强版 Depends(v2.9):
在 2.8
版本之前,DAG
模板中指定依赖关系的唯一方式是使用 dependencies
字段并指定当前任务所依赖的其他任务列表。这种语法存在局限性,因为它不允许用户指定具体依赖于任务的哪个结果。例如,一个任务可能仅在所依赖任务成功(或失败等)时才运行。
v2.9
及以后的版本通过新的关键字 depends
来解决此问题,它允许用户指定依赖的任务、它们的状态以及任何复杂的布尔逻辑。该字段是一个字符串字段,语法格式为 <task-name>.<task-result>
。
任务结果 | 含义 |
---|---|
.Succeeded | 成功,无错误 |
.Failed | 失败,以非 0 码退出 |
.Errored | 错误,不表现为程序以非 0 退出码结束 |
.Skipped | 跳过 |
.Omitted | 忽略 |
.Daemoned | 任务已作为守护进程启动,并且当前不在等待状态 |
为了书写方便,通常使用 task
等价表示 (task.Succeeded || task.Skipped || task.Daemoned)
,例如:
depends: "task || task-2.Failed"
# 上下等价
depends: "(task.Succeeded || task.Skipped || task.Daemoned) || task-2.Failed"
# 任意一个成功或全部失败
depends: "task-1.AnySucceeded || task-2.AllFailed"
dependencies: ["A", "B", "C"]
# 上下等价
depends: "A && B && C"
六、结语
Argo
是一款功能强大的云原生工作流引擎,它为 K8s
用户提供了简洁而强大的工具,以便定义和执行复杂的工作流程。得益于其声明式的工作流定义、高度的可扩展性以及与 K8s
的深度集成,Argo
成为了自动化云原生应用程序工作流程的绝佳工具。无论是在持续集成/持续部署(CI/CD
)流程中,还是在数据处理和机器学习工作流程中,Argo
都能大显身手,助力用户提升效率和实现自动化。
通过本文的介绍,您已经对 Argo
的关键概念、架构和基础使用方法有了初步了解。接下来,您可以尝试将 Argo
应用于实际项目中,以此简化工作流程的管理和自动化过程。
更多推荐
所有评论(0)