Argo Workflow介绍:一款强大的云原生持续集成工具

2023年 7月 9日 31.9k 0

Argo workflow 是什么

老牌的 CICD 工具 Jenkins 应该是大部分都接触过的,而在云原生时代,诞生了两大 CI/CD 框架,也就是 Argo Workflow 和 Tekton,本文主要介绍一下 Argo Workflow。

Argo Workflow 是一个云原生的工作流引擎,基于 kubernetes 来做编排任务,目前 Argo 项目是 CNCF 的毕业项目。

只有当需要执行对应的 step 时才会创建出对应的 pod,因此和 Tekton 一样,对资源的申请和释放具有很好的利用性。

基于 Argo Workflow 可以完成一些比较复杂的工作流,下面是一个来自某个 issue 的图:

Argo Workflow

Argo Workflow

架构概览

架构图

架构图

在 Argo Workflow 中,每一个 step/dag task 就是一个 pod 中的容器,最基础的 pod 会有 1 个 init 容器和两个工作容器,其中 init 容器和主容器都是 argoproj/argoexec 容器,另一个则是 step 中需要使用的容器,也就是实际执行内容的容器,在 pod 中充当 sidecar。

  • main 容器,也就是 step/dag task 中定义的容器,用于执行实际内容。
  • init 容器,用于为 main 容器处理 artifact 以及参数相关的逻辑。
  • wait 容器,等待 main 容器执行完成,以及处理一些清理任务,例如上传 artifact 到 S3。

需要理清的一点是虽然 Argo Workflow 将工作容器定义为main容器,但实际上wait容器是 pod 中的主容器。

主要资源

在 Argo Workflow 中主要的 CRD 对象有几个:

  • Workflow
  • WorkflowTemplate
  • CronWrokflow

接下来分别了解一下三个CRD对象。

Workflow

Workflow 定义的字段和 workflowTemplate 定义的字段基本上是一致的,因此将字段的解释放在 workflowTemplate 部分,对 Workflow 的理解只需要知道Workflow 是一个流水线的"实例",也就是只有创建了 Workflow 对象是才会真正的运行流水线。

WorkflowTemplate

WorkflowTemplate 是最重要的对象了,基本上绝大部分时间你都是和它在打交道,其中还有一个 template 的定义,在刚认识 Argo workflow 时需要注意区分的一点是 workflowTemplate 和 template,这在我刚入门时也造成了一点困惑,接下来讲一下这两个的区别:

workflowTemplate 是 argo workflow 中实现的 CRD 对象,而 template 则是对象内的一个字段,实际执行内容都是在 template 中定义的,一个 workflowTemplate 至少要包含一个 template。 workflowTemplate 需要将一个 template 配置为 entrypoint,也就是流水线的起点,在这个 template 的 steps 中又可以应用多个相同或不同的 template。

简单举一个例子来理解 workflowTemplate 多个 template,假设要封装一个 workflowTemplate 来处理 git 相关的场景:

分别为以下三个操作创建三个 template,假设需要在同一份代码中多次 merge && commit,那么流水线入口是 git clone,然后 git merge, git add && git commit,在这种情况下 template2 和 template3 是作为 template1 的一部分存在的。而当流水线入口直接就是 git merge 或 git add && git commit 的情况时, template2 或 template3 是作为单独的流水线逻辑存在。

  • git clone
  • git merge
  • git add && git commit
  • 因此 template 可以单独作为流水线入口执行,也可以被其他的 template 引用。

    先列出几个关键词做一个简单的概述来更好的了解 Template:

    • steps, 流水线每一步的执行内容,--表示与同级别的step并行执行,-表示与同级别的 step 顺序执行。
    • container,真正执行内容的定义,与 kubernetes container spec 定义一致。
    • script,这是一个基于 container 的类型,可以让用户直接在CI中直接执行一些脚本并且得到返回的结果,例如执行 python 代码,执行 node 代码以及执行 shell 等等。
    • resource,这个类型可以支持在 CI 中对 Kubernetes 的对象进行操作,例如创建一个 configMap,然后根据这个 Kubernetes 资源对象的状态来判断该步骤是否成功。(这个功能太酷了!)

    以上几个点是理解 template 最主要的内容,一个简单示意的 yaml 格式如下:

    ...
      entrypoint: hello-hello-hello #配置template入口
      templates:
      - name: hello-hello-hello
        steps:
        - - name: hello1            
            template: whalesay
            arguments:
              parameters:
              - name: message
                value: "hello1"
        - - name: hello2a   
            template: whalesay
            arguments:
              parameters:
              - name: message
                value: "hello2a"   #hello2a与hello1 是顺序执行
          - name: hello2b          #hello2a与hello2b是并行执行
            template: whalesay
            arguments:
              parameters:
              - name: message
                value: "hello2b"
    
      - name: whalesay  # 定义一个用于真正执行内容的 template
        inputs:
          parameters:
          - name: message
        container:
          image: docker/whalesay
          command: [cowsay]
          args: ["{{inputs.parameters.message}}"]
    ...
    

    我们在执行代码测试的过程中经常会有一些依赖服务要怎么在 Argo Workflow 中实现呢?

    Argo Workflow 为 step 提供了 sidecars 参数,可以配置你需要的依赖容器,例如 DID,etcd,Redis 和 Mysql 等等.

    下面是一个简单的 yaml 示例,为 CI 添加一个 redis 依赖服务:

    ...
            container:
              image: busybox:1.36.1
              command: [sh, -c]
              args: ["echo hello"]
            sidecars:
            - name: redis
              image: redis:7.0.11
    ...
    

    另一个比较常见的是 并行版本/参数测试,例如跑测试时希望让同一份代码基于多个版本的 Etcd 服务做测试,那么 Argo workflow 也提供了 withItems 的方式来实现这个功能。

    还有一个常见的需求是编译缓存,例如 java 应用编译产物希望在下一个 CI 中继续应用,避免每次都去下载一些重复的 jar,Argo workflow 通过 volume 功能来实现这部分内容. 也可以通过 artifact 功能实现,例如上传到 S3,需要时再进行下载。

    根据文件唯一性来确认编译缓存是否更改,对于并行测试来说编译缓存可能是一样的,例如只更新了代码而没有更新 pom.xml 那么缓存依赖是一样的. 对于 pom.xml 更改了,也就是编译缓存变更了,那么可以需要先更新编译缓存,然后再跑并行测试,当然这是具体的业务内容了。

    接下来从官方一个默认的 workflowTemplate 来看一下实际的 yaml 是怎么样的。

    一个默认的简单workflowTemplate

    当创建 workflowTemplate 时会有一个默认的 workflowTemplate,来看一下这个 workflowTemplate 做了什么事情。

    metadata:
      name: lovely-dragon
      namespace: default
      labels:
        example: 'true'
    spec:
      workflowMetadata:
        labels:
          example: 'true'
      entrypoint: argosay
      arguments:
        parameters:
          - name: message
            value: hello argo
      templates:
        - name: argosay
          inputs:
            parameters:
              - name: message
                value: '{{workflow.parameters.message}}'
          container:
            name: main
            image: argoproj/argosay:v2
            command:
              - /argosay
            args:
              - echo
              - '{{inputs.parameters.message}}'
      ttlStrategy:
        secondsAfterCompletion: 300
      podGC:
        strategy: OnPodCompletion
    

    默认创建的 workflowTemplate 只有一个 template 并且它做的唯一一件事情就是打印传参 message

    需要注意的是 默认的 workflow 中设置了 podGC 和 workflow 的 TTL,一旦 pod 执行完成了就会删除 pod,并且由于默认没有配置日志持久化,这时候去查看日志的话只会看到空白。

    为了方便研究测试,可以先将这部分内容给注释掉,或为 Argo Workflow 设置日志持久化。

    注意:实际生产应用时请正确设置日志持久化。

    下面的配置表示 workflow 会在 workflow 执行完成后的 300 秒删除,而 workflow 过程中产生的 pod 会在 pod 执行完成后立刻删除。

    ...
      ttlStrategy:
        secondsAfterCompletion: 300
      podGC:
        strategy: OnPodCompletion
    ...
    

    DAG 有向无环图

    在 Tekton 中,DAG 的表示可以直接理解为 Pipeline。在整个流水线的过程中,可以串行或并行地执行 Tekton Task 并且任务起点与终点不会形成一个闭环。

    除了 steps template 之外,Argo WorkflowTemplate 同样支持 DAG,以 dag template 的方式存在,可以让用户更好的维护复杂的工作流。

    这里基于一个官方文档的示例来简单了解一下 Argo workflow dag template:

    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
            dependencies: [A]
            template: echo
            arguments:
              parameters: [{name: message, value: B}]
          - name: C
            dependencies: [A]
            template: echo
            arguments:
              parameters: [{name: message, value: C}]
          - name: D
            dependencies: [B, C]
            template: echo
            arguments:
              parameters: [{name: message, value: D}]
    

    可以看到工作流的入口 template 为 diamond,由于只有任务A没有顺序依赖,因此一开始只会执行任务A,任务A成功执行后开始同时执行任务B和任务C,最终任务B和任务C都顺利执行完后开始执行任务D。可以看到 dependencies 是一个数组传参,因此也可以将上述示例修改为任务D只需要等待任务C顺利执行后就开始执行。

    支持Kubernetes资源操作

    在前面已经知道有一个 resource 类型的 template,这在我看来是很酷的功能!接下来看一个官方例子:等待 workflow 执行完成。

    apiVersion: argoproj.io/v1alpha1
    kind: Workflow
    metadata:
      generateName: Kubernetes-wait-wf-
    spec:
      entrypoint: Kubernetes-wait-wf
      templates:
      - name: Kubernetes-wait-wf
        steps:
        - - name: create-wf
            template: create-wf
        - - name: wait-wf
            template: wait-wf
            arguments:
              parameters:
              - name: wf-name
                value: '{{steps.create-wf.outputs.parameters.wf-name}}'
    
      - name: create-wf
        resource:
          action: create
          manifest: |
            apiVersion: argoproj.io/v1alpha1
            kind: Workflow
            metadata:
              generateName: sleep-
            spec:
              entrypoint: sleep
              templates:
              - name: sleep
                container:
                  image: alpine:latest
                  command: [sleep, "20"]        
        outputs:
          parameters:
          - name: wf-name
            valueFrom:
              jsonPath: '{.metadata.name}'
    
      - name: wait-wf
        inputs:
          parameters:
          - name: wf-name
        resource:
          action: get
          successCondition: status.phase == Succeeded
          failureCondition: status.phase in (Failed, Error)
          manifest: |
            apiVersion: argoproj.io/v1alpha1
            kind: Workflow
            metadata:
              name: {{inputs.parameters.wf-name}}        
    

    可以看到,Argo Workflow 原生支持直接在流水线中创建 Kubernetes 对象,并且根据对象的字段来控制流水线的执行。上述的示例效果是在 step1 中创建一个 workflow,然后在 step2 中等待创建的 workflow 执行完成,或者说等待这个 workflow 对象变更成预期的 success 或 failure 对应的状态。

    这个在 resource 操作在流水线需要处理一些 Kubernetes 资源时会是一个很有用的功能。

    还有什么

    一个更完善的流水线可能还会包含很多复杂的内容,这里留下一些参考点为研究 Argo workflow 时提供一些方向:

    • CI 矩阵
    • 为 CI 配置环境变量
    • 为 CI 配置多个依赖服务 (sidecar)
    • 设置/使用特别 bucketName s3 artifact
    • 工件存储 artifact
    • volume 传递编译缓存
    • 条件判断 step,当某些条件成立时才执行对应的 step
    • workflow 和 step 并行度限制
    • resource 类型 template,直接编写 python 代码

    以上都是 Argo Workflow 本身就提供的功能,不需要自己再做一些封装,因此足以看到 Argo Workflow 提供的功能的实用性。

    CronWorkflow

    CronWorkflow 是一个用于定时触发 workflow 的定义,在 CI 中也是很常见的,例如每晚构建。

    同样地,通过 Argo 的 UI 界面创建一个定时的 workflow 时也会有一个默认的 cronWorkflow,可以通过这个默认的 cronWorkflow 来了解下:

    metadata:
      name: sparkly-tiger
      namespace: default
      labels:
        example: 'true'
    spec:
      workflowMetadata:
        labels:
          example: 'true'
      schedule: '* * * * *'
      workflowSpec:
        entrypoint: argosay
        arguments:
          parameters:
            - name: message
              value: hello argo
        templates:
          - name: argosay
            inputs:
              parameters:
                - name: message
                  value: '{{workflow.parameters.message}}'
            container:
              name: main
              image: argoproj/argosay:v2
              command:
                - /argosay
              args:
                - echo
                - '{{inputs.parameters.message}}'
        ttlStrategy:
          secondsAfterCompletion: 300
        podGC:
          strategy: OnPodCompletion
    

    经过前面 workflow 和 workflowTemplate 的了解后,可以看到 cronWorkflow 的 yaml 文件整体来说是差不多的,无非是多了一些定时相关的配置。

    上述示例中 schedule 配置为 * * * * *,也就是每分钟会执行一次 workflow。关于定时的内容都会有一个注意点是定时任务的时区, cronWorkflow 支持为定时任务设置时区,具体可以看看官方的这个cron workflow 示例。

    与 Tekton 的对比

    经过一番折腾后能够感受到一些很明显的区别:

    从系统架构上来说,Tekton 做得更好,整体架构比较清晰,但从用户功能的角度上来说 Argo Workflow 更容易上手使用。

    Argo workflow 的 UI 能够展现出比较直观的 CI 顺序效果,同时 Argo WOrkflow 会有很多实用的功能。

    另一个是 Argo workflow 中提供了一些 Tekton 默认所没有的功能,在我看来这些也都是比较酷和实用的功能,例如:

    • resource 类型 template,可以直接创建 Kubernetes 对象以及 对 Kubernetes 对象 get,等待 Kubernetes 对象某个字段变更。
    • artifact 功能,例如和 S3 打交道,这在流水线中是很常见的需求,但 Tekton 本身并没有提供。

    Argo workflow 的文档建设也比 Tekton 更好。

    总的来说 Tekton 提供的内容处于更底层的位置,与 kubernetes 类似,是一个 CI/CD 底层的引擎,真正用好它需要基于它做一些事情。而 Argo Workflow 处于更上层一点的位置,提供了很多实用的功能,可以很方便的应用起来。

    写在最后

    到目前为止,我们了解了 Argo Workflow 的强大特性以及与 Tekton 的一个简单对比,实际在企业内应该选择 Argo Workflow 还是 Tekton 还是需要根据业务特点以及实际验证一些测试后才能决定。

    以上只是聊到了 Argo Workflow 的一部分功能,如果想了解更多的功能可以先从官方示例开始,官方代码仓库给了很多的示例yaml,因此可以通过这些示例 yaml 很快的了解到相关的功能。

    相关文章

    KubeSphere 部署向量数据库 Milvus 实战指南
    探索 Kubernetes 持久化存储之 Longhorn 初窥门径
    征服 Docker 镜像访问限制!KubeSphere v3.4.1 成功部署全攻略
    那些年在 Terraform 上吃到的糖和踩过的坑
    无需 Kubernetes 测试 Kubernetes 网络实现
    Kubernetes v1.31 中的移除和主要变更

    发布评论