工作空间

工作空间是任务或管道在运行时所需的文件系统的声明。工作空间允许任务声明在运行时需要提供的文件系统部分,从而实现任务之间的数据共享、持久存储以及秘密、配置或公共工具的挂载。

为什么需要工作空间

传统 CI/CD 存储挑战

在传统的 CI/CD 系统中,步骤或作业之间共享数据面临几个挑战:

  • 数据共享:在管道的不同阶段之间共享数据的困难
  • 存储配置:持久存储所需的复杂配置
  • 凭证管理:向作业提供凭证的不安全方法
  • 灵活性:存储类型和配置的选项有限
  • 重用性:任务通常对存储位置有硬编码的假设

Tekton 的解决方案

Tekton 工作空间通过以下方式解决这些挑战:

  • 声明式方法:任务声明其存储需求,而不指定实现
  • 运行时绑定:存储细节在运行时提供,而不是在任务中硬编码
  • 灵活实现:工作空间可以由各种卷类型支持
  • 数据共享:工作空间支持管道中任务之间的数据共享
  • 关注点分离:任务作者定义存储需求,用户提供实际存储

优势

  • 灵活性:任务可以在不同的存储实现上运行,而无需修改
  • 重用性:任务可以在不同的环境和存储配置中重复使用
  • 安全性:敏感数据可以通过适当的卷类型安全地提供
  • 持久性:数据可以通过 PersistentVolumeClaims 在任务运行之间保持持久
  • 隔离性:每个 TaskRun 都可以具有自己的独立存储
  • 共享性:多个任务可以通过同一个工作空间共享数据

场景

工作空间在多个场景中非常有用,包括:

  • 源代码访问:为构建和测试任务提供源代码
  • 工件存储:在任务之间存储构建工件
  • 凭证提供:安全地向任务提供凭证
  • 配置共享:在任务之间共享配置文件
  • 缓存存储:维护依赖项或构建工件的缓存
  • 共享工具:提供对公共工具和实用程序的访问

限制和局限

  • 由 PersistentVolumeClaims 支持的工作空间,使用 ReadWriteOnce 访问模式时,不能同时在多个节点上挂载
  • 由 ConfigMaps 或 Secrets 支持的工作空间大小限制为 1MB
  • 由 ConfigMaps 或 Secrets 支持的工作空间始终以只读方式挂载
  • 工作空间不支持在没有 VolumeClaimTemplate 的情况下动态提供卷
  • 工作空间的数据持久性取决于底层卷类型

原则

工作空间声明

在声明工作空间时:

  1. 任务指定工作空间的名称和可选属性
  2. 任务定义工作空间的使用方式(只读或读写)
  3. 任务可以指定工作空间的默认挂载路径
  4. 任务可以将工作空间标记为可选
  5. TaskRuns 或 PipelineRuns 提供实际的存储实现

工作空间绑定

在绑定工作空间时:

  1. TaskRuns 或 PipelineRuns 为每个工作空间指定卷源
  2. 卷源可以是 PersistentVolumeClaims、ConfigMaps、Secrets 或其他类型
  3. TaskRuns 或 PipelineRuns 可以在卷内指定 subPath
  4. PipelineRuns 定义工作空间在任务之间的共享方式

配置示例

任务中的基本工作空间

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: build
spec:
  workspaces:
    - name: source
      description: 包含源代码的工作空间
      readOnly: false
  steps:
    - name: build
      image: golang
      workingDir: $(workspaces.source.path)
      script: |
        go build -o app .

任务中的可选工作空间

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: lint
spec:
  workspaces:
    - name: cache
      description: 用于缓存 linter 数据的工作空间
      optional: true
  steps:
    - name: lint
      image: golangci/golangci-lint
      script: |
        if [ "$(workspaces.cache.bound)" == "true" ]; then
          export GOLANGCI_LINT_CACHE=$(workspaces.cache.path)
        fi
        golangci-lint run ./...

重要参数

ReadOnly 标志

readOnly 标志指示任务是否会修改工作空间的内容。

使用场景

  • 防止意外修改源代码
  • 共享不应被修改的配置数据
  • 允许多个任务安全地同时访问相同的工作空间
  • 提供不应被更改的凭证

原则

只读工作空间:

  • 在任务的容器中作为只读卷挂载
  • 防止任务写入工作空间
  • 允许多个任务更安全地并发访问
  • 可以由 ConfigMaps 和 Secrets 等只读卷源支持

配置示例

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: test
spec:
  workspaces:
    - name: source
      description: 包含源代码的工作空间
      readOnly: true
  steps:
    - name: test
      image: golang
      workingDir: $(workspaces.source.path)
      script: |
        go test ./...

Optional 标志

optional 标志指示任务执行时工作空间不是必需的。

使用场景

  • 提供可选缓存以提高性能
  • 支持可选凭证注入
  • 使任务在不同环境中更加灵活
  • 允许任务适应特定数据的存在或缺失

原则

可选工作空间:

  • 在 TaskRun 中不需要提供
  • 任务必须处理工作空间未提供的情况
  • 任务可以使用 $(workspaces.name.bound) 变量检查工作空间是否被绑定
  • 使任务更加灵活和可重用

配置示例

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: build-with-cache
spec:
  workspaces:
    - name: source
      description: 包含源代码的工作空间
    - name: cache
      description: 构建工件的缓存
      optional: true
  steps:
    - name: build
      image: gradle
      workingDir: $(workspaces.source.path)
      script: |
        if [ "$(workspaces.cache.bound)" == "true" ]; then
          gradle build --build-cache --gradle-user-home=$(workspaces.cache.path)
        else
          gradle build
        fi

隔离工作空间

隔离工作空间允许限制对敏感数据的访问,仅限于特定的步骤。

使用场景

  • 限制对凭证的访问,仅限于需要的步骤
  • 保护敏感配置免受不必要的暴露
  • 在任务中实现最小特权原则
  • 降低凭证泄露的风险

原则

隔离工作空间:

  • 仅在明确请求的步骤中挂载
  • 需要将 enable-api-fields 特性标志设置为 "beta"
  • 允许更安全地处理敏感数据
  • 在不同步骤中可以有不同的挂载路径

配置示例

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: clone-and-build
spec:
  workspaces:
    - name: source
      description: 源代码的工作空间
    - name: ssh-credentials
      description: 用于 git 操作的 SSH 凭证
  steps:
    - name: clone
      workspaces:
        - name: ssh-credentials
      image: alpine/git
      script: |
        mkdir -p ~/.ssh
        cp $(workspaces.ssh-credentials.path)/* ~/.ssh/
        chmod 600 ~/.ssh/id_rsa
        git clone git@github.com:org/repo.git $(workspaces.source.path)
    - name: build
      image: golang
      workingDir: $(workspaces.source.path)
      script: |
        go build -o app .

卷源

PersistentVolumeClaim

PersistentVolumeClaims 提供可持久化的存储,可以超出 TaskRun 或 PipelineRun 的生命周期。

使用场景

  • 在管道中共享任务之间的数据
  • 在管道运行之间持久化构建工件
  • 维护依赖项的缓存
  • 存储稍后分析的测试结果

原则

PersistentVolumeClaims:

  • 提供通过 Kubernetes PersistentVolumes 支持的持久存储
  • 可以是预先存在的或从 VolumeClaimTemplate 创建的
  • 支持不同的访问模式(ReadWriteOnce、ReadWriteMany、ReadOnlyMany)
  • 可以在管道中的多个任务之间共享

配置示例

apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  name: build-with-pvc
spec:
  taskRef:
    name: build
  workspaces:
    - name: source
      persistentVolumeClaim:
        claimName: my-source-pvc

EmptyDir

EmptyDir 提供的临时存储仅存在于 TaskRun 的生命周期内。

使用场景

  • 单个 TaskRun 的临时存储
  • 在任务的步骤之间共享数据
  • 不需要持久化的情况
  • 测试和调试任务

原则

EmptyDir:

  • 创建一个仅在 TaskRun 的持续时间内存在的临时目录
  • 在 TaskRun 完成时自动清理
  • 不能在不同的 TaskRuns 之间共享
  • 适用于不需要持久化数据的任务

配置示例

apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  name: build-with-emptydir
spec:
  taskRef:
    name: build
  workspaces:
    - name: source
      emptyDir: {}

ConfigMap 和 Secret

ConfigMaps 和 Secrets 提供了一种将配置数据和敏感信息注入到任务中的方式。

使用场景

  • 向任务提供配置文件
  • 注入用于身份验证的凭证
  • 提供特定于环境的设置
  • 共享少量只读数据

原则

ConfigMaps 和 Secrets:

  • 始终作为只读卷挂载
  • 大小限制为 1MB
  • 必须在创建 TaskRun 之前存在
  • 适合用于配置数据和凭证

配置示例

apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  name: deploy-with-config
spec:
  taskRef:
    name: deploy
  workspaces:
    - name: config
      configMap:
        name: deployment-config
    - name: credentials
      secret:
        secretName: deployment-credentials

参考文献