Argo 是 Applatix 推出的一个开源项目,为 Kubernetes 提供 container-native(工作流中的每个步骤是通过容器实现)工作流程。Argo 可以让用户用一个类似于传统的 YAML 文件定义的 DSL 来运行多个步骤的 Pipeline。该框架提供了复杂的循环、条件判断、依赖管理等功能,这有助于提高部署应用程序的灵活性以及配置和依赖的灵活性。使用 Argo,用户可以定义复杂的依赖关系,以编程方式构建复杂的工作流、制品管理,可以将任何步骤的输出结果作为输入链接到后续的步骤中去,并且可以在可视化 UI 界面中监控调度的作业任务。

Argo 简介

Argo V2 版本通过 Kubernetes CRD(Custom Resource Definition)来进行实现的,所以我们可以通过 kubectl 工具来管理 Argo 工作流,当然就可以和其他 Kubernetes 资源对象直接集成了,比如 Volumes、Secrets、RBAC 等等。新版本的 Argo 更加轻量级,安装也十分简单,但是依然提供了完整的工作流功能,包括参数替换、制品、Fixture、循环和递归工作流等功能。

Argo 中的工作流自动化是通过使用 ADSL(Argo 领域特定语言)设计的 YAML 模板(因为 Kubernetes 主要也使用相同的 DSL 方式,所以非常容易使用)进行驱动的。ADSL 中提供的每条指令都被看作一段代码,并与代码仓库中的源码一起托管。Argo 支持6中不同的 YAML 结构:

  • 容器模板:根据需要创建单个容器和参数
    
  • 工作流模板:定义一个作业任务(工作流中的一个步骤可以是一个容器)
    
  • 策略模板:触发或者调用作业/通知的规则
    
  • 部署模板:创建一个长期运行的应用程序模板
    
  • Fixture 模板:整合 Argo 外部的第三方资源
    
  • 项目模板:可以在 Argo 目录中访问的工作流定义
    

Argo 支持几种不同的方式来定义 Kubernetes 资源清单:Ksonnect、Helm Chart 以及简单的 YAML/JSON 资源清单目录。

安装

安装 Argo 非常简单,首先当然需要一个 Kubernetes 集群(1.9+版本),kubectl 工具以及访问集群的 kubeconfig 文件(默认位于 ~/.kube/config)。

Mac 系统:

$ brew install argoproj/tap/argo

Linux 系统:

$ curl -sSL -o /usr/local/bin/argo https://github.com/argoproj/argo/releases/download/v2.2.1/argo-linux-amd64

chmod +x /usr/local/bin/argo

安装完成后,可以使用下面命令校验是否安装成功:

$ argo version

argo: v2.3.0

  BuildDate: 2019-05-20T22:11:23Z

  GitCommit: 88fcc70dcf6e60697e6716edc7464a403c49b27e

  GitTreeState: clean

  GitTag: v2.3.0

  GoVersion: go1.11.5

  Compiler: gc

  Platform: darwin/amd64

然后安装控制器和 UI 界面:

$ kubectl create ns argo

$ kubectl apply -n argo -f https://raw.githubusercontent.com/argoproj/argo/v2.3.0/manifests/install.yaml

安装完成后,为了访问方便,我们将 argo-ui 改成 NodePort 类型的 Service(当然也可以创建 Ingress 对象通过域名进行访问):

$ kubectl get pods -n argo

NAME                                   READY   STATUS              RESTARTS   AGE

argo-ui-76c6cf75b4-vh6w6               0/1     ContainerCreating   0          14s

workflow-controller-69f6ff7cbc-5pqbj   0/1     ContainerCreating   0          14s

$ kubectl get pods -n argo

NAME                                   READY   STATUS    RESTARTS   AGE

argo-ui-76c6cf75b4-vh6w6               1/1     Running   0          10m

workflow-controller-69f6ff7cbc-5pqbj   1/1     Running   0          10m

$ kubectl get svc -n argo

NAME      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE

argo-ui   ClusterIP   10.97.124.167   <none>        80/TCP    10m

$ kubectl edit svc argo-ui -n argo

kind: Service

metadata:

......

spec:

......

  sessionAffinity: None

  type: NodePort

......

service/argo-ui edited

$ kubectl get svc -n argo

NAME      TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE

argo-ui   NodePort   10.97.124.167   <none>        80:32686/TCP   12m

$ kubectl get crd |grep argo

workflows.argoproj.io                         2019-09-10T03:27:41Z

$ kubectl api-versions |grep argo

argoproj.io/v1alpha1

然后我们就通过上面的 32686 端口来访问 argo-ui 了:

到这里就证明 Argo 的基础环境就已经安装完成了。

基本的 Argo 工作流模板

下面是一个非常简单的模板,首先定义了一个工作流,创建一个带有两个容器的 Pod,其中一个容器带有 curl 命令,而另外一个容器是一个 nginx sidecar 容器,这里 curl 这个容器是“主”容器,用于轮询 nginx sidecar 容器,直到它准备好为请求提供服务为止。

将上面模板保存为名为 argo-base-template.yaml 的文件,然后可以使用 argo 命令来提交这个工作流:

$ argo submit argo-base-template.yaml

Name:                sidecar-nginx-ftcpf

Namespace:           default

ServiceAccount:      default

Status:              Pending

Created:             Tue Sep 10 12:05:53 +0800 (now)

$ argo list

NAME                  STATUS    AGE   DURATION   PRIORITY

sidecar-nginx-ftcpf   Running   15m   15m        0
当然同样的我们可以直接使用 kubectl 来进行安装,但是 Argo CLI 提供了更强大的功能,比如 YAML 校验、参数传递、重试等等功能。

上面就是我们定义的一个工作流,创建一个 Pod 并执行 Workflow 中定义的配置:

同样可以通过 argo logs查看容器日志,也可以通过 argo-ui 在页面上查看相关信息:

带条件的工作流模板

前面我们提到过 Argo 支持工作流执行过程中的条件语句。Argo 提供的 Coinflip 示例就描述了如何在模板中使用 “when”,其执行依赖于从父级接收的输出。

将 Coinflip 保存到本地:

$ wget https://raw.githubusercontent.com/argoproj/argo/master/examples/coinflip.yaml

上面的 Worflow 运行一个随机的整数脚本,常量为 0=heads、1=tails,调用特定模板(heads 或者 tails)取决于“flip-coin”任务的输出,如上面 Workflow 图中所示,flip-coin 任务执行 heads 任务只有当 heads 模板被执行的时候。

同样,使用 argo submit来提交 Coinflip 这个 Workflow:

$ argo submit coinflip.yaml

Name:                coinflip-pr9x9

Namespace:           default

ServiceAccount:      default

Status:              Pending

Created:             Tue Sep 10 15:56:04 +0800 (now)

$ argo get coinflip-pr9x9

Name:                coinflip-pr9x9

Namespace:           default

ServiceAccount:      default

Status:              Failed

Message:             child 'coinflip-pr9x9-3371049263' failed

Created:             Tue Sep 10 15:56:04 +0800 (4 minutes ago)

Started:             Tue Sep 10 15:56:04 +0800 (4 minutes ago)

Finished:            Tue Sep 10 15:59:40 +0800 (51 seconds ago)

Duration:            3 minutes 36 seconds


STEP               PODNAME                    DURATION  MESSAGE

 ✖ coinflip-pr9x9                                       child 'coinflip-pr9x9-3371049263' failed

 └---⚠ flip-coin   coinflip-pr9x9-3371049263  3m        failed to save outputs: verify serviceaccount default:default has necessary privileges

可以看到这里出现了一个权限错误,Argo 官网上给出的解决方案是给 default:default 绑定上 admin 的 clusterrole 权限:

$ kubectl create rolebinding default-admin --clusterrole=admin --serviceaccount=default:default

然后删除上面的 Workflow,重新创建:

$ argo delete coinflip-pr9x9

$ argo submit coinflip.yaml

Name:                coinflip-jxmzx

Namespace:           default

ServiceAccount:      default

Status:              Pending

Created:             Tue Sep 10 16:30:59 +0800 (now)

上面的 Workflow 将会创建两个容器,一个是用于执行 randomint python 脚本,另外一个是用于根据脚本的执行结果执行的 tails 模板,如下所示,tails 模板根据 randomint 脚本的结果(result==tails)被调用执行了:

$ kubectl get pods

NAME                                   READY   STATUS      RESTARTS   AGE

coinflip-jxmzx-1126897109              0/2     Completed   0          3m28s

coinflip-jxmzx-3981997132              0/2     Completed   0          3m36s

$ kubectl logs coinflip-jxmzx-3981997132 main

tails

$ kubectl logs coinflip-jxmzx-1126897109 main

it was tails

类似地,递归(递归调用模板)也可以添加到条件规范中,例如,下面的模板输出显示 flip-coin 模板执行 n 次,直到结果为 heads 为止,下载 flip-coin 递归模板示例:

$ wget https://raw.githubusercontent.com/argoproj/argo/master/examples/coinflip-recursive.yaml

我们可以简单对比下前面的 coinflip,基本上是一致的,唯一的区别就是 tails 模板重新指向了 coinflip,这样就形成了递归:

然后用同样的方法来提交这个 Workflow,可以通过 argoget命令来查看具体的执行步骤: 我们可以看到这里递归执行了4次,最后才出现 flip-coin 的模板输出 heads,这个时候递归才退出,每一次递归调用都会产生一个 Pod,加上最后的 heads 模板,所以一共会产生 5 个 Pod:

$ kubectl get pods

NAME                                   READY   STATUS      RESTARTS   AGE

coinflip-recursive-9dpxn-1771762865    0/2     Completed   0          8m33s

coinflip-recursive-9dpxn-177901715     0/2     Completed   0          8m26s

coinflip-recursive-9dpxn-3162439927    0/2     Completed   0          8m41s

coinflip-recursive-9dpxn-3886219258    0/2     Completed   0          8m19s

coinflip-recursive-9dpxn-545668525     0/2     Completed   0          8m49s

同样可以在 argo-ui 中查看这个 Workflow 的调用示意图:

带循环和参数的 Workflow 模板

Argo 可以很方便的迭代 Workflow 中的一组输入,也可以处理用户提供参数(比如:输入参数)。在下面的 Workflow 中,使用两个输入参数 “hello kubernetes”和 “hello argo”来执行 Whalesay 模板。

下载这里我们需要使用的 Workflow 示例:

$ wget https://raw.githubusercontent.com/argoproj/argo/master/examples/loops.yaml

不过需要注意,我们需要把 loops.yaml 文件里面的 withItems 修改成下面的参数值:

withItems:

- hello kubernetes

- hello argo

完整的 YAML 文件内容如下所示:

上面的模板包含一个单独的模板,通过使用一个 items 列表并根据提供的参数值数量来运行任务,所以,会创建两个 Pod,一个使用 “hello kubernetes”参数,另外一个使用 “hello argo”参数,同样使用 argo submit命令来创建这个 Workflow:

$ argo submit loops.yaml

Name:                loops-ftm5r

Namespace:           default

ServiceAccount:      default

Status:              Pending

Created:             Tue Sep 10 17:33:50 +0800 (now)

使用 argoget命令来查看这个 Workflow 的详细信息,可以看到迭代了两个步骤出来,当然就会产生两个 Pod:

我们去查看生成的两个 Pod 的日志信息,里面就包含上面的传入的两个参数 “hello kubernetes”和 “hello argo”的信息:

类似的我们还可以完成更加复杂的循环:循环迭代一组 items,动态生成 items 列表等,具体的实现我们直接查看 Argo GitHub 仓库上面提供的 example 样例:https://github.com/argoproj/argo/blob/master/examples/。

由 DAG(有向无环图)和步骤的依赖定义的多步骤工作流模板

Argo 允许用户使用简单的 Python 对象 DAG 启动多步骤的 Pipeline,还可以在工作流规范中定义多个模板(嵌套工作流)。

下载使用的示例文件:

$ wget https://raw.githubusercontent.com/argoproj/argo/master/examples/dag-diamond.yaml

Workflow 更详细的内容如下所示: 接下来我们来提交这个 Workflow:

$ argo submit dag-diamond.yaml

Name:                dag-diamond-qv45w

Namespace:           default

ServiceAccount:      default

Status:              Pending

Created:             Tue Sep 10 18:59:24 +0800 (now)

然后使用 argoget命令来查看这个 Workflow 的详细信息:

同样在 argo-ui 中也可以查看到这个 Workflow 的依赖关系:

使用 Minio 进行制品管理和 Argo 集成

制品是任何工作流的一部分(比如:CI/CD 中),工作流中的步骤会生成制品,然后其他后续步骤可以使用这个制品。

我们这里使用 Minio 来作为制品仓库,Minio 是兼容 Amazon S3 API 的开源对象存储服务器。

首先先安装 Minio,我们这里使用 Helm 来快速的安装,如果对 Helm 的使用还不熟悉的,可以查看前面我们的文章:Kubernetes Helm 初体验。

$ helm install stable/minio --name minio --set service.type=NodePort --set defaultBucket.enabled=true --set defaultBucket.name=my-bucket --set persistence.enabled=false --namespace argo

我们这里指定了一个名为 my-bucket 的默认 Bucket,使用 NodePort 类型的 Service,另外需要注意的是我这里单纯为了测试,所以将 persistence.enabled 设置为了 false,对于线上环境一定要记得持久化数据:

$ kubectl get pods -n argo  -l app=minio

NAME                     READY   STATUS    RESTARTS   AGE

minio-7954d6976d-jjm42   1/1     Running   0          9m25s

$ kubectl get svc -n argo  -l app=minio

NAME    TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE

minio   NodePort   10.109.167.218   <none>        9000:31311/TCP   9m31s

到这里证明 Minio 就已经安装成功了,然后我们就可以通过 31311 这个 NodePort 端口访问 Minio 的 Dashboard 页面,我们可以使用默认的 accessKey(AKIAIOSFODNN7EXAMPLE) 和 secretKey(wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY)登录:

然后我们需要把 Minio 和 Argo 集成在一起,通过编辑 workflow-controller configmap 来引用上面的 Minio Service 和 Secret,上面我们通过 Helm 安装 Minio 的时候会自动将默认的 accessKey 和 secretKey 添加到名为 minio 的 Secret 对象中去:

$ kubectl get secret minio -n argo -o yaml

apiVersion: v1

data:

  accesskey: QUtJQUlPU0ZPRE5ON0VYQU1QTEU=

  secretkey: d0phbHJYVXRuRkVNSS9LN01ERU5HL2JQeFJmaUNZRVhBTVBMRUtFWQ==

kind: Secret

......

type: Opaque

直接编辑 workflow-controller-configmap,按照如下方式添加上 Minio 相关配置:

...

data:

  config: |

    artifactRepository:

      s3:

        bucket: my-bucket  # 默认的 bucket 名称

        endpoint: minio.argo:9000  # Minio 服务地址

        insecure: true

        # accessKeySecret 和 secretKeySecret 是 secret 中包含的,引入名为 minio 的 k8s secret 对象,这两个 key:'accesskey' 和 'secretkey', 存储 真是的 minio 认证信息。

        accessKeySecret:

          name: minio

          key: accesskey

        secretKeySecret:

          name: minio

          key: secretkey

kind: ConfigMap

metadata:

  annotations:

    kubectl.kubernetes.io/last-applied-configuration: |

      {"apiVersion":"v1","kind":"ConfigMap","metadata":{"annotations":{},"name":"workflow-controller-configmap","namespace":"argo"}}

  creationTimestamp: "2019-09-10T03:27:43Z"

  name: workflow-controller-configmap

  namespace: argo

  resourceVersion: "1890436"

  selfLink: /api/v1/namespaces/argo/configmaps/workflow-controller-configmap

  uid: b1fd1a24-721d-4ce1-a02c-90fff0e931ee

编辑完成后我们就完成了 Minio 和 Argo 的集成。不过这里需要注意的是 Secret 对象是 namespace 作用域的,上面 ConfigMap 解析 Secret 对象是和我们使用的 Workflow 的 namespace 中获取 Minio Secret 的,所以如果 Minio 和 Workflow 不在同一个 namespace 下面,需要我们拷贝一份 Secret 到对应的 namespace 下面去,比如我们这里 Workflow 都在 default 这个 namespace 下面,那么我就需要在 default 下面创建相同的 Secret:(minio-secret.yaml)

apiVersion: v1

kind: Secret

metadata:

  name: minio

type: Opaque

data:

  accesskey: QUtJQUlPU0ZPRE5ON0VYQU1QTEU=

  secretkey: d0phbHJYVXRuRkVNSS9LN01ERU5HL2JQeFJmaUNZRVhBTVBMRUtFWQ==

创建上面的 Secret 对象:

$ kubectl create -f minio-secret.yaml

然后下载我们这里使用到的 Workflow 示例文件:

$ wget https://raw.githubusercontent.com/argoproj/argo/master/examples/artifact-passing.yaml

详细的 Workflow 内容如下图所示:

上面 Workflow 包括两个步骤:

  • 步骤1生成制品:使用 whalesay 模板生成制品
    
  • 步骤2消费制品:使用步骤1中创建的制品并打印消息。
    

同样,我们使用 argo submit命令提交这个 Workflow:

$ argo submit artifact-passing.yaml

Name:                artifact-passing-x4rhq

Namespace:           default

ServiceAccount:      default

Status:              Pending

Created:             Wed Sep 11 12:01:07 +0800 (now)

我们可以看到上面 Workflow 的两个步骤都已经成功了,然后我们去 minio-ui 上面就可以查看到上面 Workflow 生成的制品了:

上面我们创建的制品被存储在 Minio 的 my-bucket 中,消费制品的任务根据 Workflow 中的定义根据提供的配置拉取制品,Minio 类似于 S3 提供了一个可共享的链接来使用制品:

除了上面提到的这些用法之外,Argo Workflow 还有很多其他用法,我们可以查看 Argo 官方提供的样例了解更多使用方法:https://github.com/argoproj/argo/tree/master/examples。

本文更多的是提到 Argo Workflow 的一些基本使用方法,下一篇文章我们再通过一个完整的示例来演示下 Argo Workflow 的实际用途。

参考链接

  • https://argoproj.github.io/docs/argo/demo.html
    
  • https://itnext.io/argo-workflow-engine-for-kubernetes-7ae81eda1cc5
    
  • https://github.com/argoproj/argo/tree/master/examples