An open API service indexing awesome lists of open source software.

https://github.com/devops-ws/argo-workflows-guide

Argo Workflows Guide
https://github.com/devops-ws/argo-workflows-guide

argo-workflows guide

Last synced: 7 months ago
JSON representation

Argo Workflows Guide

Awesome Lists containing this project

README

          

[Argo Workflows](https://argoproj.github.io/argo-workflows/) 是一个[云原生](https://en.wikipedia.org/wiki/Cloud-native_computing)的通用的工作流引擎。本教程主要介绍如何用其完成持续集成(Continous Integration, CI)任务。

## 基本概念
对任何工具的基本概念有一致的认识和理解,是我们学习以及与他人交流的基础。

以下是本文涉及到的概念:

* WorkflowTemplate,工作流模板
* Workflow,工作流

为方便读者理解,下面就几个同类工具做对比:

| Argo Workflow | Jenkins |
|---|---|
| WorkflowTemplate | Pipeline |
| Workflow | Build |

## 最佳实践
* 把所有 Workflow YAML 文件存到一个 Git 仓库(例如:`infra/workflows`)中,并利用 [Argo CD](https://github.com/devops-ws/argo-cd-guide) 同步到 Kubernetes 集群
* 团队之间共用的部分封装为 `ClusterWorkflowTemplate`

## 安装
首先,你需要有一套 [Kubernetes](https://github.com/kubernetes/kubernetes/) 环境。下面的工具可以帮助你快速按照好一套 Kubernetes 环境:

> 推荐使用 [hd](https://github.com/LinuxSuRen/http-downloader) 安装下面的工具
>
> 安装 `hd` 的命令为:`curl https://linuxsuren.github.io/tools/install.sh|bash`

| 工具 | 工具安装 |使用 |
|---|---|---|
| [k3d](https://k3d.io/) | `hd i k3d` | `k3d cluster create` |
| [kubekey](https://github.com/kubesphere/kubekey) | `hd i kk` | `kk create cluster` |
| [minikube](https://github.com/kubernetes/minikube) | `hd i minikube` | `minikube start` |

当 Kubernetes 环境就绪后,就可以通过下面的命令会在命名空间(`argo`)下安装最新版本的 `Argo Workflow`:

```shell
kubectl create namespace argo
kubectl apply -n argo -f https://github.com/argoproj/argo-workflows/releases/latest/download/install.yaml
```

如果你的环境访问 GitHub 时有网络问题,可以使用下面的命令来安装:

```shell
docker run -it --rm -v $HOME/.kube/:/root/.kube --network host --pull always ghcr.io/linuxsuren/argo-workflows-guide:master
```

推荐使用的工具:

|||
|---|---|
| [k9s](https://k9scli.io/) | K9s is a terminal based UI to interact with your Kubernetes clusters. |

## 设置访问方式
我们可以用下面的方式或者其他方式来设置 Argo Workflows 的访问端口:

```shell
kubectl -n argo port-forward deploy/argo-server --address 0.0.0.0 2746:2746
# 或者设置为 NodePort
kubectl -n argo patch svc argo-server --type='json' -p '[{"op":"replace", "path":"/spec/type", "value":"NodePort"}, {"op":"add", "path":"/spec/ports/0/nodePort","value":31517}]'
# 暴露 k3d 端口
k3d node edit k3d-k3s-default-serverlb --port-add 31517:31517
```

> 需要注意的是,这里默认的配置下,服务器设置了自签名的证书提供 HTTPS 服务,因此,确保你使用 `https://` 协议进行访问。

例如,地址为:`https://10.121.218.242:2746/`

Argo Workflows UI 提供了多种认证登录方式,对于学习、体验等场景,我们可以通过下面的命令直接设置绕过登录:

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

## 简单示例

下面是一个非常简单的示例:

```shell
cat < /proc/sys/user/max_user_namespaces
```

除了 `buildkit` 以外,也可以考虑使用 `kaniko`(你可以从 [library.yaml](templates/library.yaml)中找到对应的配置)。

### 小结
在上面的例子中,有如下几点需要注意的:

* 采用 buildkit 构建镜像,避免挂载本地 Docker 的 `/var/run/docker.sock` 文件
* 上面的例子,在 Kubernetes 集群不以 Docker 作为[容器运行](https://kubernetes.io/docs/setup/production-environment/container-runtimes/)时也能正常使用
* 在实际使用过程中,有遇到过 buildkit 报错的情况,可以考虑增加重试机制进一步保障构建成功
* `registry.insecure=true` 这个参数对于私有化环境中没有证书的情况非常重要
* buildkit 还支持缓存持久化,从而加快构建速度,有兴趣的朋友可以翻阅官方文档,或帮助完善这里的例子
* Go 缓存代理是可选的,但推荐在内网中部署以加快依赖下载速度

## 循环任务
Argo Workflow 的 [Loop](https://argoproj.github.io/argo-workflows/walk-through/loops/) 功能,可以简化重复的任务,方便维护。以下是一个例子:

```shell
cat < /tmp/version' # 将期望输出的内容写入文件
outputs:
parameters:
- name: version
valueFrom:
path: /tmp/version # 读取容器中的文件,并作为内容输出到变量 version 中
- name: print
inputs:
parameters:
- name: version # 定义输入变量
container:
image: alpine
command:
- sh
- -c
- 'echo {{inputs.parameters.version}}'
EOF
```

## Webhook
所有主流 Git 仓库都是支持 webhook 的,借助 webhook 可以当代码发生变化后实时地触发工作流的执行。

Argo Workflows 利用 `WorkflowEventBinding` 将收到的 webhook 请求与 WorkflowTemplate 做关联。请参考下面的例子:

```shell
cat < values.yaml < \dt
List of relations
Schema | Name | Type | Owner
--------+--------------------------------+-------+-------
public | argo_archived_workflows | table | root
public | argo_archived_workflows_labels | table | root
public | argo_workflows | table | root
public | schema_history | table | root
(4 rows)

app_db=> select name,phase from argo_archived_workflows;
name | phase
--------------+-----------
plugin-pl6rx | Succeeded
plugin-8gs7c | Succeeded
```

## GC
Argo Workflows 有个工作流执行记录(Workflow)的清理机制,也就是 Garbage Collect(GC)。GC 机制可以避免有太多的执行记录,
防止 Kubernetes 的后端存储 Etcd 过载。

我们可以在 ConfigMap 中配置期望保留的工作执行记录数量,这里支持为不同状态的执行记录设定不同的保留数量。配置方法如下:

```yaml
apiVersion: v1
data:
retentionPolicy: |
completed: 3
failed: 3
errored: 3
kind: ConfigMap
metadata:
name: workflow-controller-configmap
namespace: argo
```

需要注意的是,这里的清理机制会将多余的 Workflow 资源从 Kubernetes 中删除。如果希望能更多历史记录的话,建议启用并配置好归档功能。

除了工作流有回收清理机制外,也可以针对 Pod 设置回收机制,参考配置如下:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: hello-world # Name of this Workflow
namespace: default
spec:
podGC:
strategy: OnPodCompletion
```

清理策略的可选值包括:

* `OnPodCompletion`
* `OnPodSuccess`
* `OnWorkflowCompletion`
* `OnWorkflowSuccess`

建议 PodGC 与日志持久化配合使用,不然可能会由于 Pod 被删除后无法查看工作流日志。

## 可观测
Argo Workflows 支持通过 Prometheus 采集监控指标,包括:[预定义、自定义](https://argoproj.github.io/argo-workflows/metrics/)的指标,下面是添加自定义指标的示例:

```yaml
spec:
metrics:
prometheus:
- name: exec_duration_gauge
labels:
- key: name
value: '{{workflow.name}}' # 工作流名称
- key: templatename
value: '{{workflow.labels.workflows.argoproj.io/workflow-template}}' # 工作流模板名称
- key: namespace
value: '{{workflow.namespace}}' # 工作流所在命名空间
help: Duration gauge by name
gauge:
value: '{{workflow.duration}}' # 工作流执行时长
- counter:
value: "1"
help: "Total count of all the failed workflows"
labels:
- key: name
value: '{{workflow.name}}'
- key: namespace
value: '{{workflow.namespace}}'
- key: templatename
value: '{{workflow.labels.workflows.argoproj.io/workflow-template}}'
name: failed_count
when: '{{workflow.status}} == Failed'
- counter:
value: "1"
help: "Total count of all the successed workflows"
labels:
- key: name
value: '{{workflow.name}}'
- key: namespace
value: '{{workflow.namespace}}'
- key: templatename
value: '{{workflow.labels.workflows.argoproj.io/workflow-template}}'
name: successed_count
when: '{{workflow.status}} == Succeeded'
- counter:
value: "1"
help: "Total count of all the workflows"
labels:
- key: name
value: '{{workflow.name}}'
- key: namespace
value: '{{workflow.namespace}}'
- key: templatename
value: '{{workflow.labels.workflows.argoproj.io/workflow-template}}'
name: total_count
```

上面包含了工作流的成功、失败、总量的数据指标。

## 工作流默认配置
在实际场景下,我们往往需要配置不少的工作流模板,而这些模板中也通常会有一些通用的配置项,例如:
拉取私有镜像的凭据、Pod 回收策略、卷挂载等待。我们可以把这些公共配置加到 ConfigMap 中,请参考如下:

```yaml
apiVersion: v1
data:
workflowDefaults: |
spec:
podGC:
strategy: OnPodCompletion # Pod 完成后即删除
imagePullSecrets:
- name: harbor-pull # 公共的私有镜像拉取凭据
volumeClaimTemplates: # 默认的代码拉取卷位置
- metadata:
name: work
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 64Mi
kind: ConfigMap
metadata:
name: workflow-controller-configmap
namespace: argo
```

## Golang SDK
Argo Workflows 官方[维护了 Golang、Java、Python 语言](https://argoproj.github.io/argo-workflows/client-libraries/)的 SDK。下面以 Golang 为例,讲解 SDK 的使用方法。

在运行下面的示例前,有两点需要注意的:

* Argo Workflows Server 地址
* Token

你可以选择直接使用 `argo-server` 的 Service 地址,将端口 `2746` 转发到本地,或将 Service 修改为 `NodePort`,或者其他方法暴露端口。也可以执行下面的命令,再启动一个 Argo 服务:

```shell
argo server
```

第二个,就是用户认证的问题了。如果你对 Kubernetes 认证系统非常熟悉的话,可以跳过这一段,直接找一个 Token。为了让你对 Argo 的用户认证更加了解,我们为下面的测试代码创建一个新的 ServiceAccount。

我们需要分别创建:

* Role,规定可以对哪些资源有哪些操作权限

```shell
kubectl create role demo --verb=get,list,update,create --resource=workflows.argoproj.io --resource=workflowtemplates.argoproj.io -n default
```

* ServiceAccount,代表一个用户

```shell
kubectl create serviceaccount demo -n default
```

* RoleBinding,将用户和角色(Role)进行绑定

```shell
kubectl create rolebinding demo --role=demo --serviceaccount=default:demo -n default
```

* Secret,关联一个 ServiceAccount,并自动生成 Token

```shell
kubectl apply -n default -f - < 上面的例子中,我们使用的是 `Role` 和 `RoleBinding` ,这样的角色只能允许访问所在命名空间(namespace)的资源。上面创建的用户,只能够访问 `default` 这命名空间下的 `Workflow` 和 `WorkflowTemplate` 。
> 如果想要创建一个全局的角色以及绑定,可以使用 `ClusterRole` 和 `ClusterRoleBinding` 。

上面的用户创建完成后,我们就可以通过下面的命令拿到指定权限的 `Token` 了:

```shell
kubectl get secret -n default demo.service-account-token -ojsonpath={.data.token}|base64 -d
```

接下来,创建一个 Golang 工程,并将下面的示例代码拷贝到源文件 `main.go` 中。

```shell
mkdir demo
cd demo
go mod init github.com/linuxsuren/demo
go get github.com/argoproj/argo-workflows/v3@v3.4.4
go mod tidy
```

示例代码:

```golang
package main

import (
"fmt"
"github.com/argoproj/argo-workflows/v3/pkg/apiclient"
"github.com/argoproj/argo-workflows/v3/pkg/apiclient/workflow"
"github.com/argoproj/argo-workflows/v3/pkg/apiclient/workflowtemplate"
"github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

/**
** Before run this demo, please create a parameterless WorkflowTemplate in namespace default.
** In this demo, we will print all the WorkflowTemplates in namespace default.
** Then run a Workflow base on the first WorkflowTemplate.
*/

func main() {
opt := apiclient.Opts{
ArgoServerOpts: apiclient.ArgoServerOpts{
URL: "localhost:31808", // argo-server address
Path: "/",
Secure: true,
InsecureSkipVerify: true,
},
AuthSupplier: func() string {
return "Bearer your-token"
},
}
ctx, client, err := apiclient.NewClientFromOpts(opt) // the context will carry on auth
if err != nil {
panic(err)
}

wftClient, err := client.NewWorkflowTemplateServiceClient()
if err != nil {
fmt.Println("failed to get the WorkflowTemplates client", err)
return
}
defaultNamespace := "default"

fmt.Println("get the WorkflowTemplate list from", defaultNamespace)
wftList, err := wftClient.ListWorkflowTemplates(ctx, &workflowtemplate.WorkflowTemplateListRequest{
Namespace: defaultNamespace,
})
if err != nil {
fmt.Println("failed to list WorkflowTemplates", err)
return
}
for _, wft := range wftList.Items {
fmt.Println(wft.Namespace, wft.Name)
}

if wftList.Items.Len() > 0 {
wft := wftList.Items[0]

wfClient := client.NewWorkflowServiceClient()
_, err := wfClient.CreateWorkflow(ctx, &workflow.WorkflowCreateRequest{
Namespace: defaultNamespace,
Workflow: &v1alpha1.Workflow{
ObjectMeta: metav1.ObjectMeta{
GenerateName: wft.Name,
},
Spec: v1alpha1.WorkflowSpec{
WorkflowTemplateRef: &v1alpha1.WorkflowTemplateRef{
Name: wft.Name,
},
},
},
})
if err != nil {
fmt.Println("failed to create workflow", err)
}
}
}
```

最后,执行命令:`go run .`

> 把上面的示例代码编译后,二进制文件大致在 60M+

## References
* [DevOps Practice Guide](https://github.com/LinuxSuRen/devops-practice-guide)
* [Argo CD Guide](https://github.com/LinuxSuRen/argo-cd-guide)
* [Argo Rollouts Guide](https://github.com/LinuxSuRen/argo-rollouts-guide)
* [更多场景下的模板样例](templates/README.md)