第 11/60 天
引言
在 Tekton 中编写流水线时,最核心的问题之一就是数据如何在 Task 之间流转。Token 构建阶段生成的镜像地址、测试报告中的测试结果、构建产物——这些数据需要在 Pipeline 的多个 Task 之间传递,而 Tekton 提供了三种核心机制来实现这一目标:Parameters(参数)、Results(结果) 和 Workspaces(工作空间)。
很多新手在编写 Tekton 流水线时,常常混淆这三个概念:参数是输入、结果是输出、工作空间是共享存储。但实际应用中,它们之间的配合远比表面上复杂——参数可以引用之前 Task 的结果,工作空间可以跨 Task 共享文件,而 Results 有大小限制(KB 级别)且只支持字符串。理解这些机制的区别和适用场景,是编写生产级 Tekton 流水线的基础。
本文将深入解析 Parameters、Results 和 Workspaces 的原理、用法和最佳实践,并通过多个可运行的 YAML 示例展示如何在真实场景中组合使用它们。
核心概念
Parameters(参数)
Parameters 是 Tekton Task/Pipeline 的输入参数,类似于函数参数。支持字符串、数组等类型,可以在 Task 的 Step 中通过 $(params.xxx) 方式引用。
| 属性 | 说明 |
|---|---|
type |
string 或 array,默认 string |
default |
可选默认值,未指定时使用 |
description |
参数描述 |
Results(结果)
Results 是 Task 的输出结果,用于将关键信息(如镜像地址、构建版本号)传递给下游 Task。Results 以文件形式存储在 /tekton/results/ 目录下,最大支持约 4KB 的数据。
下游 Task 通过 $(tasks.<task-name>.results.<result-name>) 语法引用上游 Task 的 Results。
Workspaces(工作空间)
Workspaces 是 Tekton 的共享存储机制,用于在 Task 的 Step 之间、以及 Pipeline 中不同 Task 之间共享文件。它支持多种存储后端:
| 后端类型 | 说明 | 适用场景 |
|---|---|---|
emptyDir |
临时卷,Pod 销毁后丢失 | 临时文件共享 |
PersistentVolumeClaim |
持久化存储 | 需要持久化的构建产物 |
ConfigMap |
配置数据注入 | 注入配置文件 |
Secret |
敏感数据注入 | 注入凭证 |
hostPath |
宿主机路径 | 本地开发调试 |
三者的关系
Parameters (输入) → Task → Results (输出)
↓
Workspaces (共享存储)
- Parameters:上游 Task 的结果可以作为下游 Task 的参数
- Results:Task 输出的轻量级数据,适合传递字符串值
- Workspaces:Task 之间共享的存储空间,适合传递文件和大型数据
实战步骤
1. 编写带参数的 Task
首先,创建一个支持参数的 Task,接收镜像名称和标签,构建一个完整的镜像引用。
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: build-image
spec:
params:
- name: image-name
type: string
description: 镜像名称
- name: image-tag
type: string
description: 镜像标签
default: latest
- name: build-args
type: array
description: 构建参数列表
default:
- "--no-cache"
- "--compress"
steps:
- name: build
image: gcr.io/kaniko-project/executor:latest
args:
- --destination=$(params.image-name):$(params.image-tag)
- --context=$(params.build-args[*])
script: |
#!/busybox/sh
echo "Building image: $(params.image-name):$(params.image-tag)"
echo "Build args: $(params.build-args[*])"
在这个 Task 中,我们定义了三种参数类型:
– image-name:必选字符串参数
– image-tag:可选字符串参数,默认值为 latest
– build-args:数组参数,用于传递多个构建参数
2. 编写带 Results 的 Task
Results 用于从 Task 输出关键信息。以下是一个代码检查 Task,输出检查结果和代码行数统计。
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: code-check
spec:
params:
- name: source-path
type: string
description: 源码路径
results:
- name: total-lines
description: 代码总行数
- name: check-status
description: 代码检查状态 (PASS/FAIL)
- name: report-url
description: 检查报告 URL
steps:
- name: count-lines
image: alpine:latest
script: |
#!/bin/sh
cd $(workspaces.source.path)/$(params.source-path)
LINES=$(find . -name "*.py" -o -name "*.go" -o -name "*.java" | xargs wc -l 2>/dev/null | tail -1 | awk '{print $1}')
echo -n "$LINES" | tee /tekton/results/total-lines
- name: run-lint
image: alpine:latest
script: |
#!/bin/sh
# 模拟代码检查
echo "Running code lint..."
echo -n "PASS" | tee /tekton/results/check-status
echo -n "https://reports.example.com/check/$(params.source-path)" | tee /tekton/results/report-url
关键点:
– Results 通过写入 /tekton/results/<name> 文件来输出
– 值必须是字符串,不能包含换行符(使用 echo -n 避免多余换行)
– 下游 Task 通过 $(tasks.code-check.results.total-lines) 引用
3. 编写带 Workspaces 的 Task
Workspaces 让多个 Task 共享文件系统空间。以下是一个完整的 CI 流水线,包含代码检出、构建和测试三个阶段。
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: clone-and-build
spec:
params:
- name: repo-url
type: string
- name: branch
type: string
default: main
results:
- name: commit-sha
description: Git commit SHA
- name: build-status
description: 构建状态
workspaces:
- name: source
description: 源码工作空间
steps:
- name: clone
image: alpine/git:latest
script: |
#!/bin/sh
cd $(workspaces.source.path)
git clone --branch $(params.branch) --depth 1 $(params.repo-url) .
SHA=$(git rev-parse HEAD)
echo -n "$SHA" | tee /tekton/results/commit-sha
echo "Cloned commit: $SHA"
- name: build
image: golang:1.21-alpine
script: |
#!/bin/sh
cd $(workspaces.source.path)
go build -o /workspace/output/app ./cmd/
echo "Build completed"
echo -n "SUCCESS" | tee /tekton/results/build-status
4. 组合成 Pipeline:参数传递 + Results 引用 + Workspace 共享
现在,让我们将上述 Task 组合成一个完整的 Pipeline,展示三种机制如何协同工作。
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: ci-pipeline
spec:
params:
- name: repo-url
type: string
description: Git 仓库地址
- name: branch
type: string
description: 分支名称
default: main
- name: image-registry
type: string
description: 镜像仓库地址
default: harbor.stellardata.top
- name: image-name
type: string
description: 镜像名称
workspaces:
- name: shared-workspace
description: 跨 Task 共享的源码工作空间
tasks:
- name: code-check
taskRef:
name: code-check
params:
- name: source-path
value: "."
workspaces:
- name: source
workspace: shared-workspace
- name: clone-and-build
taskRef:
name: clone-and-build
params:
- name: repo-url
value: $(params.repo-url)
- name: branch
value: $(params.branch)
workspaces:
- name: source
workspace: shared-workspace
- name: build-image
taskRef:
name: build-image
params:
- name: image-name
value: $(params.image-registry)/$(params.image-name)
- name: image-tag
# 引用上游 Task 的 Results —— 使用 commit SHA 作为镜像标签
value: $(tasks.clone-and-build.results.commit-sha)
runAfter:
- clone-and-build
workspaces:
- name: source
workspace: shared-workspace
- name: notify
taskRef:
name: notify
params:
- name: message
value: |
Build: $(tasks.clone-and-build.results.build-status)
Commit: $(tasks.clone-and-build.results.commit-sha)
Image: $(params.image-registry)/$(params.image-name):$(tasks.clone-and-build.results.commit-sha)
Code Check: $(tasks.code-check.results.check-status)
runAfter:
- code-check
- build-image
Pipeline 数据流图解:
Pipeline Parameters
├── repo-url ──────────────────────────┐
├── branch ────────────────────────────┤
├── image-registry ────────────────────┤
└── image-name ────────────────────────┤
│
Workspace: shared-workspace ──┐ │
▼ ▼
Task: clone-and-build ──────→ 代码文件写入 workspace
│ │
├── Results: commit-sha ──────┤
└── Results: build-status ────┤
│
▼
Task: code-check ────────────→ 读取 workspace 中的源代码
│ │
└── Results: check-status ────┤
│
▼
Task: build-image ←── 引用 commit-sha 作为镜像标签
│
▼
Task: notify ←── 引用多个 Results 生成通知
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/backend"
workspaces:
- name: shared-workspace
persistentvolumeclaim:
claimName: build-workspace-pvc
6. 使用 Python 脚本验证 Results 数据流
以下 Python 脚本可以用于验证 PipelineRun 执行后,Results 是否正确传递。
#!/usr/bin/env python3
"""验证 Tekton PipelineRun 的 Results 数据流"""
import json
import subprocess
import sys
def get_pipelinerun_results(name: str, namespace: str = "default") -> dict:
"""获取 PipelineRun 的所有 TaskRun 及其 Results"""
cmd = [
"kubectl", "get", "pipelinerun", name,
"-n", namespace,
"-o", "json"
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"Error: {result.stderr}")
sys.exit(1)
pr = json.loads(result.stdout)
# PipelineRun 的 Results 在 status.pipelineResults 中
pipeline_results = pr.get("status", {}).get("pipelineResults", [])
print("=" * 60)
print(f"PipelineRun: {name}")
print("=" * 60)
for r in pipeline_results:
print(f" {r['name']}: {r['value']}")
# 同时获取每个 TaskRun 的 Results
task_runs = pr.get("status", {}).get("taskRuns", {})
for task_name, tr in task_runs.items():
task_results = tr.get("status", {}).get("taskResults", [])
if task_results:
print(f"nTask: {task_name}")
for r in task_results:
print(f" {r['name']}: {r['value']}")
return pipeline_results
def check_result_chain(pr_name: str) -> bool:
"""验证 Results 引用链是否完整"""
results = get_pipelinerun_results(pr_name)
expected_keys = {
"commit-sha", "build-status", "check-status", "report-url"
}
actual_keys = {r["name"] for r in results}
missing = expected_keys - actual_keys
if missing:
print(f"n❌ 缺少以下 Results: {missing}")
return False
print("n✅ 所有 Results 数据流完整")
print(f" 共 {len(results)} 个 Results")
return True
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python3 verify_results.py <pipelinerun-name>")
sys.exit(1)
pr_name = sys.argv[1]
namespace = sys.argv[2] if len(sys.argv) > 2 else "default"
check_result_chain(pr_name)
7. Workspaces 高级用法:PVC 模板与存储策略
在生产环境中,推荐使用 PersistentVolumeClaim 模板,让 Tekton 自动管理 PVC 的生命周期。
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: ci-pipeline-run-with-pvc-template
spec:
pipelineRef:
name: ci-pipeline
params:
- name: repo-url
value: "https://github.com/example/my-app.git"
- name: image-registry
value: "harbor.stellardata.top"
- name: image-name
value: "my-app/backend"
workspaces:
- name: shared-workspace
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard
常见问题
Q1: Results 和 Workspaces 有什么区别?什么时候该用哪个?
Results 用于传递轻量级字符串数据(< 4KB),如镜像标签、版本号、状态值。Workspaces 用于共享文件系统数据,如源代码、构建产物、配置文件。规则:能写成字符串的用 Results,需要文件系统的用 Workspaces。
Q2: 为什么不推荐在 Results 中放大型数据?
Tekton 的 Results 存储在 /tekton/results/ 目录下,每个 Result 文件的大小限制约为 4KB。如果试图存储超过限制的数据,Tekton 会截断或报错。对于大型数据(如构建日志、测试报告),应使用 Workspaces 存储文件,再通过外部存储(如对象存储)的 URL 作为 Result 传递。
Q3: Workspaces 的 emptyDir 和 PersistentVolumeClaim 有什么区别?
emptyDir:Pod 创建时创建,Pod 销毁时删除。适合临时数据,不需要持久化。PersistentVolumeClaim:使用持久化存储,Pod 销毁后数据保留。适合需要跨 Task 共享但不需要持久化的场景(使用volumeClaimTemplate自动清理),或需要保留构建产物的场景。
Q4: 多个 Task 同时写入同一个 Workspace 会冲突吗?
如果多个 Task 并行执行且写入同一个 Workspace,取决于存储后端:
– ReadWriteOnce(RWO)PVC:同一时间只能被一个 Pod 挂载,并行 Task 会串行执行
– ReadWriteMany(RWX)PVC:允许多个 Pod 同时挂载,但需要 Task 自己处理文件锁
– 最佳实践:避免并行 Task 写入同一 Workspace,使用 runAfter 控制顺序
Q5: 如何在 Pipeline 中引用上游 Task 的 Results?
使用 $(tasks.<task-name>.results.<result-name>) 语法。注意:引用的 Task 必须在当前 Task 之前执行,可以通过 runAfter 字段确保执行顺序。
params:
- name: image-tag
value: $(tasks.build.results.image-tag)
runAfter:
- build
Q6: Parameters 支持哪些类型?数组参数怎么用?
支持 string 和 array 两种类型。数组参数在 Step 中通过 $(params.xxx[*]) 展开为多个参数,或通过 $(params.xxx[0]) 访问单个元素。在 script 中,数组变量需要通过 ${@} 或 ${*} 方式展开。
总结
- Parameters、Results、Workspaces 三足鼎立:参数负责输入,结果负责输出,工作空间负责共享存储——三者共同构成了 Tekton 流水线的数据流骨架
- Results 是轻量级数据通道:适合传递字符串片段(镜像标签、版本号、状态值),有 4KB 大小限制,通过文件写入
/tekton/results/目录实现 - Workspaces 是文件级共享机制:支持多种存储后端(emptyDir、PVC、ConfigMap、Secret),生产环境推荐使用
volumeClaimTemplate自动管理 PVC 生命周期 - 引用链是 Pipeline 编排的关键:
$(tasks.<name>.results.<name>)语法让上游 Task 的数据流向下游,配合runAfter控制执行顺序 - 生产级最佳实践:用 Workspaces 传递源代码和构建产物,用 Results 传递元数据(commit SHA、镜像标签),用 Parameters 实现流水线模板化——三者组合可实现灵活、可复用的 CI 流水线















暂无评论内容