王厂长经营着一家叫「乐可可乐」的饮料厂。过去,他的产线是一条铁板一块的巨型机器——想换个瓶型,得停机拆装三天;想多灌一种口味,整条线都要返工。工程师们天天救火,产线却越改越僵。有一天,一位顾问告诉他:别再把产线做成「铁板一块」,把它拆成一排标准工位——贴标工位、灌装工位、压盖工位、装箱工位。每个工位只干一件事,都有自己的操作规范;工位之间用传送带连接,用共享仓库传递物料,用传感器自动开工。要换产品?换几个工位、调一下顺序就行。这套思路,正是云原生 CI/CD 框架 Tekton 的底层哲学。
Tekton 运行在 Kubernetes 之上,把「构建、测试、部署」这些 DevOps 动作,全都变成可以声明、复用、组合的积木。你写一份 YAML,声明「这条流水线由哪些任务组成」,剩下的交给 Tekton 去调度。本文会沿着工厂的故事,带你认识 Task、Pipeline、PipelineRun、Workspace、Trigger、Results 这些核心零件,从原理到实践,把一条可移植的云原生构建系统完整搭起来。
对 AI 从业者而言,Tekton 是打通「模型训练到模型上线」的标准化管道——掌握它,你就能把 ML 流水线以声明式方式固化到 Kubernetes,实现「代码即管道、管道即产品」,是 ML 工程化与平台工程方向的核心技能栈。
目录
可视化演示
动态展示Tekton组件化流水线、控制器执行机制、CI/CD阶段流程与Workspace/Results传递机制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
初识 Tekton:从一根流水线说起
王厂长的老朋友全是做软件的。有次他去硅谷参观,发现人家程序员「生产软件」的方式和自己的可乐厂惊人地相似:代码从提交到上线,也要经过编译、测试、打包、部署一道道工序,老办法是在一台服务器上写一堆又长又乱的脚本,谁改谁知道——这就像他那条「铁板一块」的旧产线,换个需求就要拆机器。于是开源社区造出了 Tekton:把 CI/CD 的每一道工序都做成一个标准「工位」,写在 Kubernetes 上,用声明式 YAML 描述整条产线,而不是写命令脚本。
声明式是什么感受?传统脚本像「派活的口令」:先运行这个命令,再 ssh 到那台机器,再执行那个脚本,顺序错了就崩。而 Tekton 的声明式只描述「最终要长成什么样」:我说这条流水线有「编译 → 测试 → 打包」三个工位,具体怎么调度、怎么并发、怎么重试,交给控制器去安排。你像厂长看图纸一样,一眼就能看清管道结构。这个特性让 CI/CD 从「手艺活」变成了「搭积木」:任何一个工位(Task)都能独立拆下来,装到别的流水线上复用。
组件化复用是 Tekton 的核心魅力。就像可乐厂可以把「贴标工位」原封不动搬到果汁产线,一个写好的 Task 也可以在多个 Pipeline 里被反复引用。为了让小白理解调度代价,我们引入一条贯穿全文的公式:整条流水线的耗时,等于所有并行路径中耗时最长的那条,而不是简单相加。
理解了关键路径,你就明白了 Tekton 调度器的良苦用心:它尽量把无依赖的任务并行化,让瓶颈只出现在真正串行的地方。这跟厂长把「吹瓶」和「调配糖浆」同时开工是同一个道理。回到工厂——铁板一块的死机器已经被拆掉,新的工位阵列开始有了雏形。
- 路径集合 Paths流水线中从起点到终点的所有可能依赖路径,每条路径由一串依次执行的任务组成。
- 路径求和把路径 p 上每个任务的耗时 t_i 累加,得到该路径的总耗时。
- 取最大值在所有路径耗时中挑最大的一个,它就是整条流水线无法再压缩的「关键路径」。
核心资源:工位卡片 Task 与标准动作 Step
厂长要给每个工位写一张「工位卡片」,上面列着这个工位要按顺序执行的每一步操作。Tekton 里,这张卡片就叫 Task,卡片上的一行行操作叫 Step。Task 是最小的工作单元,Step 则是这个工位里的一个个具体动作。比如「灌装工位」这个 Task,包含三个 Step:打开瓶盖、注入 500ml 可乐、拧紧瓶盖。
理解 Task 的关键在于理解 Step 的运行方式。每个 Step 都是一个 独立的容器镜像——就像工位上每个动作都由一台专门机器完成:注水机和拧盖机互不干扰。多个 Step 在同一个 Task 内按声明顺序依次执行,前一个 Step 退出码为 0 才轮到下一个。Task 还可以声明 params(参数),让工位卡片在重复使用时可以换参数,例如灌装工位接收「容量」参数,这次灌 500ml,下次灌 330ml,卡片不用重写。
从函数式编程的角度看,一个 Task 就是一组容器动作的顺序复合:
一个最小 Task 定义,用 YAML 写出来就是这样:metadata 里的 name 是工位名,spec.steps 下列出每个动作。实际使用中,Task 可以小到一条 echo,大到跑完整套模型训练脚本。写好的工位卡片可以收进共享「积木箱」,谁要谁取——这就是 Tekton 强大的组件复用基础。
- steps(Task)读取 Task 这个资源内部的「步骤列表」,列表长度 m 表示该工位有几个动作。
- exec(img_i)第 i 个 Step 会在镜像 img_i 对应的容器里运行,这个容器是临时创建的,运行完即销毁。
- 顺序执行方括号是「有序列表」符号——Tekton 保证 s_1 完成后才启动 s_2,这正是工位内动作的严格先后。
流水线运行:开工单 PipelineRun 与岗位单 TaskRun
产线图纸画好了,但图纸不会自己生产。王厂长每接到一笔订单,就要开一张「生产工单」,把排产要求写清楚,车间才真正动起来。Tekton 里,Pipeline 是图纸,PipelineRun 才是开工单——你定义一个 Pipeline 资源只是声明「有这么一条产线」,只有创建一个 PipelineRun 实例,控制器才会真正调度容器去执行。同一张图纸可以开无数张工单,对应同一条流水线的无数次运行。
同理,TaskRun 就是「岗位工单」。当 PipelineRun 启动后,控制器会把流水线上的每个 Task 实例化为一个 TaskRun,并记录这个 Task 某一次运行的输入、输出和状态。这种「定义与运行分离」的设计,让同一套声明可以被反复、并发地执行而不互相污染——就像王厂长用同一张图纸,同时给白班和夜班各开一张互不影响的工单。
每次运行,运行状态都会在几个固定状态间流转:
Runner 还支持 retries(重试次数)和 timeout(超时时间)——某个工位操作失手,可以允许它重来两次;某个工位卡死 10 分钟,自动宣告失败,避免整条产线无限等下去。这就像给每个工位装了「自动求救铃」和「超时断电保护」,让流水线在真实生产环境中足够健壮。
- 状态集合用花括号列出运行可能处于的全部状态,这是 Tekton 状态枚举的完整集合。
- 状态约束任意时刻
s只能取集合中的一个值,不会出现「既 Running 又 Succeeded」的模糊状态。 - 终态判定Succeeded 要求所有 Step 的退出码都为 0,只要有一个非 0,整体就进入 Failed。
工作空间:共享仓库 Workspace
新产线有个大改动:工位之间不再用「硬连接」的专用传送槽,而是统一到中间设一个共享仓库。贴标工位把贴好标的瓶子放进仓库,灌装工位从仓库取货,灌完再放回去。这样无论工位怎么换顺序、换组合,只要对接仓库就行。Tekton 里的 Workspace 就是这个共享仓库。
Workspace 是一个「共享存储抽象层」。Task 里声明 workspaces 字段,说明这个工位需要挂载一个货架;Pipeline 把不同 Task 的同一命名 Workspace 连到同一个实际存储后端,数据就打通了。后端类型非常灵活:PVC(持久卷)像大型恒温仓库,数据长期保存;ConfigMap 像公告板,适合放只读配置;EmptyDir 像随产线启停的临时棚,流水线一跑完就清空。
在每个 Task 的容器里,Workspace 会被挂载成一个真实路径,容器内读写的文件就会落到共享仓库中。这个「挂载」操作可以形式化地描述为:
用 Workspace 要小心并发:多个 Task 同时写同一个文件会互相覆盖,就像两个工人抢同一支记号笔。所以最佳实践是各 Task 使用不同的子目录,把仓库划分成各自的区域。仓库让产线解耦——工位不认识彼此,只认识货架,这是云原生流水线可移植性的基石。
- 任务与仓库建立映射将 Task 需要的每个工作空间 W_i 对应到容器内的一个目录 path_i。
- 集合表示全部挂载花括号内的集合表示 Task 中所有挂载关系的总和,k 个声明就有 k 个映射。
- 目录即约定如果编译 Task 约定把产物写到
/workspace/output,打包 Task 也从同一路径读取,两者就对接上了——这是工位间协作的「通用语言」。
触发机制:传感器 Trigger
产线不能永远靠人工按开关。王厂长在传送带入口装了一批传感器:空瓶到位,自动启动灌装;订单系统弹出一条「加急 1000 箱」,产线自动插入一条临时任务。Tekton 把这些自动化启动的机制统称为 Trigger(触发)。
Trigger 是一组协同工作的组件。EventListener 是传感器本身——它暴露一个 HTTP 服务地址(Webhook),Git 提交、镜像推送、定时器都会向这个地址发「信号」;TriggerBinding 负责读懂信号:从 JSON 事件里提取 branch、commit SHA、镜像标签等关键字段;TriggerTemplate 是「排产模板」:接收提取出来的参数,统一生成一个 PipelineRun 开工单。三者串联,真正做到了「事件来了,流水线自动开跑」。
这个串联过程可以画成一条清晰的流水线:
有了 Trigger,DevOps 团队才能实现真正的 GitOps:开发者只需要把代码推到主干分支,剩下的构建、测试、部署全部由 Tekton 自动接管。厂长终于可以放心去吃午饭了——传感器看场子,流水线自己知道自己该干什么。
- 事件输入Event 是外部系统发出的原始信息,比如一次 git push 请求。
- Binding 解析TriggerBinding 按声明好的映射规则,从 Event 中抓取字段,产出键值对形式的参数。
- Template 渲染TriggerTemplate 把参数填入预定义的 PipelineRun 模板,实例化出真正的运行对象。
结果传递:质检标签 Results
可乐产线上,每个工位完成工作后,都要在瓶身上贴一张「质检标签」:灌装工位贴上「净含量 500ml」,压盖工位读到「已灌装」才动手压盖,装箱工位统计标签上的数据决定装箱数量。Tekton 里的 Results 就是这种跨工位传递的小体积文本结果。
Task 在 results 字段里声明自己要输出的结果名,执行时把值写入指定路径;下游 Task 通过一个特殊的引用语法把上游结果取进来用。比如编译 Task 声明了一个名叫 jar_path 的 result,值是一个 jar 包名,打包 Task 想引用它,只需写 $(results.compile.jar_path)。这句话可以拆成三部分理解:
Results 适合传递小体积元数据;大体积文件请走 Workspace。除了结果传递,Tekton 还支持 Matrix(矩阵):给同一 Task 的不同参数组合并行铺开多路执行,例如一组测试用例矩阵同时跑 20 个压测工位,最后汇总。质检标签 + 并行矩阵,让流水线既能灵活接力,又能大规模加速。
$(...) 是引用前缀,results 声明这是取结果,中间两段分别指定「哪个任务」的「哪个结果」。Tekton 在调度前会把这个表达式替换成实际值。- 引用前缀
$(和)框出一个待替换的变量表达式,类似模板字符串的占位符。 - 任务标识
<task>段指向上游某个 Task 的名字,表示「我要拿那个工位的结果」。 - 结果标识
<result>段指定该 Task 输出结果的名字,最终表达式被替换成真实文本。
实践落地:让工厂真正跑起来
理论讲完,王厂长要动手建厂了。第一步是安装 Tekton Pipelines:在 Kubernetes 集群里执行一条 kubectl apply 安装官方 manifests——相当于把整条产线的「操作系统」装好。装完就能用 kubectl 或 tkn 命令行工具与 Tekton 交互了。
第二步是编写最小示例。一个小到不能再小的流程:先定义一个 Task,执行一句问候;再定义一个 Pipeline 引用这个 Task;最后创建 PipelineRun 触发执行。YAML 长这样:
把这堆 YAML 用 kubectl apply -f 提交,几秒后 kubectl get pipelinerun 就能看到 Succeeded。如果失败,排查三板斧:tkn pipelinerun logs -f <run名> 看整条流水线日志;kubectl get taskrun 定位是哪个岗位单挂了;再 kubectl logs <pod> 看具体容器的输出。大多数问题出在镜像拉不下来、路径写错、参数没传全这三处。
最佳实践总结成四句话:Task 尽量小且单一职责(一个工位只干一件事);产物优先走 Results 传递,大文件走 Workspace;Pipeline 里用 runAfter 显式声明依赖,别指望「恰好顺序」;所有镜像固定 tag,保证可重复构建。厂长看着已经跑通的首条流水线,想起那条拆掉的铁板机器——现在换瓶型只需换工位卡片,加口味只需并联一串新工位,他第一次觉得,生产线的未来真的可以像乐高一样自由。
apiVersion: tekton.dev/v1kind: Taskmetadata: name: say-hellospec: steps: - name: greeting image: alpine:3.18 script: | echo "hello, tekton"---apiVersion: tekton.dev/v1kind: Pipelinemetadata: name: hello-pipelinespec: tasks: - name: first taskRef: name: say-hello---apiVersion: tekton.dev/v1kind: PipelineRunmetadata: name: hello-pipeline-runspec: pipelineRef: name: hello-pipeline
AI 应用场景
场景一:ML 模型的自动化训练与发布管道
阶段:模型训练与交付(MLOps 的 CI 环节)
解决的问题:传统 AI 团队训练模型靠「个人笔记本 + 手工拷贝」,数据集一换、参数一改,整个流程要手动重跑一遍,环境依赖极易出错。用 Tekton 把「数据预处理 → 模型训练 → 评估 → 导出模型」声明为四个 Task,每次代码提交自动触发整条流水线,模型产物写入共享 Workspace,评估指标通过 Results 传给通知服务。
具体案例:某推荐系统团队每天十几次实验提交,过去每周浪费 20 人时在「传数据、配环境、跑脚本」上;接入 Tekton 后,实验完全自动化,每个 commit 自动产出带评估分数的模型报告,效果比对一目了然。
场景二:微服务团队的 GitOps 多环境发布
阶段:持续交付(CD 环节)
解决的问题:微服务数量爆炸后,逐个 kubectl rollout 手工发布既慢又容易出错。Tekton Pipeline 构建「构建镜像 → 推送仓库 → 更新 K8s YAML → 滚动发布 → 健康检查」的完整链路,配合 Trigger 监听 Git 提交,实现「推代码即发布」。
具体案例:一个 30 人团队维护 40 个微服务,用 Tekton 加 Argo CD 梳理出 dev/staging/prod 三条 PipelineRun 模板,版本发布从人均 40 分钟降到全自动 5 分钟,回滚只需重新触发上一版本号。
场景三:多任务并行测试矩阵
阶段:质量保障与测试
解决的问题:单机串行跑几千条回归测试要数小时,而 Tekton 的 Matrix 能力可以把同一测试 Task 按不同参数(浏览器、数据集、分片)并行铺到多个节点,总耗时被关键路径大幅压缩。
具体案例:某电商平台在发版前需要跑 3000 个端到端用例,按 6 种浏览器 × 4 个数据分片组成 24 路矩阵并行执行,在集群 8 节点上把耗时从 3 小时压到 20 分钟。
Python 演示
import time class Task: def __init__(self, name, steps): self.name = name self.steps = steps # 每个元素: (container, function) def run(self, workspace, params): print(f"[Task] {self.name} 启动") for container, action in self.steps: print(f" [Step] 容器={container} -> {action.__name__}") time.sleep(0.05) result = action(params) workspace[self.name] = result print(f"[Task] {self.name} 完成, Result={workspace[self.name]}") class Pipeline: def __init__(self, name, tasks): self.name = name self.tasks = tasks def run(self, workspace): print(f"=== Pipeline {self.name} 开始 ===") for task in self.tasks: task.run(workspace, {}) print(f"=== Pipeline {self.name} 成功 ===") def compile_source(params): return "app.jar" def build_image(params): return "registry.local/app:v1" def run_tests(params): return "tests-passed" if __name__ == "__main__": shared_workspace = {} pipeline = Pipeline("build-test-pipeline", [ Task("compile", [("maven:3.8", compile_source)]), Task("test", [("junit:5", run_tests)]), Task("package", [("docker:20", build_image)]), ]) pipeline.run(shared_workspace) print("共享工作区最终内容:", shared_workspace)
class Task模拟一个 Tekton Task,构造时接收工位名和步骤列表。def run(self, workspace, params)模拟 Task 执行:逐个 Step 跑,结果写入共享工作区。workspace[self.name] = result对应 Tekton 中 Task 把 Results 写入共享存储。class Pipeline模拟一条流水线,按顺序把 Task 串起来执行。def run(self, workspace)循环调用各 Task,对应 PipelineRun 的串行调度。compile_source / build_image / run_tests模拟每个 Step 里的容器动作函数。shared_workspace = {}模拟 Workspace:一个全流程共享的 Python 字典。pipeline.run(shared_workspace)相当于创建 PipelineRun 并等待完成。
常见误区
误区我以为写好了 Pipeline YAML 并 `kubectl apply` 之后,流水线就自动开始跑了,结果等半天什么都没发生,差点以为 Tekton 装坏了。
正解Pipeline 只是「图纸」,图纸不会自己生产。必须再创建一个 PipelineRun 实例(或配置 Trigger 自动创建),控制器才会真正调度容器执行。就像可乐厂的产线图纸挂在墙上,要等厂长开「生产工单」才会动工。
为何常见:习惯了「声明后即刻生效」的 Deployment 模型,容易忘掉 Tekton 的「声明与执行分离」设计,其实这正是为了支持同一图纸反复开单。
误区我以为 Task A 写完的文件会自动出现在 Task B 里,毕竟它们在同一台集群上,结果一运行 B 发现目录是空的。
正解每个 Task 的每个 Step 都是独立容器,默认彼此隔离,文件系统互不可见。要传文件必须挂载同一个 Workspace,或用 Results 传文本。就像两个相邻工位不会共享工具台,必须通过公共仓库转运物料。
为何常见:习惯把 CI 脚本当作「同一台机器上的 shell 脚本」,忽略了云原生容器隔离这一根本前提。
误区一个 Task 里的三个 Step 应该像 Shell 脚本里三行命令一样,能随意读取彼此设置的环境变量吧?
正解Task 内的多个 Step 也是不同的容器,虽然顺序执行,但环境变量、进程空间都不共享。共享数据要么通过 Workspace 落盘,要么通过显式的 params 传值。就好比灌装工位的注水机不会自动知道压盖工位的力矩设定,必须写在工位卡片(Task 配置)里。
为何常见:把「组合」误当成「合体」,实际上 Tekton 强调的最小隔离让每个 Step 的镜像和环境都彼此独立。
误区为了省事,我把数据库密码和产物路径都写在 Task YAML 里,反正集群内网别人看不到。
正解Task 应该参数化:密码放进 Kubernetes Secret 再挂载为 Workspace,路径通过 params 传入。这样 Task 才能跨团队、跨环境复用。就像工位卡片上不该写死「用老王的密码开仓库门」,而应写「凭门禁卡进仓库」。
为何常见:初学阶段往往只关注「跑通」,缺少可移植性思维,等要复用到另一个项目时就只能复制粘贴、到处改配置。
面试题
Q1. 声明了一个 Pipeline 资源后,要真正执行流水线必须创建哪个资源?
- A.创建 Task 实例
- B.修改 Pipeline 定义
- C.创建 PipelineRun← 正确答案
- D.部署事件监听器
答案:C。Pipeline 是「图纸」,只有创建 PipelineRun 这个「开工单」才会调度执行。Task 是组成图纸的工位;修改 Pipeline 只会改图纸;事件监听器属于 Trigger,只在配置了自动触发时才会间接创建 PipelineRun。
Q2. 同一个 Task 内的多个 Step 默认如何执行?
- A.所有 Step 并行执行
- B.按声明顺序执行← 正确答案
- C.随机顺序执行
- D.由镜像启动快慢决定
答案:B。Task 内 Step 是串行的:前一个 Step 必须以退出码 0 结束,后一个才启动。虽然它们在不同容器运行,但执行顺序由 steps 列表的次序严格决定,并行发生在 Task 与 Task 之间。
Q3. 两个 Task 之间需要共享一个较大的产物文件,推荐使用什么方式?
- A.挂载同一个 Workspace← 正确答案
- B.写进容器镜像缓存
- C.用全局环境变量
- D.让任务在同一 Pod 驻留
答案:A。Workspace 是共享存储抽象,适合大体积文件传递。容器镜像缓存不可写回;环境变量只能传小文本且不跨容器;多个 Task 默认在不同 Pod 运行,不会因 Task 在同一 Pod 就共享文件。
Q4. Results 机制适合传递什么类型的数据?
- A.大型二进制数据集
- B.小体积文本结果← 正确答案
- C.整个容器镜像
- D.数据库全量备份
答案:B。Results 把结果写入 K8s 资源状态中,适合 jar 包名、镜像 digest、测试统计数等小文本。大体积数据应放 Workspace,镜像和数据库备份属于外部存储系统管理范畴。
Q5. PipelineRun 的正确状态流转顺序是?
- A.Pending → Succeeded → Failed
- B.Pending → Running → Succeeded/Failed← 正确答案
- C.Running → Pending → Succeeded
- D.Failed → Running → Succeeded
答案:B。状态先 Pending 排队,容器启动后进入 Running,最终根据所有 Step 退出码进入唯一的终态 Succeeded 或 Failed。A 的 Failed 是从 Succeeded 之后再失败,不符合单终态语义;C、D 顺序颠倒。
Q6. 以下哪个不是 Tekton Workspace 的内置后端类型?
- A.PersistentVolumeClaim
- B.ConfigMap
- C.EmptyDir
- D.独立物理硬盘← 正确答案
答案:D。Tekton 支持 PVC、ConfigMap、EmptyDir、Secret 等 K8s 原生卷类型。独立物理硬盘不是 K8s 抽象,需先封装成 PV/PVC 才能使用。选 PVC 可持久保存,EmptyDir 则随运行结束释放。
Q7. Trigger 组件链中,负责从 Webhook 事件里提取字段的是哪个组件?
- A.EventListener
- B.TriggerTemplate
- C.TriggerBinding← 正确答案
- D.Interceptor
答案:C。EventListener 负责接收事件;TriggerBinding 负责把事件 JSON 映射成参数;TriggerTemplate 负责用参数渲染 PipelineRun。Interceptor 可选,用来过滤或修改事件,不属于必须的三件套。
Q8. 以下属于 Tekton 设计目标的有哪些?
- A.与 Kubernetes 深度集成← 正确答案
- B.Task 可复用与组合← 正确答案
- C.流水线声明式定义← 正确答案
- D.内置可视化 UI 平台
答案:ABC。Tekton 构建在 K8s CRD 之上,天然深度集成;Task 设计为独立可复用单元;流水线用声明式 YAML 描述。选项 D 错误,Tekton 是底层引擎,UI 由社区工具(如 Tekton Dashboard)另行提供。
Q9. 创建 PipelineRun 之前,通常需要满足哪些前提?
- A.引用的 Task 已存在← 正确答案
- B.所需参数已经提供← 正确答案
- C.Workspace 已绑定后端← 正确答案
- D.镜像已推送到本地 Docker
答案:ABC。Task 不存在会导致运行失败;参数缺失会校验不过;Task 声明了 Workspace 就必须在 PipelineRun 里绑定实际存储。D 错误:Kubernetes 集群从远程仓库拉镜像,和本地 Docker 无关。
Q10. 下列属于 Tekton 核心概念的有哪些?
- A.Task 任务单元← 正确答案
- B.PipelineRun 运行实例← 正确答案
- C.Workspace 共享工作区← 正确答案
- D.Deployment 无状态应用
答案:ABC。Task 是最小工作单元,PipelineRun 是执行实例,Workspace 是共享存储,三者是 Tekton 核心资源。Deployment 是 K8s 原生工作负载概念,不属于 Tekton 流水线的核心概念。