上面背景图片用了一张手枪扳机的图片,啥意思呢?trigger对于pipeline的作用就像扳机对于手枪的作用的,读完文章再来体会这句话。
前面我们学过tekton的pipeline组件,里面可以运行自定义工作流任务,而这些工作流任务例如task和pipeline都需要对象的触发对象例如taskrun和pipelineRun。触发的方式有两种:手动kubectl apply 文件触发和client-go触发。tekton为大家选择第三种方式,使用api接口触发。这就是本讲要着重讲的tekton的另一个组件trigger。
触发器Trigger使用户能够将事件中的字段映射到资源模板中,换句话说,这允许事件既可以建模也可以将其实例化为tekton资源。对于tektoncd / pipeline,触发器使得事件中的参数轻易封装到PipelineRuns、PipelineResources等对象中
上面这张是trigger的原理图,就是使用eventListener(事件监听器)监听事件,监听到事件后由triggerbinding将json数据解析并传送到triggerTemplate,而triggertemplate根据传入参数进行解析最后实例化tekton的资源(task,taskrun,pipeline等对象),要是实例化生成taskrun和pipelineRun,那就可以实现对流水线的触发。整个过程分工明确,每个对象都能做到解耦复用,设计理念非常棒。
TriggerTemplate, TriggerBinding, EventListener, ClusterTriggerBinding都是CRD对象,下面就TriggerTemplate, TriggerBinding, EventListener, ClusterTriggerBinding的使用进行介绍:
定义:TriggerTemplate是可以模板化tekton资源的资源,TriggerTemplate可使得参数在资源模板中任何位置被使用。简而言之,triggerTempate就像定义了一个完整的函数,它定义了函数所使用的形参和函数体,triggerTemplate就等着triggerBinding传入参数,然后实例化资源对象。
看下triggerTemplate的使用,你会更清楚。
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate #TriggerTemplate
metadata:
name: pipeline-template
spec:
params: #参数定义
- name: gitrevision
description: The git revision
default: master
- name: gitrepositoryurl
description: The git repository url
- name: message
description: The message to print
default: This is the default message
- name: contenttype
description: The Content-Type of the event
resourcetemplates: #资源模板, 将参数传入资源模板,实例化一个pipelinerun
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: simple-pipeline-run-
spec:
pipelineRef:
name: simple-pipeline #pipelineRun所运行的pipeline
params:
- name: message
value: $(params.message) #这些参数都是上面定义的参数
- name: contenttype
value: $(params.contenttype) #这些参数都是上面定义的参数
resources:
- name: git-source
resourceSpec:
type: git
params:
- name: revision
value: $(params.gitrevision)#这些参数都是上面定义的参数
- name: url
value: $(params.gitrepositoryurl)#这些参数都是上面定义的参数
与pipeline和task对象类似,TriggerTemplates并不会做任何实际的工作,而是充当应创建哪些资源的蓝图。上面的例子实例化了tekton的PipelineRun资源,你也可以创建其他tekton的资源。你可以先在集群中创建pipelineRun所使用的pipeline和task资源,毕竟pipelineRun只是对pipeline的一次调用,pipeline是可以复用的。当然,你也可以在triggertemplate中创建一套完整的task,pipeline,pipelineRun。但是这样的话就没有体会到tekton解耦的理念。反正能用就行,自己看场景发挥。
注意:如果省略命名空间,它将解析为EventListener的命名空间。
参数:TriggerTemplates可以声明由TriggerBinding或EventListener提供的参数。参数必须具有名称,并且可以具有可选的描述和默认值。可以使用以下变量替换语法在TriggerTemplate中引用参数,其中参数的名称为:
$(params.)
参数的目的是使TriggerTemplates可重用。
TriggerBindings是用于绑定事件的。使用TriggerBindings可以捕获事件中的字段并将其存储为参数。如下例:
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
name: pipeline-binding
spec:
params:
- name: gitrevision
value: $(body.head_commit.id)
- name: gitrepositoryurl
value: $(body.repository.url)
- name: contenttype
value: $(header.Content-Type)
TriggerBindings接收从EventListener发送过来的参数,然后传入TriggerTemplates,在该模板上实例化Pod,以“侦听”各个事件。
参数:TriggerBindings可以提供传递给TriggerTemplate需要的参数。每个参数都有一个名称和一个值。
事件变量:TriggerBindings可以使用$()中包装的JSONPath表达式来访问HTTP JSON正文和标头中的值。标头中的密钥不区分大小写。
这些都是有效表达式:
$(body.key1)
$(.body.key)
这些是无效表达式:
.body.key1 # INVALID - Not wrapped in $()
$({body) # INVALID - Ending curly brace absent
如果 ( ) 嵌 入 另 一 个 ()嵌入另一个 ()嵌入另一个()内(json中嵌入json的情况),我们将使用最里面的$()的内容作为JSONPath表达式
$($(body.b)) -> $(body.b)
$($($(body.b))) -> $(body.b)
带点的key怎么获取?访问包含.的JSON字符,我们需要转义。例如:
# body contains a filed called "tekton.dev" e.g. {"body": {"tekton.dev": "triggers"}}
$(body.tekton\.dev) -> "triggers"
举个使用例子:
`$(body)` 代表整个Body体中的json.
$(body) -> "{"key1": "value1", "key2": {"key3": "value3"}, "key4": ["value4", "value5", "value6"]}"
$(body.key1) -> "value1" 获取body中key1的值
$(body.key2) -> "{"key3": "value3"}" 获取body中key2的值
$(body.key2.key3) -> "value3"
$(body.key4[0]) -> "value4"
$(body.key4[0:2]) -> "{"value4", "value5"}"
# $(header) 代表事件中的header
$(header) -> "{"One":["one"], "Two":["one","two","three"]}"
$(header.One) -> "one"
$(header.one) -> "one"
$(header.Two) -> "one two three"
$(header.Two[1]) -> "two"
多个绑定:在EventListener中,您可以将多个绑定指定为触发器的一部分。这使您可以创建可重用的绑定,这些绑定可以与各种触发器混合并匹配。例如,触发器具有一个绑定,该绑定提取事件信息,而另一个绑定提供部署环境信息,如下:
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
name: event-binding
spec:
params:
- name: gitrevision
value: $(body.head_commit.id)
- name: gitrepositoryurl
value: $(body.repository.url)
---
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
name: prod-env
spec:
params:
- name: environment
value: prod
---
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
name: staging-env
spec:
params:
- name: environment
value: staging
---
apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener #在listener中组合binding和template
metadata:
name: listener
spec:
triggers:
- name: prod-trigger #触发器1
bindings: #trigger-binding #两个绑定
- name: event-binding
- name: prod-env
template: #引用已有的template
name: pipeline-template
- name: staging-trigger # 触发器2
bindings:# 引用定义的trigger-binding #两个绑定
- name: event-binding
- name: staging-env
template: #引用已有的template
name: pipeline-template
关于这个例子,之后我会出个实践例子。
EventListener也是trigger的CRD资源,它允许用户以声明方式处理带有JSON负载的基于HTTP的传入事件。用户可以声明TriggerBindings以从事件中提取字段,并将其应用于TriggerTemplates进而创建Tekton资源。此外,EventListeners允许使用事件拦截器进行轻量级的事件处理(这个之后有机会再讲)。
与在其他地方看到的通过kubectl或tkn进行的示例用法相比,EventListener实际上是Tekton的另一种客户端形式。特别是,基于HTTP的事件会绕过您通过kubeconfig文件和kubectl config系列命令获得的常规Kubernetes身份验证路径。所以,至少每个EventListener都需要拥有自己的ServiceAccount,并且所有接收的事件都会导致Tekton资源交互,而tekton资源交互使用的权限就是ServiceAccount所赋予的。但是,如果您需要跨各种触发器和拦截器对一组Tekton资源进行不同级别的许可,而并非所有触发器或拦截器都以相同的方式操作某些Tekton资源,那么简单的单个EventListener将无法满足要求。
您可以创建多个EventListener对象,如果在它们自己的命名空间中创建每个EventListener,则可以轻松地为每个ServiceAccount分配不同的权限来满足您的需求。通常,创建命名空间的同时还会定义一组默认的ServiceAccounts和Secrets。
缺点:从总体上讲,带有关联的Secrets和ServiceAccounts的命名空间被证明是etcd存储底层Kubernetes中最昂贵的项目(开销比较大)。在更大的集群中,etcd的存储容量可能成为一个问题。 多个EventListener意味着必须向访问“接收器”的外部实体公开多个HTTP端口,如果您的集群和外部实体之间恰好有一个HTTP防火墙,这意味着要增加管理成本,请在防火墙中为每个服务打开端口,除非您可以使用Kubernetes Ingress充当EventListeners集合的路由抽象层。
每个命名空间有多个EventListener,这很可能意味着管理员需要进行更多的ServiceAccount / Secret / RBAC操作。但是,通过减少命名空间的数量,您可以节省etcd的存储成本。 多个EventListener和潜在的防火墙问题仍然存在(同样,除非您使用Ingress)
能够在EventListenerTrigger上设置ServiceAccount的功能还允许获得更细粒度的权限。 您仍然必须创建其他ServiceAccounts。 但是,如果不使用Ingress,则留在1个命名空间内,并通过与其关联的“接收器”将EventListener的数量减至最少,可以将对etcd存储和端口注意事项的担心降至最低。
trigger作为触发器暴露服务的方式是通过http对外提供api接口,而对外提供http的具体实现,trigger这边提供了两种:使用k8s的ingress和使用Openshift Route。
trigger controller会为每一个建立的eventlistener创建对应的deployment,该deployment的副本数是1,你可以为该deployment创建service,而service的selector选择的标签是eventlistener: build-listener。build-listener是所创建的eventlistener的资源名称。如果你想将service暴露出去,可以使用Ingress的方式:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-resource
namespace: getting-started
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- path: /
backend:
serviceName: getting-started-listener-b8rqz # REPLACE WITH YOUR SERVICE NAME FROM STEP 2
servicePort: 8080
如果你的触发器只在内网中使用,那么service就够了。
trigger提供的第二种暴露事件监听器的方式是openshift,Openshift也是为了解决从集群外部(就是从除了集群节点以外的其它地方)访问服务的需求。我目前还没用,暂时不介绍用法,有兴趣的同学自行谷歌吧。
以上就是trigger中的主要内容,还存在一些例如拦截器和对接github/gitlab的内容,我将放在之后的实践文章中。本篇主要介绍了tekton的trigger组件中的核心概念:TriggerTemplate, TriggerBinding, EventListener。记住下面这张图,你就能掌握trigger运行原理:
看着这张图,就想到:事件发送给eventListener 的pod,pod讲事件中的参数解析给到triggerbinding,triggerbind找到关联的triggerTemplate并把参数传入,triggerTemplate实例化tekton中的资源(即创建),以上过程实现从一个http请求到运行一个完整的工作流。
如有问题,烦请留言。