第 10/60 天
引言
在上一篇文章中,我们学习了如何编写 Tekton 的 Task 和 Step,构建了第一个构建任务。然而,真实的 CI 流水线远不止一个任务——代码检出、单元测试、代码检查、镜像构建、安全扫描、制品推送……每一个环节都需要独立的任务,并且它们之间存在明确的前后依赖关系。
Tekton Pipeline 正是为此而生。Pipeline 是 Tekton 的”编排层”,它通过声明式 YAML 来定义多个 Task 的执行顺序、并行关系、数据传递和条件分支。本文将深入讲解 Pipeline 的核心编排机制,并通过一个完整的 CI 流水线示例,展示如何将多个 Task 组合成可复用、可观测的多阶段流水线。
核心概念
Pipeline 的编排原语
| 概念 | 说明 | 类比 |
|---|---|---|
| Pipeline | 一系列 Task 的有序集合 | 工作流定义 |
| PipelineRun | Pipeline 的一次执行实例 | 运行记录 |
| Task | 流水线中的最小执行单元 | 单个工序 |
| runAfter | 声明 Task 之间的显式依赖关系 | 先后顺序 |
| from | 声明对上一个 Task 的 Results 依赖 | 数据传递 |
| when | 条件执行,满足条件才运行 Task | 分支判断 |
| finally | 无论成功失败都会执行的收尾任务 | 清理/通知 |
流水线编排的三种模式
- 串行模式:Task A → Task B → Task C,使用
runAfter声明依赖 - 并行模式:Task A 完成后,B 和 C 同时执行,互不依赖
- 扇入/扇出模式:多个上游 Task 完成后,汇聚到下游 Task
数据传递机制
Pipeline 中的 Task 通过以下方式交换数据:
- Workspaces:共享卷挂载,适用于大文件传递(如源码目录)
- Results:Task 内输出的结构化数据,供下游 Task 通过
$(tasks.<task-name>.results.<result-name>)引用 - Parameters:Pipeline 级别的参数,向下传递到每个 Task
实战步骤
1. 编写基础 Task:代码检出
首先创建一个通用的 Git Clone Task,用于从仓库拉取源码:
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: git-clone
spec:
params:
- name: url
description: Git 仓库地址
- name: revision
description: 分支或 commit SHA
default: main
workspaces:
- name: source
description: 源码输出目录
results:
- name: commit
description: 检出的 commit SHA
- name: commit-message
description: 提交信息
steps:
- name: clone
image: alpine/git
script: |
#!/bin/sh
git clone "$(params.url)" /workspace/source/
cd /workspace/source/
git checkout "$(params.revision)"
echo -n "$(git rev-parse HEAD)" | tee $(results.commit.path)
echo -n "$(git log -1 --pretty=%s)" | tee $(results.commit-message.path)
2. 编写 Task:单元测试
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: unit-test
spec:
params:
- name: test-dir
description: 测试目录
default: ./...
workspaces:
- name: source
description: 源码目录(与 git-clone 共享)
results:
- name: test-status
description: 测试结果状态
steps:
- name: run-tests
image: golang:1.22
script: |
#!/bin/sh
cd /workspace/source
go test $(params.test-dir) -v -race -coverprofile=coverage.out 2>&1 | tee /workspace/test-output.txt
if [ ${PIPESTATUS[0]} -eq 0 ]; then
echo -n "PASS" | tee $(results.test-status.path)
else
echo -n "FAIL" | tee $(results.test-status.path)
exit 1
fi
3. 编写 Task:Docker 镜像构建与推送
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: build-and-push
spec:
params:
- name: image-registry
description: 镜像仓库地址,如 harbor.stellardata.top
- name: image-name
description: 镜像名称
- name: image-tag
description: 镜像标签
default: latest
workspaces:
- name: source
description: 源码目录
results:
- name: image-digest
description: 镜像 digest
- name: image-url
description: 完整镜像地址
steps:
- name: build-and-push
image: gcr.io/kaniko-project/executor:latest
env:
- name: DOCKER_CONFIG
value: /kaniko/.docker
args:
- --context=$(workspaces.source.path)
- --dockerfile=$(workspaces.source.path)/Dockerfile
- --destination=$(params.image-registry)/$(params.image-name):$(params.image-tag)
- --cache=true
- --cache-ttl=24h
script: |
#!/busybox/sh
/kaniko/executor
--context=$(workspaces.source.path)
--dockerfile=$(workspaces.source.path)/Dockerfile
--destination=$(params.image-registry)/$(params.image-name):$(params.image-tag)
--cache=true
--cache-ttl=24h
--digest-file=/tmp/digest
DIGEST=$(cat /tmp/digest)
echo -n "$DIGEST" | tee $(results.image-digest.path)
echo -n "$(params.image-registry)/$(params.image-name):$(params.image-tag)" | tee $(results.image-url.path)
4. 编写 Pipeline:多阶段 CI 流水线
现在将以上三个 Task 编排成一个完整的 Pipeline:
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: ci-pipeline
spec:
params:
- name: repo-url
description: 源码仓库地址
type: string
- name: branch
description: 目标分支
type: string
default: main
- name: image-registry
description: Harbor 镜像仓库地址
type: string
- name: image-name
description: 镜像名称
type: string
workspaces:
- name: shared-source
description: 所有 Task 共享的源码目录
tasks:
- name: checkout
taskRef:
name: git-clone
params:
- name: url
value: $(params.repo-url)
- name: revision
value: $(params.branch)
workspaces:
- name: source
workspace: shared-source
- name: run-tests
taskRef:
name: unit-test
runAfter:
- checkout
params:
- name: test-dir
value: ./...
workspaces:
- name: source
workspace: shared-source
- name: build-image
taskRef:
name: build-and-push
runAfter:
- run-tests
params:
- name: image-registry
value: $(params.image-registry)
- name: image-name
value: $(params.image-name)
- name: image-tag
value: $(tasks.checkout.results.commit)
workspaces:
- name: source
workspace: shared-source
finally:
- name: notify-status
taskRef:
name: send-notification
params:
- name: commit
value: $(tasks.checkout.results.commit)
- name: status
value: "success"
5. PipelineRun:触发执行
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: ci-pipeline-run-001
generateName: ci-pipeline-run-
spec:
pipelineRef:
name: ci-pipeline
params:
- name: repo-url
value: https://github.com/example/my-app.git
- name: branch
value: main
- name: image-registry
value: harbor.stellardata.top
- name: image-name
value: my-app
workspaces:
- name: shared-source
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
6. 条件分支:when 表达式实现选择性执行
有些场景下,我们希望根据条件来决定是否执行某个 Task。例如,仅当代码推送到 main 分支时才构建镜像:
tasks:
- name: build-image
taskRef:
name: build-and-push
when:
- input: $(params.branch)
operator: in
values: ["main", "release-*"]
runAfter:
- run-tests
params:
- name: image-registry
value: $(params.image-registry)
- name: image-name
value: $(params.image-name)
- name: image-tag
value: $(tasks.checkout.results.commit)
workspaces:
- name: source
workspace: shared-source
7. 查看流水线执行状态
使用 tkn CLI 查看流水线执行情况:
# 列出所有 PipelineRun
tkn pipelinerun list
# 查看特定 PipelineRun 的详细信息
tkn pipelinerun describe ci-pipeline-run-001
# 实时查看日志
tkn pipelinerun logs ci-pipeline-run-001 -f
# 查看每个 Task 的详细状态
tkn pipelinerun describe ci-pipeline-run-001 -o yaml | grep -A 5 "taskRuns"
常见问题
Q1: Pipeline 中 Task 的执行顺序如何保证?
A: 使用 runAfter 字段声明依赖关系。Tekton 调度器会解析 Task 之间的依赖 DAG,确保上游 Task 完成后才执行下游 Task。如果没有依赖关系,Task 会并行执行。
Q2: Workspaces 共享时,多个 Task 同时写入会冲突吗?
A: 不会,因为 Tekton 的 runAfter 和 DAG 调度保证了串行访问。但要注意,不同的 Task 之间应使用不同的子目录来避免潜在的文件名冲突。
Q3: 如何从 Pipeline 外部传递参数到 Task?
A: 在 Pipeline 的 params 中定义参数,然后在 Task 的 params 中通过 $(params.<param-name>) 引用。PipelineRun 实例化时提供具体值。
Q4: Pipeline 执行失败后如何自动清理或通知?
A: 使用 finally 字段定义收尾 Task,无论 Pipeline 成功或失败,finally 中的 Task 都会执行。可以用于发送通知、清理资源、记录日志等。
Q5: from 和 runAfter 有什么区别?
A: runAfter 只控制执行顺序,不传递数据。from 既控制顺序也传递数据(通过 Results 引用),通常用于需要上一个 Task 输出结果的场景。
总结
- Pipeline 是 Tekton 的编排核心,通过声明式 YAML 定义多阶段 CI 流水线,Task 之间通过
runAfter和from建立依赖关系 - Workspaces 和 Results 构成数据传递双通道:Workspaces 用于大文件共享,Results 用于轻量级结构化数据传递
- 并行执行与条件分支:Tekton 自动并行化无依赖的 Task,
when表达式实现条件执行,finally确保收尾操作 - 生产实践建议:将通用 Task(git-clone、build、test)定义为 ClusterTask 实现复用,为每个项目编写 Pipeline 进行编排
- 下一步:下一篇文章将介绍 Tekton Triggers,用 Webhook 自动触发流水线,实现真正的”代码提交即构建”















暂无评论内容