Spring-_-Bear 的 CSDN 博客导航

一、引言

工作流是系统开发中频繁提及的核心概念,其在机器学习平台中扮演着串联建模、训练到预测全过程的角色,在 CI/CD 系统中则是连接开发、构建与部署环节的桥梁,同时在办公自动化领域中也广泛应用,以实现逐级审批等流程。因此,为项目引入一个既简单易用,又具备适当抽象层和扩展能力的工作流框架显得尤为重要。

Argo Workflows(以下简称 Argo) 是一款开源的云原生工作流引擎,专为实现 Kubernetes(以下简称 K8s) 环境下的并行作业编排与管理而设计。它允许用户通过声明式的方式来定义工作流程,这些流程不仅包括简单的顺序执行任务(步骤工作流),还能支持复杂的并行处理和条件分支逻辑(DAG 工作流)。借助 K8s 的资源管理机制,Argo 将工作流程的每个步骤作为独立的 Pod 进行调度和执行,确保与 K8s 生态系统的深度集成,并展现出卓越的可扩展性。Argo 现已晋升为 CNCF 的毕业项目,这一成就标志着其在云原生领域的显著地位和广泛认可。

本文旨在为您提供 Argo 的关键特性、核心概念、总体架构以及基本使用方法的全面概述。

二、关键特性

  1. 声明式工作流:Argo 使用声明式 YAML 文件来定义工作流程,这使得工作流程的定义、版本控制和复用变得极其简单。
  2. 可扩展性:通过自定义资源定义(CRDs),Argo 可以轻松扩展以支持新的工作流步骤类型。
  3. 并行执行:Argo 支持并行执行工作流步骤,这可以显著提高工作流的效率。
  4. 条件逻辑:工作流可以包含条件逻辑,Argo 允许根据前一步骤的结果动态选择执行路径。
  5. 参数化和模板:Argo 支持参数化和模板,这使得创建可重用的工作流程变得更加容易。
  6. 持久化卷和输出参数:工作流步骤之间可以通过持久化卷和输出参数进行数据交换。
  7. 监控和日志记录:Argo 提供了对工作流执行的监控和日志记录功能,使用户能够跟踪工作流的进度和状态。

三、核心概念

在使用 Argo 之前,我们需要先了解以下几个核心概念:

  1. Workflow:工作流是一个定义了一系列任务及其执行顺序的 YAML 文件,其中工作流中的每个步骤都是一个 Pod,可以在 K8s 集群上执行。
  2. Task:任务是工作流中的单个步骤,通常由一个 Pod 负责执行。每个任务都会定义一个容器镜像和相关的参数,以及该任务所需的其他配置。
  3. Template:模板是工作流中可重用的任务定义。在模板中,可以定义任务的输入、输出、参数、容器镜像、命令等。
  4. Step:步骤是工作流中的序列任务,它们按照定义的顺序依次执行。每个步骤可以引用一个任务模板或者直接定义一个内联任务。
  5. DAGDAG 是一种用于表示任务之间依赖关系的有向无环图。在 Argo 中,可以使用 DAG 来定义任务之间的并行和依赖关系。
  6. Arguments:参数是工作流中的变量,可以在任务之间传递。参数可以用来动态地传递配置信息或者充当任务输出。
  7. ArtifactArtifact 是任务输出的数据,可以是文件或者目录。Argo 支持在任务之间传递 Artifact,以便后续任务可以使用它们。
  8. Inputs/Outputs:定义了任务的输入和输出数据。输入可以来自前一个任务的输出或者其他外部源,输出则可以被后续任务使用或者存储。
  9. Retries:任务可以配置重试次数,如果任务失败,Argo 将尝试重新执行任务,直到达到最大重试次数或者任务成功。
  10. 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 总体架构由以下几个核心组件组成:

  1. Argo CLI:命令行工具,可以更加简洁方便地对 Workflow 实例进行创建、查看、删除等操作。
  2. Argo UI:提供 Web GUI 页面工具及一些运维工具,以可视化的方式观测 Workflow 实例的运行动态。
  3. Workflow ControllerWorkflow 调度与生命周期管理控制器,负责监视 Workflow CR 的创建、更新与删除,并控制 Workflow 实例及每个实例下节点的运行。
  4. Workflow CRDWorkflow 资源描述文件,文件定义了 Workflow 内各执行节点的依赖关系、串/并行行为、入参出参、退出回调等。

总的来说,Argo UIArgo CLI 作为客户端为用户提供了更便捷的访问入口,而 Workflow CRDWorkflow Controller 作为服务端提供了控制 Workflow 实例调度、运转与管理的能力,最终 Workflow 内每个节点将作为核心的业务执行单元,以 K8s 原生 Pod 的形式运行。

五、基本使用

5.1 安装 Argo

在安装 Argo 之前,请确保您拥有一套 K8 集群环境:

  1. 安装 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
    
  2. 安装 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
    
  3. 安装 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 通常涉及以下步骤:

  1. 定义工作流:使用 YAML 文件定义工作流,包括步骤、依赖关系和参数。

  2. 提交工作流:通过 Argo CLIUI 将工作流提交到 K8s 集群。

  3. 执行工作流:Workflow Controller 将工作流步骤作为 Pod 调度到 K8s 集群中执行。

  4. 监控工作流:通过 Argo UICLI 监控工作流的执行状态。

  5. 查看输出:工作流完成后,可以查看输出参数和日志。

  1. 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
    
  2. 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 中的花括号
  1. 通过 -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>"
    
  2. 通过 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 应用于实际项目中,以此简化工作流程的管理和自动化过程。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐