Skip to main content

Command Palette

Search for a command to run...

使用 Argo Workflow 组织跨云运维的可能性

Updated
4 min read

在微服务、容器化和 IaC 等概念普及之前,自动化通常是使用过程性操作进行的,例如摘流——升级——恢复的过程。为了运维方便,通常这些操作序列会由所谓的运维流程编排工具完成,例如 AWS 的 SSM Automation,或者阿里云的 OOS 等。随着运维自动化的要求逐步提高,这些工具的编排能力也逐步扩展,出现了插件扩展、循环、跳转等更复杂的行为,甚至还出现了人工审批等蜜汁操作。自动化的编排复杂度也不断延伸——AWS 公开的作业脚本中已经出现了超过 3000 行 50 个步骤的庞然大物。

古时候的自动化运维通常是围绕着虚拟机进行的——管你是谁家的机器,只要你开了 SSH,或者装了我家的 Agent,你就跟我姓了。但是随着公有云服务能力的不断扩展,虚拟机的运维操作占比就逐步降低了,围绕 API 进行的运维能力逐步超过了虚拟机,成为主流。

不管有用没用,多云已经成为部分架构师的口头禅了。再加上前面的两个情况—— SRE 平台需要有一个能跨云的、面向 API 的、具备复杂编排能力并且能用编程方式进行扩展的自动化工具了,另外随着面对资源规模的不同,必要的并发能力和横向扩展的能力也是必要的。经过一番比对,我觉得 Argo Workflow 可能是个合适的选择。

Argo 大概于 2017 年以 GitOps 工具的形态,由 Intuit 发布,2020 年进入 CNCF 孵化,2022 年毕业,现在已经成长为包含 Argo CD、Argo Workflows、Argo Events 以及 Argo Rollouts 的生态群,并在 2022 年开始有了 Argo Con 峰会。

架构

根据官方提供的组件图可以看出:

  1. Argo Workflows 运行在 Kubernetes 集群里。

  2. 可以利用 Kubernetes API 对 Argo 进行控制。

  3. 用户可以通过 CLI、Kubectl 和 Web UI 三种方式和 Argo 进行交互。

  4. 可以对接外部 idP,让 Argo Workflows 具备单点登录能力

  5. Workflow 也是以 Pod 的形式在集群中运行的。

下图则是对工作流的一个描述。

这里不难发现,Argo Workflow 除了支持工作流之外,还支持了 DAG,它的工作流节点是用多容器 Pod 的形式运行的——每个 Pod 中包含 Wait、Init 和 Main 三个容器。

功能

Argo Workflow 提供了非常丰富的自动化编排能力。流程方面,提供了循环、条件、递归、暂停、恢复等常见内容;容错方面提供了超时、重试、异常捕捉/跳转等能力;另外他还支持脚本执行、变量定义和处理、工件传递等用于应对复杂场景的功能。功能方面,个人评估是略强于 AWS 的 SSM Automation 的。

起步

下文均用目前的 v3.5.6 为例

Argo Workflows 的快速部署方式非常简单,下面两行命令即可:

$ kubectl create namespace argo
namespace/argo created
$ kubectl apply -n argo -f https://github.com/argoproj/argo-workflows/releases/download/v3.5.6/install.yaml
...
priorityclass.scheduling.k8s.io/workflow-controller created
deployment.apps/argo-server created
deployment.apps/workflow-controller created

当然,这只是一个测试环境的玩法,项目也用 Helm Chart 的方式提供了用于生产环境的部署途径。

服务启动后,可以看到两个 Pod:

$ kubectl get po -n argo
NAME                                   READY   STATUS    RESTARTS   AGE
workflow-controller-5bb8788d57-sxnv2   1/1     Running   0          29s
argo-server-67bcf4bb48-sq9jp           1/1     Running   0          29s

为了简化使用可以进行一点修改:

$ kubectl patch deployment \
  argo-server \
  --namespace argo \
  --type='json' \
  -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/args", "value": [
  "server",
  "--auth-mode=server"
]}]'

默认的认证方式需要使用 Service Account,并且需要进行较多的 RBAC 配置,有些复杂,所以这里改成了服务侧自行认证。

然后把服务改成 NodePort:

$ kubectl patch svc argo-server -n argo -p '{"spec": {"type": "NodePort"}}'
service/argo-server patched

这样,就可以在获取端口后,直接浏览器直接访问 Argo UI 了(注意这里默认使用的是 https 协议)。

教程中提供了一个 Hello World 流程,内容如下:

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: hello-world-
  labels:
    workflows.argoproj.io/archive-strategy: "false"
  annotations:
    workflows.argoproj.io/description: |
      This is a simple hello world example.
spec:
  entrypoint: whalesay
  templates:
  - name: whalesay
    container:
      image: docker/whalesay:latest
      command: [cowsay]
      args: ["hello world"]

这个简单的 YAML 可以看到 Argo 工作流定义中的基本元素:

  1. 这是一个 CRD,类型是 argoproj.io/v1alpha1Workflow

  2. 这一清单需要重复使用,因此 metadata 中没有给出 Name,而是给出了 generateName

  3. spec.templates 中保存的步骤的定义,并使用 spec.entrypoint 指定了入口环节。

  4. 仅有的一个步骤中,使用一个容器镜像,并指定了执行命令,输出一段文字。

使用 kubectl create 提交工作流,看看结果:

$ kubectl create -f install.yaml
workflow.argoproj.io/hello-world-fdddc created

用浏览器打开控制台,浏览 workflows 页面,可以看到,出错了:

错误原因也很 Kubernetes,就是 RBAC 权限不足:

Error (exit code 1): pods "hello-world-fdddc" is forbidden: User "system:serviceaccount:default:default" cannot patch resource "pods" in API group "" in the namespace "default"

看来这里用到的什么修改 Pod 的功能,看一下命名空间中的 hello-world,会看到它的内容和我们在模板中指定的简单几行完全不同,多出了 initContainer 和 Sidecar。主容器的命令也被加入了新的内容。

这里偷个懒,直接借用 Argo 明明空间里的 Argo SA,用法很简单,在 YAML 的 entrypoint 字段后加入同级元素 serviceAccountName: argo,并且在 Argo 命名空间里创建:

$ kubectl create -f hello-world.yaml -n argo
workflow.argoproj.io/hello-world-l4q2x created

浏览器控制台可以看到,这次成功运行,并且输出了结果:

argo CLI 也可以方便的查看:

$ argo list -A
NAMESPACE   NAME                STATUS      AGE   DURATION   PRIORITY   MESSAGE
argo        hello-world-l4q2x   Succeeded   7h    10s        0
default     hello-world-fdddc   Error       8h    10s        0          Error (exit c

场景

用户可以通过 Restful API、SDK、CLI 和 Web 控制台来访问 AWS 服务,自动化操作通常会使用 SDK 或者 CLI 的方式。这里我们设置一个场景:查询当前账户的 EC2 实例,并关机。

这里需要用到几个能力:

  1. 使用容器模板加载 AWS 凭据,并运行 AWS CLI 的能力

  2. 将 AWS CLI 结果输出为变量的能力

  3. 循环处理列表变量的能力

加载 Secret

假设我们的凭据文件保存在当前目录的 credentials 文件中,我们需要将它创建为 Secret,并在后续的容器模板中进行加载:kubectl create secret generic awskey --from-file=credentials

工作流中想要加载 Secret,跟 Pod 是很相似的,例如我们将会这样编写列出 EC2 实例的环节:

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: shutdown-ec2-
  labels:
    workflows.argoproj.io/archive-strategy: "false"
spec:
  serviceAccountName: argo
  entrypoint: list-instances
  volumes:
    - name: aws-secret
      secret:
        secretName: awskey
  templates:
    - name: list-instances
      container:
        image: amazon/aws-cli:2.15.43
        args:
          - "ec2"
          - "describe-instances"
          - "--output"
          - "json"
          - "--region" 
          - "ap-northeast-1"
          - "--query"
          - "Reservations[].Instances[].InstanceId"          
        volumeMounts:
          - name: aws-secret
            mountPath: /root/.aws

这个步骤写完之后,可以运行一下,看看结果:

$ argo submit -n argo --watch aws-list-ec2.yaml
...
STEP                   TEMPLATE        PODNAME             DURATION  MESSAGE
 ✔ shutdown-ec2-7ngl9  list-instances  shutdown-ec2-7ngl9  4s

查看日志会发现,成功返回了一个 JSON 数组,其中包含了我们需要的实例 ID 列表。

循环关闭

接下来把这个工作流改为多模板的模式,便于我们加入参数和循环能力。

实际上 AWS CLI 是直接支持用数组方式关闭多个 EC2 实例的

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: shutdown-ec2-
  labels:
    workflows.argoproj.io/archive-strategy: "false"
spec:
  serviceAccountName: argo
  entrypoint: shutdown-all-ec2
  volumes:
    - name: aws-secret
      secret:
        secretName: awskey
  templates:
    - name: shutdown-all-ec2
      steps:
        - - name: list
            template: list-instances
        - - name: shut
            template: shutdown-ec2
            arguments:
              parameters:
                - name: ec2id
                  value: "{{item.InstanceId}}"
            withParam: "{{steps.list.outputs.result}}"
    - name: list-instances
      container:
        image: amazon/aws-cli:2.15.43
        command: ["aws"]
        args:
          - --output
          - json
          - --region
          - ap-northeast-1
          - ec2
          - describe-instances
          - --query
          - "Reservations[].Instances[]"
        volumeMounts:
          - name: aws-secret
            mountPath: /root/.aws
    - name: shutdown-ec2
      inputs:
        parameters:
          - name: ec2id
      container:
        image: amazon/aws-cli:2.15.43
        command: ["aws"]
        args:
        - "ec2"
        - "stop-instances"
        - --region
        - ap-northeast-1        
        - "--instance-ids"
        - "{{inputs.parameters.ec2id}}"
        volumeMounts:
          - name: aws-secret
            mountPath: /root/.aws

上面的 YAML 的主要变化:

  • 把原有的单步骤流程拓展成了多步骤

  • 列表中加入了格式化内容,精简输出

  • 将列表结果作为循环变量,传递给了用于关机的后续步骤

arguments:
  parameters:
    - name: ec2id
      value: "{{item}}"
withParam: "{{steps.list.outputs.result}}"

这一段将步骤 list 的控制台输出作为循环变量,传递给 shutdown-ec2 模板的 ec2id 参数,逐个关机。

注意这里的写法,使用 step 的方式对模板进行引用,形成多步骤流程。

运行后,可以看到 Argo 用并发的形式,进行了批量关机操作。

补充

首先是 AWS CLI 提供了丰富的功能,调用起来实在是比 SDK 方便太多,所以这里用这种形式来简化操作。

其次是这里对输出变量的做法,其实 Argo 提供了丰富的内置函数,可以对这些输出内容进行较为复杂的处理,当然,也可以用 Script 步骤进行更加细致的定制工作。

再次,过程中直接加载 AWS 凭据的方法非常不推荐,关于容器环境中的敏感信息管理,已经有很多陈述,这里就不节外生枝了。

最后,Argo 的文档真烂,真的烂。。

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts