第 12/60 天
引言
前几篇文章我们学会了如何编写 Task、编排 Pipeline,但每次运行都要手动创建 PipelineRun——这在生产环境显然不可接受。真正的 CI 应该是「代码一提交,流水线自动跑起来」。Tekton 官方为此提供了独立的组件 Tekton Triggers,它监听外部事件(最典型的就是 Git 仓库的 Webhook),把事件载荷中的关键信息(分支、commit SHA、仓库地址)提取出来,自动生成并执行对应的 PipelineRun。
本文将深入讲解 Tekton Triggers 的三大核心 CRD:EventListener(事件入口)、TriggerBinding(数据绑定)、TriggerTemplate(资源模板),以及用于安全校验的 Interceptor(拦截器)。通过一个完整的 GitHub push 事件 → 自动触发构建流水线的实战案例,让你掌握事件驱动 CI 的全链路搭建方法。
核心概念
事件流全景
一次 Webhook 触发的完整链路如下:
Git 仓库 push
│ 发送 HTTP POST(JSON 载荷 + 签名头)
▼
EventListener(常驻 Pod,监听 8080 端口)
│ 1. Interceptor 校验签名、过滤事件类型/分支
▼
TriggerBinding(从载荷提取参数)
│ 2. body.ref、body.head_commit.id、body.repository.clone_url ...
▼
TriggerTemplate(生成资源)
│ 3. 用 $(tt.params.xxx) 填充 PipelineRun 字段
▼
PipelineRun(真正执行 CI 流水线)
四个核心 CRD
| 组件 | 类型 | 职责 | 类比 |
|---|---|---|---|
| EventListener | Service + Pod | 暴露 HTTP 端点,接收并分发事件 | 邮局的收件窗口 |
| Interceptor | 内嵌插件 | 校验签名、过滤事件、修改载荷 | 安检与分拣员 |
| TriggerBinding | 参数提取 | 用 $(body.xxx) 从 JSON 载荷取值 |
拆信封取信件 |
| TriggerTemplate | 资源模板 | 定义要创建的 Kubernetes 资源(PipelineRun) | 填好的申请表 |
v1 版本的 Note:
triggers.tekton.dev/v1提供独立的 Trigger CRD(把 Binding + Template + Interceptors 组合成一个对象),EventListener 通过name引用它;也可以在 EventListener 的spec.triggers里内联定义。两种写法等价,本文以独立 Trigger 为例(结构更清晰)。
取值语法速查
| 表达式 | 含义 |
|---|---|
$(body.xxx) |
从 Webhook JSON 载荷取值(点号路径) |
$(header.xxx) |
从 HTTP 请求头取值 |
$(tt.params.xxx) |
引用 TriggerTemplate 中定义的参数 |
$(extensions.xxx) |
引用 Interceptor 处理后的扩展字段 |
实战步骤
1. 安装 Tekton Triggers
确保已经安装了 Tekton Pipelines(第 8 天内容),然后安装 Triggers:
# 安装最新稳定版(所有 CRD、Controller、Webhook 一次性部署到 tekton-pipelines 命名空间)
kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml
# 生产环境建议锁定具体版本,避免升级漂移
# kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/previous/v0.28.0/release.yaml
# 验证安装
kubectl get pods -n tekton-pipelines | grep triggers
kubectl get crd | grep triggers
2. 创建凭据:GitHub Webhook Secret
在 GitHub Webhook 配置里填写的 Secret,用于 Interceptor 校验 X-Hub-Signature-256 签名:
# 生成随机 token 并写入 K8s Secret
kubectl create secret generic github-secret
-n tekton-pipelines
--from-literal=secretToken="$(openssl rand -hex 24)"
--dry-run=client -o yaml | kubectl apply -f -
3. 创建 TriggerTemplate(定义生成的 PipelineRun)
apiVersion: triggers.tekton.dev/v1
kind: TriggerTemplate
metadata:
name: ci-pipeline-template
namespace: tekton-pipelines
spec:
params:
- name: git-repo-url # 仓库地址
- name: git-revision # commit SHA
- name: git-branch # 分支全名,如 refs/heads/main
resourcetemplates:
- apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: ci-run- # 自动生成唯一名称
labels:
app: demo-app
tekton.dev/trigger: github-push
spec:
serviceAccountName: build-bot
pipelineRef:
name: ci-pipeline # 第 10 天创建的构建流水线
params:
- name: repo-url
value: $(tt.params.git-repo-url)
- name: revision
value: $(tt.params.git-revision)
- name: branch
value: $(tt.params.git-branch)
workspaces:
- name: source
volumeClaimTemplate: # 自动创建 PVC,用完即清
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi
4. 创建 TriggerBinding(从事件载荷提取参数)
apiVersion: triggers.tekton.dev/v1
kind: TriggerBinding
metadata:
name: github-push-binding
namespace: tekton-pipelines
spec:
params:
- name: git-revision
value: $(body.head_commit.id) # 最新一次 commit 的 SHA
- name: git-repo-url
value: $(body.repository.clone_url) # 如 https://github.com/example/demo-app.git
- name: git-branch
value: $(body.ref) # 如 refs/heads/main
5. 创建 Trigger 与 EventListener(入口 + 安全校验 + 分发)
apiVersion: triggers.tekton.dev/v1
kind: Trigger
metadata:
name: github-push-trigger
namespace: tekton-pipelines
spec:
interceptors:
- ref:
name: "github" # 官方 GitHub 拦截器:校验签名
params:
- name: "secretRef"
value:
secretName: github-secret # 与步骤 2 创建的 Secret 对应
secretKey: secretToken
- name: "eventTypes"
value: ["push", "pull_request"] # 只接受这两种事件
bindings:
- ref: github-push-binding
template:
ref: ci-pipeline-template
---
apiVersion: triggers.tekton.dev/v1
kind: EventListener
metadata:
name: ci-event-listener
namespace: tekton-pipelines
spec:
serviceAccountName: tekton-triggers-sa # 需要具备创建 PipelineRun 的 RBAC 权限
triggers:
- name: github-push-trigger # 引用上面的 Trigger
EventListener 会自动创建一个名为 el-ci-event-listener 的 Service(默认 ClusterIP,端口 8080),以及接收事件的 Pod。
6. 暴露 EventListener 到公网
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: el-ci-event-listener
namespace: tekton-pipelines
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
ingressClassName: nginx
rules:
- host: events.stellardata.top
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: el-ci-event-listener
port:
number: 8080
7. 本地联调:模拟 GitHub Webhook
先把事件入口转发到本地,用伪造的 GitHub push 载荷测试:
# 端口转发(另开一个终端)
kubectl port-forward -n tekton-pipelines service/el-ci-event-listener 8080:8080
{
"ref": "refs/heads/main",
"head_commit": {
"id": "9f8e7d6c5b4a3c2d1e0f1234567890abcdef1234"
},
"repository": {
"clone_url": "https://github.com/example/demo-app.git"
}
}
# 用与 Webhook Secret 相同的 token 计算 HMAC-SHA256 签名
SECRET="your-github-webhook-secret"
SIG=$(echo -n "$(cat /tmp/gh-payload.json)" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print "sha256="$2}')
# 发送模拟请求
curl -v -X POST http://localhost:8080
-H "Content-Type: application/json"
-H "X-GitHub-Event: push"
-H "X-Hub-Signature-256: $SIG"
--data @/tmp/gh-payload.json
# 验证流水线被自动创建并执行
kubectl get pipelineruns -n tekton-pipelines -l tekton.dev/trigger=github-push
签名错误(或缺失)时,EventListener 会返回 403 并拒绝触发;签名正确时返回 202 Accepted,并在几秒内看到新的 PipelineRun。
8. 配置 GitHub 仓库 Webhook
到 GitHub 仓库 Settings → Webhooks → Add webhook 填写:
| 字段 | 值 |
|---|---|
| Payload URL | https://events.stellardata.top/ |
| Content type | application/json |
| Secret | 与步骤 2 生成的 token 一致 |
| 触发事件 | 勾选 Just the push event(或按需勾选 Pull requests) |
保存后 GitHub 会立即发送一条 ping 事件,可在 Webhook 页面看到绿色勾(202 响应),说明链路已打通。
9. 进阶:CEL Interceptor 过滤分支
只允许 main 分支触发,并把 commit SHA 截短为 7 位传给下游:
apiVersion: triggers.tekton.dev/v1
kind: Trigger
metadata:
name: github-main-trigger
namespace: tekton-pipelines
spec:
interceptors:
- ref:
name: "github"
params:
- name: "secretRef"
value: { secretName: github-secret, secretKey: secretToken }
- ref:
name: "cel"
params:
- name: "filter"
value: "body.ref == 'refs/heads/main'"
- name: "overlays"
value:
- key: truncated-sha
expression: "body.head_commit.id.truncate(7)"
bindings:
- ref: github-push-binding
template:
ref: ci-pipeline-template
CEL 过滤不满足时请求返回 200(不报错但也不创建资源),避免因「拒绝」在 GitHub 后台刷红叉。
常见问题
Q1: EventListener 的 Service 类型怎么选?
ClusterIP 适合集群内部调用或搭配 Ingress/API 网关;NodePort 适合无 Ingress 的裸金属环境做简单暴露;LoadBalancer 适合云厂商直接分配公网 IP。生产环境推荐 Ingress + ClusterIP,由网关统一管理 TLS 与域名。
Q2: GitHub Webhook 一直显示签名校验失败(403)?
最常见的原因有三个:一是 GitHub Webhook 里的 Secret 与 github-secret Secret 中 secretToken 的值不一致;二是 secretRef 的 name/secretKey 写错;三是签名计算方式不对——流程是先拼接 JSON 请求体(原始 body 字符串)与 Secret 做 HMAC-SHA256,前缀 sha256=,任何转义或换行差异都会导致签名不匹配。可用步骤 7 的 curl 脚本与 openssl dgst 在本地先验证。
Q3: 如何让不同分支触发不同的流水线?
用多个 Trigger 配合 CEL filter:例如 body.ref == 'refs/heads/main' 绑定生产流水线模板,body.ref.startsWith('refs/heads/feature') 绑定开发流水线模板。也可以在同一个 TriggerTemplate 里通过 $(body.ref) 动态决定参数,再让流水线内部用 when 表达式(Tekton v1 的条件)做分支判断。
Q4: 一个 EventListener 可以挂多个 Trigger 吗?
可以。EventListener 的 spec.triggers 支持多个条目,每个条目可以引用不同的 Trigger(或内联定义),EventListener 会根据 URL 路径、Interceptor 过滤结果把事件路由到匹配的 Trigger。生产环境常用模式是:一个仓库一个 EventListener,内部挂「push 触发构建」「PR 触发测试」等多个 Trigger。
Q5: EventListener 挂了会影响生产吗?如何高可用?
EventListener 本质是无状态 Pod,Controller 会保证一个副本持续运行;同时它支持 spec.replicas 字段扩多副本(自动生成对应 Deployment)。更稳妥的做法是:在 Ingress/网关层面启用重试,并在推送端保留事件日志;对要求严格不丢事件的场景,可开启 Tekton Triggers 的 delivery 功能(把失败事件投递到 Dead Letter 队列),或在 CI 侧做「轮询补偿」(如 Argo CD 的 Image Updater 定期检查新镜像,第 31 天会讲)。
总结
- 事件驱动是生产 CI 的标配:Tekton Triggers 把「Webhook 事件 → PipelineRun」这条链路标准化,开发者只需 push 代码即可触发完整构建流程
- 三件套各司其职:EventListener 负责接收 HTTP 事件,TriggerBinding 负责从 JSON 载荷提取参数,TriggerTemplate 负责生成 PipelineRun——理解这个数据流就掌握了 Triggers 的核心
- 安全校验不可跳过:生产环境必须使用 Interceptor(GitHub/GitLab/CEL)校验签名并过滤事件类型,否则任何人都能伪造请求触发你的流水线
- CEL 拦截器是精细化控制的关键:分支过滤、字段截断、载荷改写都可以用 CEL 表达式完成,一个表达式就能实现「只对 main 分支触发」
- 与后续篇目衔接:Triggers 触发构建后,镜像将推送至 Harbor(第 29 天完整流水线),再由 Argo CD 拉取部署(第 30 天)——事件驱动只是全链路的起点















暂无评论内容