生产环境 DevOps 实战 | 第 11 天:Tekton 参数、Results 与 Workspaces——流水线数据流核心机制

第 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 stringarray,默认 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 的 emptyDirPersistentVolumeClaim 有什么区别?

  • 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 支持哪些类型?数组参数怎么用?

支持 stringarray 两种类型。数组参数在 Step 中通过 $(params.xxx[*]) 展开为多个参数,或通过 $(params.xxx[0]) 访问单个元素。在 script 中,数组变量需要通过 ${@}${*} 方式展开。

总结

  1. Parameters、Results、Workspaces 三足鼎立:参数负责输入,结果负责输出,工作空间负责共享存储——三者共同构成了 Tekton 流水线的数据流骨架
  2. Results 是轻量级数据通道:适合传递字符串片段(镜像标签、版本号、状态值),有 4KB 大小限制,通过文件写入 /tekton/results/ 目录实现
  3. Workspaces 是文件级共享机制:支持多种存储后端(emptyDir、PVC、ConfigMap、Secret),生产环境推荐使用 volumeClaimTemplate 自动管理 PVC 生命周期
  4. 引用链是 Pipeline 编排的关键$(tasks.<name>.results.<name>) 语法让上游 Task 的数据流向下游,配合 runAfter 控制执行顺序
  5. 生产级最佳实践:用 Workspaces 传递源代码和构建产物,用 Results 传递元数据(commit SHA、镜像标签),用 Parameters 实现流水线模板化——三者组合可实现灵活、可复用的 CI 流水线
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容