01
组件概述
Argo Workflows是一个开源的容器原生工作流引擎,允许用户在Kubernetes集群上定义并执行复杂的业务流程。支持对相关流程进行个性化编排,包括执行顺序、相互之间的依赖等等。
Argo Workflows 是以 Kubernetes 自定义资源定义(Custom Resource Definitions,简称 CRD)的形式来实现其功能。CRD 是 Kubernetes 提供的一种机制,它允许用户在不修改 Kubernetes 核心代码的情况下,扩展 API Server,定义新的资源类型。
常见的应用场景包括:
CI/CD;
数据处理和分析;
机器学习;
批处理作业。
Argo是云原生计算基金会(CNCF)的毕业项目。
本文将重点关注CI/CD的应用场景,并通过下表对比Argo与业界常用的其它CI/CD组件的功能和特点:
Fluxcd | Jenkins | Argo | |
---|---|---|---|
持续集成与部署 | 不完全支持 | 支持 | 支持 |
编排 | - | Pipeline、插件生态 | 工作流、声明式配置 |
Gitops | 支持 | 插件生态 | 支持 |
云原生 | 支持 | 插件生态 | 支持 |
扩展性 | 自定义控制器 | 插件生态 | CRD |
UI界面 | 由第三方提供支持 | 支持 | 支持 |
社区活跃度 | 高 | 高 | 高 |
运维复杂度 | 简单 | 高 | 一般 |
适用场景 | 轻量级、仅需要持续部署 | 高度定制化、依赖庞大的插件系统支持 | 云原生、轻量级 |
Argo Workflows的核心优势包括:
云原生:专为Kubernetes设计,可以在任何Kubernetes集群上运行,因此可以更轻松地与基于Kubernetes的环境进行集成;
声明式:使用YAML文件来声明式的定义工作流,这使得它们更容易被版本控制,并且能够以代码的形式进行统一管理;
轻量级:任务的运行基于容器,没有传统虚拟机和基于服务器环境的开销和限制;
灵活性:支持Step和DAG结构,允许用户定义任意复杂度的工作流,包括条件分支、循环、并行任务等。
02
核心概念
2.1 Workflow
Workflow是Argo中最重要的资源,具有两个重要功能:
定义了要执行的工作流,包括元数据、Spec、Status;
存储工作流总体的状态信息,包括阶段信息、启动和完成时间等等。
所以,工作流不仅仅是一个单纯的静态定义,它还代表了一个具体的实例。
数据结构
type Workflow struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata" protobuf:"bytes,1,opt,name=metadata"`
Spec WorkflowSpec `json:"spec" protobuf:"bytes,2,opt,name=spec "`
Status WorkflowStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
metav1.TypeMeta、metav1.ObjectMeta:元数据相关内容,和k8s的书写规范保持一致即可;
Spec:工作流的规范,也是用户定义工作流主要内容的位置。核心是"templates"以及"entrypoint",templates可以大致理解为"函数",它们定义要执行的逻辑。entrypoint字段定义"main"函数是什么,即首先要执行的模板是哪个,在后面的Spec一节会详细介绍;
Status:包含有关工作流的总体状态信息,例如:Phase(阶段信息,包括Running、Succeeded、Failed等)、StartedAt(工作流的开始时间)、FinishedAt(工作流的完成时间)等。
示例:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
labels:
app.kubernetes.io/name: app_name
app.kubernetes.io/instance: service_name
app.kubernetes.io/version: app_version
app.kubernetes.io/managed-by: pipeline
generateName: output-something-
namespace: default
# 上述内容书写规范参考k8s即可
spec:
entrypoint: main # 定义工作流入口的主模板,此处模板名称为"main"
templates: # 模板列表,可以定义多个模板
- name: echo-message1 # 定义了一个"echo-message1"模板
container:
image: alpine:3.20.3
command: ["/bin/sh"] # 启动命令
args: ["-c", "echo message1"] # 传递给上方command的参数,最终打印"message1"并退出
Spec
数据结构
// 数据结构部分内容较多,下方仅挑选常用字段进行说明,完整信息可以参考官方源码
type WorkflowSpec struct {
// 模板列表,用户定义实际工作流逻辑的位置
Templates []Template `json:"templates,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,1,opt,name=templates"`
// 入口点,用于定义主模板,通常为"main"
Entrypoint string `json:"entrypoint,omitempty" protobuf:"bytes,2,opt,name=entrypoint"`
// 参数,包含parameters及artifacts,发送到上面的entrypoint
Arguments Arguments `json:"arguments,omitempty" protobuf:"bytes,3,opt,name=arguments"`
// 同k8s中的SA
ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"bytes,4,opt,name=serviceAccountName"`
// 卷列表,具体配置参考k8s中的volume
Volumes []apiv1.Volume `json:"volumes,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,5,opt,name=volumes"`
// PVC列表,具体配置参考k8s中的PVC
VolumeClaimTemplates []apiv1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty" protobuf:"bytes,6,opt,name=volumeClaimTemplates"`
// 同k8s中的Affinity
Affinity *apiv1.Affinity `json:"affinity,omitempty" protobuf:"bytes,11,opt,name=affinity"`
// 同k8s中的Toleration
Tolerations []apiv1.Toleration `json:"tolerations,omitempty" patchStrategy:"merge" patchMergeKey:"key" protobuf:"bytes,12,opt,name=tolerations"`
// 同k8s中的imagePullSecrets
ImagePullSecrets []apiv1.LocalObjectReference `json:"imagePullSecrets,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,13,opt,name=imagePullSecrets"`
// 模板引用,在工作流结束时调用,无论主工作流是成功、失败还是错误
OnExit string `json:"onExit,omitempty" protobuf:"bytes,17,opt,name=onExit"`
// 生命周期配置,限制已完成执行的工作流的生命周期,具体取决于它是成功还是失败。如果设置了此结构,则一旦工作流完成,它将在生存时间到期后被删除。
TTLStrategy *TTLStrategy `json:"ttlStrategy,omitempty" protobuf:"bytes,30,opt,name=ttlStrategy"`
// 模板引用,包含工作流模板及集群模板
WorkflowTemplateRef *WorkflowTemplateRef `json:"workflowTemplateRef,omitempty" protobuf:"bytes,34,opt,name=workflowTemplateRef"`
// 重试策略
RetryStrategy *RetryStrategy `json:"retryStrategy,omitempty" protobuf:"bytes,37,opt,name=retryStrategy"`
}
Template
支持的模板类型有很多,下面仅列举一些常用的模板类型,详细的模板信息请参考官方文档。
Container
定义Pod中运行的主容器,也是定义实际业务逻辑的地方,书写规范与k8s容器相同。
示例:
- name: echo-message1
container:
image: alpine:3.20.3
command: ["/bin/sh"]
args: ["-c", "echo message1"]
ContainerSet
类似于Container或Script,但允许指定多个容器(并行)在单个Pod内运行,可以指定容器间的依赖关系(容器数量要尽可能的少,推荐不要大于10个)。由于一个Pod内有多个容器运行,所以它们将调度到同一台主机上运行,进而,可以很方便的使用emptyDir(而不是PVC)在各个步骤之间共享数据。
示例:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: output-something-
spec:
entrypoint: main
templates:
- name: main
volumes:
- name: workspace
emptyDir: {}
containerSet:
volumeMounts:
- mountPath: /workspace
name: workspace
containers:
- name: container-a
image: alpine:3.20.3
command: ["/bin/sh", "-c"]
args: ["echo 'container-a output message1' >> /workspace/message.txt"]
- name: container-b
image: alpine:3.20.3
command: ["/bin/sh", "-c"]
args: ["echo 'container-b output message2' >> /workspace/message.txt"]
- name: main
image: alpine:3.20.3
command: ["/bin/sh", "-c"]
args: ["echo 'main output message3' >> /workspace/message.txt"]
dependencies:
- a
- b
outputs:
parameters:
- name: message
valueFrom:
path: /workspace/message.txt
Script
对容器进行了包装,在容器基础上新增了"Source"字段,允许用户直接定义脚本,脚本将保存到文件中并执行。
示例:
- name: java-compile
script:
image: maven:3.9.9-eclipse-temurin-11
volumeMounts:
- mountPath: /package # 产物存储,例如编译产生的jar包、zip包等
name: artifacts-data
- mountPath: /workspace # 工作目录
name: workspace
command: [/bin/sh] # 启动命令
source: | # 定义脚本
git clone -b {{workflow.parameters.git_branch}} {{workflow.parameters.app_repo}}
cd /package/{{workflow.name}}/{{workflow.parameters.app_name}}
echo "Branch: $(git rev-parse --abbrev-ref HEAD)"
echo "CommitID: $(git rev-parse HEAD)"
echo "Maven Command: {{workflow.parameters.mvn_command}}"
{{workflow.parameters.mvn_command}}
在上方示例中出现了很多{{workflow.parameters.parameter_name}}的写法,该写法为变量(参数)引用,这些引用会由Argo自动替换。例如{{workflow.parameters.git_branch}}被替换为workflow中spec.arguments.parameters.git_branch的值。
Resource
直接对集群资源进行操作,支持的操作包括:get,create,apply,delete,replace,patch,此示例在集群上创建ConfigMap资源。
示例:
- name: create-cm
resource:
action: create
manifest: |
apiVersion: v1
kind: ConfigMap
metadata:
name: config-env-file
namespace: default
data:
key1: value1
key2: value2
key3: value3
Suspend
暂停模板,可以在到达该步骤时暂停工作流程,支持自动恢复或手动恢复。包含一个Duration字段,标识自动恢复前要等待的秒数,必须是字符串。默认单位是秒。也可以是Duration,例如:"2m"、"6h"、"1d"。
示例:
- name: delay
suspend:
duration: "5m"
Steps
Steps定义一系列串行/并行的工作流步骤,是对在一系列步骤中执行的模板的引用。模板的结构是列表,外部列表将按顺序运行,内部列表将并行运行,可以使用when语句进行条件控制。
示例:
- name: example
steps:
- - name: step1
template: echo-message1
- - name: step2-1
template: echo-message2-1
- name: step2-2
template: echo-message2-2
- - name: step3
template: echo-message3
- - name: step4
template: echo-message4
when: "{{steps.step3.outputs.result}} == message3"
上面的执行顺序为:
首先执行step1;
step1完成后step2-1和step2-2将并行运行;
step2-1和step2-2均完成后,step3将运行;
step3完成后,如果when语句的结果为true,则执行step4,否则会跳过。
示例的流程图如下所示:

DAG
同上面的Steps不同,DAG模板允许用户将任务定义为依赖关系图。在DAG中,用户可以列出所有需要运行任务,并可以自由设置某些任务的依赖(比如:在开始某个特定任务之前必须完成哪些其它依赖任务),没有任何依赖关系的任务将立即运行。
示例:
- name: task-test
dag:
tasks:
- name: task-a
template: echo
arguments:
parameters: [{name: message, value: task-a}]
- name: task-b
depends: "task-a"
template: echo
arguments:
parameters: [{name: message, value: task-b}]
- name: task-c
depends: "task-a"
template: echo
arguments:
parameters: [{name: message, value: task-c}]
- name: task-d
depends: "task-b && task-c" # 也可写成 dependencies: [task-b, task-c]
template: echo
arguments:
parameters: [{name: message, value: task-c}]
上面的执行顺序为:
首先运行task-a;
task-a运行完成后,task-b和task-c将同时运行;
task-b和task-c均完成后,task-d将运行。
WorkflowTemplate
WorkflowTemplate是工作流模板资源的定义,可以通过该资源定义自己的模板库,将定义好的模板提交到集群中,就可以在工作流中进行重复引用了。
数据结构
type WorkflowTemplate struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata" protobuf:"bytes,1,opt,name=metadata"`
Spec WorkflowSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
}
通过上方的数据结构并对比Workflow的数据结构,除了没有Status字段外,其它的和workflow没什么区别,这也就意味着WorkflowTemplate仅仅是一个单纯的静态定义。
示例:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: output-something--
spec:
entrypoint: main
arguments:
parameters:
- name: message
value: "message1"
workflowTemplateRef:
name: wftmpl-echo # 引用的模板名称
03
组件架构
Argo Workflow主要包含两个组件"Argo Server"及"Workflow Controller",这两个组件均部署在argo命名空间下且保证高可用:
Argo Server提供了Web UI及API服务,通过ingress对外暴露访问地址,客户端及用户可以通过域名访问Argo Server;
Workflow Controller是个控制器,负责整体的调谐过程。
通过Thanos或者Prometheus对工作流组件进行监控、告警,通过Grafana进行可视化展示。
用户可以通过Web UI或者Argo命令行等方式对相关资源进行操作。
用户创建的业务工作流均运行在业务命名空间下,底层通过分布式存储和镜像仓库提供工作流所需的输入、输出制品的存储。
每个工作流的每个Step或DAG都会创建独立的pod,每个pod中都会有三个容器,分别是:init、main、wait,图示如下:

init:init容器运行在main容器之前,用于初始化一些前置条件或准备工作。例如:获取制品和参数、挂载卷等等;
main:main容器包含了实际要执行的业务逻辑,在所有的init容器成功完成后执行;
wait:wait容器等待main容器完成并执行一系列其它动作。例如:捕获输出脚本结果、保存输出参数、保存输出制品等。
3.1 Controller 架构

Workflow Controller也同样遵循k8s项目中的一个通用的编排模式,即:“控制循环”。
简单来说,就是不断获取集群中相关资源的实际状态,同时和期望状态做一个diff,根据diff的结果对具体资源执行实际操作(增、删、改等等),这个操作通常称为“调谐”。
上图中绿色虚线框内展示了Informer的工作流程。其中Informers包含很多Informer,具体实现几乎都是SharedIndexInformer,例如wfInformer、wftmplInformer、podInformer等等,Informer的作用及详细的工作流程本文不再赘述,感兴趣的同学可以参考Kubernetes go client library。
我们主要关注Worker部分,这部分包含主要的处理逻辑,核心是“processNextItem()”方法。该方法的主要功能是从wfQueue中取出下一个待处理的工作流项,并进行一系列处理操作。其中处理工作流的具体执行操作发生在“woc.operate()”方法,它评估工作流及其 pod 的当前状态,并决定如何继续执行。感兴趣的同学可以参考controller.go。
04
安装部署及监控
4.1 Argo CLI安装
安装过程不再赘述,详细的安装步骤请参考官方安装文档。
4.2 Controller and Server安装
安装过程不再赘述,详细的安装步骤请参考官方安装文档。
相关镜像请提前同步至私有镜像仓库,并替换yaml文件中镜像地址。如果开启了认证还需要额外配置镜像拉取密钥。
4.3 配置组件监控
Controller默认通过9090端口的/metrics公布指标,所以需要在组件命名空间下添加svc,参考内容如下,按需更改:
apiVersion: v1
kind: Service
metadata:
annotations:
prometheus.io/scheme: http
prometheus.io/scrape: "true"
labels:
app: workflow-controller
name: workflow-controller-metrics
namespace: argo
spec:
clusterIP: None
ports:
- name: metrics
port: 9090
protocol: TCP
targetPort: 9090
selector:
app: workflow-controller
Prometheus中添加配置,部分参考内容如下,按需更改:
- bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
honor_labels: true
job_name: kubernetes-service-endpoints
kubernetes_sd_configs:
- role: endpoints
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true
relabel_configs:
- action: keep
regex: true
source_labels:
- __meta_kubernetes_service_annotation_prometheus_io_scrape
- action: drop
regex: true
source_labels:
- __meta_kubernetes_service_annotation_prometheus_io_scrape_slow
- action: replace
regex: (https?)
source_labels:
- __meta_kubernetes_service_annotation_prometheus_io_scheme
target_label: __scheme__
- action: replace
regex: (.+)
source_labels:
- __meta_kubernetes_service_annotation_prometheus_io_path
target_label: __metrics_path__
- action: replace
regex: (.+?)(?::\d+)?;(\d+)
replacement: $1:$2
source_labels:
- __address__
- __meta_kubernetes_service_annotation_prometheus_io_port
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_service_annotation_prometheus_io_param_(.+)
replacement: __param_$1
- action: replace
source_labels:
- __meta_kubernetes_namespace
target_label: namespace
- action: replace
source_labels:
- __meta_kubernetes_service_name
target_label: service
- action: replace
source_labels:
- __meta_kubernetes_pod_node_name
target_label: node
- action: keep
source_labels: [__meta_kubernetes_endpoint_port_name]
regex: .+
配置完成并应用到集群后,查看目标状态是否是UP,参考图示如下:

4.4 效果图展示

05
应用实践
5.1 架构方案

上图中组件概述如下:
代码仓库:代码存储、唯一事实源;
统一平台:功能平台化;
工作流管理:功能包括:工作流模板管理、工作流实例管理、webhook配置、自动创建APP、工作流异常告警等;
云部署配置管理:服务部署模板管理、服务发布管理、日志收集等;
CI:底层CI相关组件,用于处理整体的CI流程,可选的协同组件包括:代码扫描、镜像仓库等;
CD:底层CD相关组件,用于处理整体的CD流程,以Git作为唯一事实源;
K8S管理系统:多集群、混合云集群管理。配合CD组件实现混合云场景下多集群分发及差异化管理。
5.2 示例流程

整体流程如下:
平台侧提交工作流到集群中,CI流程开始;
拉取代码进行编译打包(以及可选的代码扫描及扫描结果的检查通知);
初始化dockerfile(支持自定义dockerfile);
构建镜像并推送到镜像仓库;
更新镜像tag并同步到代码仓库;
执行全局退出清理脚本(清理工作流运行期间产出的临时产物)。
5.3 示例配置
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: java-compile-build-deploy
spec:
entrypoint: main
ttlStrategy:
secondsAfterCompletion: 86400
secondsAfterFailure: 86400
secondsAfterSuccess: 86400
imagePullSecrets:
- name: harbor-secret
onExit: global-exit
arguments:
parameters:
- name: app_module
value: "example"
- name: app_name
value: "example"
- name: app_repo
value: "example"
- name: context_path
value: "example"
- name: custom_dockerfile
value: "example"
- name: dockerfile_path
value: "example"
- name: environment
value: "example"
- name: git_branch
value: "example"
- name: java_version
value: "example"
- name: ns
value: "example"
- name: product_name
value: "example"
- name: repository_name
value: "example"
- name: run_timestamp
value: "example"
- name: service_name
value: "example"
- name: mvn_command
value: "example"
- name: product_type
value: "example"
volumes:
- name: artifacts-data
persistentVolumeClaim:
claimName: artifacts-data-pvc
- name: workspace
emptyDir:
sizeLimit: 10240Mi
- name: harbor
secret:
secretName: harbor
- name: cert
secret:
secretName: harbor-tls
- name: git-ssh
secret:
secretName: git-ssh
defaultMode: 384
templates:
- name: main
steps:
- - name: compile
templateRef:
name: generic-java-compile
template: java-compile
- - name: init-jar
templateRef:
name: generic-init-dockerfile
template: init-dockerfile
when: >-
{{workflow.parameters.custom_dockerfile}} == false
&&
( '{{workflow.parameters.product_type}}' == '' ||
'{{workflow.parameters.product_type}}' == 'jar' )
- - name: init-zip
templateRef:
name: generic-init-dockerfile-zip
template: init-dockerfile
when: >-
{{workflow.parameters.custom_dockerfile}} == false
&&
'{{workflow.parameters.product_type}}' == 'zip'
- - name: build
templateRef:
name: generic-image-build
template: image-build
- - name: update-tag
templateRef:
name: generic-update-tag
template: update-tag
arguments:
parameters:
- name: pipeline_repo
value: "ssh://git@gitlab.example.com:group/example.git"
- name: pipeline_branch
value: 'main'
- name: global-exit
steps:
- - name: exit
templateRef:
name: generic-exit
template: exit
5.4 效果展示
工作流管理

工作流模板

工作流步骤

06
参考文档
官方文档(https://argo-workflows.readthedocs.io/en/latest/)
项目源码(https://github.com/argoproj/argo-workflows)