基于argo项目里的argo-events和argo-workflow可以搭建持续集成CI的流程。通过gitlab触发argo-event支持添加的webhook来触发CI流程。argo-workflow可以定义集成的各个环节,如编译、打包、build镜像以及推送镜像到镜像仓库。本文将详细讲解各个流程,欢迎提问!
argo-events支持的触发机制如下:
eventsource的pod监听到event后,将event写入eventbus消息队列,sensor的pod读取自己订阅的事件后触发预定义的后续流程。
一、环境准备
使用argoCD在腾讯云集群上部署好argoEvents 和 argoWorkflow
argo开源项目访问地址:github.com/argoproj
二、过程
gitlab添加webhook钩子
- 设置触发源为gitlab无果
从官方文档 argoproj.github.io/argo-events… 中可以可以看出,argo-events可以直接配置触发源是gitlab.
我根据官网给的demo---github.com/argoproj/ar… 配置后,报错如下
kubectl logs gitlab-eventsource-lsbfm-5c69fb6989-cnkhn -n argo
"msg":"failed to create gitlab webhook for project 16668. err: json: cannot unmarshal array into Go value of type gitlab.ProjectHook"
尝试降低argo-events的版本也无果,最终决定迂回解决,不能被限死在这儿。
- 手动配置webhook
argo-events可以配置触发源是gitlab的原理是:调用gitlab的api接口向gitlab注册一个webhook钩子而已,这个webhook钩子其实就是类似一个回调函数,当gitlab代码仓发生某个event,比如push时,就会向webhook预先定义的一个服务发送一个http消息而已。
argo-events调gitlab接口添加webhook这个过程我们可以通过手动在gitlab上配置就行了。
gitlab配置位置如下
- 添加webhook-eventsource
webhook钩子需要配置url,也就是gitlab发生events后gitlab往什么目标发送消息
这个需要我们在集群里添加一个webhook,参考官方样例 github.com/argoproj/ar…
apiVersion: argoproj.io/v1alpha1
kind: EventSource
metadata:
name: webhook
annotations:
service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnetxxx
spec:
service:
type: LoadBalancer
ports:
- port: 12000
targetPort: 12000
webhook:
# event-source can run multiple HTTP servers. Simply define a unique port to start a new HTTP server
operation-back:
# port to run HTTP server on
port: "12000"
# endpoint to listen to
endpoint: /xxxxx
# HTTP request method to allow. In this case, only POST requests are accepted
method: POST
我的服务部署在腾讯云上,因为要能让gitlab访问到,我给webhook的service添加了一个type为LoadBalancer,但是发现没有效果,这个是crd不支持配置type,我只好直接去腾讯云的控制台手动配置service的网络类型为“内网LB”,因为我的gitlab也在内网,所以直接可以通。你可以根据你的需要配置成“外网LB”。
后来看到官方文档
argoproj.github.io/argo-events…
我们可以单独配置一个Service,然后用选择器
eventsource-name: webhook来选择eventsource生成的那个pod实例。
- gitlab触发webhook测试
你可以在添加webhook的下面看到所有你添加的webhook,点击右下角的Test,再选择一个events就可以出发想webhook预定义好的目标发送消息
你可以先查看集群里webhook的日志来确定有没有收到gitlab的消息
kubectl logs webhook-eventsource-mfzcs-7d7757cc6d-mxftm -n argo
配置sensor监听webhook事件,然后触发定义了CI流程的WorkflowTemplate
- 添加sensor 根据官方样例进行配置 github.com/argoproj/ar…
apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
name: gitlab
spec:
dependencies:
- name:test-back
eventSourceName: gitlab
eventName:test-back
triggers:
- template:
name: gitlab-workflow-trigger
k8s:
group: argoproj.io
version: v1alpha1
resource: workflows
operation: create
source:
resource:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: gitlab-workflow-
spec:
template:
- name: git-checkout
templateRef:
name:test-back
template:test-back
parameters:
- src:
dependencyName: a
dest: spec.arguments.parameters.0.value
你可以配置一个WorkflowTemplate来定义CI流程的各个流程,然后通过一个项目一个Sensor的方式给WorkflowTemplate传递参数。
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: test-back
namespace: argo
spec:
... ....
参考 使用Argo workflows构建java CI流水线
最终调用接口测试
curl --location --request POST 'http://1.117.170.20:32365/pipeline' \
--header 'Content-Type: application/json' \
--data-raw '{
"repo_url":"https://gitee.com/nativesailor/native-devops.git",
"branch":"master",
"app_code":"test",
"area":"zh",
"env":"dev",
"lang":"java",
"life_cycle":"deploy"
}'
报错
{"level":"error","ts":1671339252.5666783,"logger":"argo-events.sensor","caller":"sensors/listener.go:345","msg":"Failed to execute a trigger","sensorName":"sensor-for-pipeline","error":"failed to execute trigger, failed after retries: workflows.argoproj.io is forbidden: User \"system:serviceaccount:argo:default\" cannot create resource \"workflows\" in API group \"argoproj.io\" in the namespace \"argo\"","triggerName":"pipeline-workflow-trigger","triggeredBy":["pipeline"],"triggeredByEvents":["33376339613733372d383830622d346135612d396334342d373434326261633362643036"],"stacktrace":"github.com/argoproj/argo-events/sensors.(*SensorContext).triggerWithRateLimit\n\t/home/runner/work/argo-events/argo-events/sensors/listener.go:345"}
按example添加rbac后解决。 github.com/argoproj/ar…
如何区别分支
我们可以通过gitlab给webhook发送的reqest的body里的ref字段中获取到是哪个分支发生了event
webhook会将这个body都发送给sensor。 这是消息格式
参考: argoproj.github.io/argo-events…
可以从中提取出分支信息
parameters:
- src:
dependencyName: operation-back
dataTemplate: "{{ .Input.body.ref }}"
dest: spec.arguments.parameters.0.value
- src:
dependencyName: operation-back
dataTemplate: "{{ .Input.body.project.web_url }}"
dest: spec.arguments.parameters.1.value
我们需要截取ref才能得到分支名,使用go的插件Sprig可以实现
参考sprig提供的function
String Functions有类似java的subString
Get a substring from a string. It takes three parameters:
- start (int)
- end (int)
- string (string)
substr 0 5 "hello world"
The above returns `hello`
改成:
dataTemplate: "{{ .Input.body.ref | trimPrefix \"refs\/heads\/\" }}"
或者
dataTemplate: "{{ .Input.body.ref | substr 11 17 }}"
发现总是转化失败,试了好久,很奇怪upper title啥的都能支持,难道trimPrefix和substr就不支持???
后来才明白,应该是不支持 | 这种管道方式
改成
dataTemplate: "{{ trimPrefix \"refs/heads/\" .Input.body.ref }}"
就ok了
改进后的sensor
apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
name: webhook
spec:
# template:
# serviceAccountName: operate-workflow-sa
dependencies:
- name: operation
eventSourceName: webhook-for-java
eventName: operation
triggers:
- template:
conditions: "operation"
name: java-project-workflow-trigger
k8s:
group: argoproj.io
version: v1alpha1
resource: workflows
operation: create
source:
resource:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: THIS_WILL_BE_REPLACED_BY_PROJECT_NAME_FROM_EVENT
spec:
arguments:
parameters:
- name: branch
- name: web-url
- name: project-name
- name: timestamp
workflowTemplateRef:
inputs:
parameters:
- name: branch
- name: web-url
- name: project-name
- name: timestamp
name: java-operation
parameters:
- src:
dependencyName: operation
dataTemplate: "{{ trimPrefix \"refs/heads/\" .Input.body.ref }}"
dest: spec.arguments.parameters.0.value
- src:
dependencyName: operation
dataTemplate: "{{ trimPrefix \"https://\" .Input.body.project.web_url}}"
dest: spec.arguments.parameters.1.value
- src:
dependencyName: vue-operation
dataTemplate: "{{ lower .Input.body.project.path_with_namespace}}"
dest: spec.arguments.parameters.2.value
- src:
dependencyName: operation
dataTemplate: "{{ (first .Input.body.commits).timestamp | replace \":\" \"-\" }}"
dest: spec.arguments.parameters.3.value
# Apply parameters at the template level.
parameters:
- src:
dependencyName: vue-operation
dataTemplate: "{{ lower .Input.body.project.path_with_namespace | replace \"/\" \"-\" }}-{{ trimPrefix \"refs/heads/\" .Input.body.ref }}-"
dest: k8s.source.resource.metadata.generateName
dataTemplate: "{{ trimPrefix "refs/heads/" .Input.body.ref }}" 获取分支名
dataTemplate: "{{ trimPrefix "https://" .Input.body.project.web_url}}" 获取去掉“https://” 的代码仓地址
dataTemplate: "{{ lower .Input.body.project.path_with_namespace}}" 获取工程名
dataTemplate: "{{ (first .Input.body.commits).timestamp | replace ":" "-" }}" 获取触发时间
parameters:
- src:
dependencyName: vue-operation
dataTemplate: "{{ lower .Input.body.project.path_with_namespace | replace \"/\" \"-\" }}-{{ trimPrefix \"refs/heads/\" .Input.body.ref }}-"
dest: k8s.source.resource.metadata.generateName
[metadata.generateName: Invalid value: "xxxx/XXXX-FF/app-main-dev-": a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is 'a-z0-9?(\.a-z0-9?)*'),
metadata.generateName有规则,不能有斜杠或者大写字母,通过 lower .Input.body.project.path_with_namespace | replace "/" "-" 进行适配。