使用 Workspaces 加速 Tekton 流水线
在实际工作中,我们经常需要的一个功能是能够在任务之间共享制品,以便缓存构建工具(比如 Maven 和 NPM)的依赖项,在 Tekton 0.10 版本就发布增加了对 Workspaces 的支持,这使得流水线中的任务可以更加轻松地使用 PV 来共享数据了,Workspaces 允许指定一个或多个 pipeline 中 task 运行时需要的 volume。
Tekton Pipelines 中的 Workspaces 是指流水线运行时需要的共享卷的声明,在流水线定义中,Workspaces 可以作为共享卷传递给相关任务,这样当为多个任务提供相同的 Workspaces 的时候,它们就可以从相同的 Volumes 中读取和写入数据。当然 Workspaces 的 Volumes 卷除了可以是 PVC,也可以是 ConfigMap,或者是在任务之间挂载和共享的 Secret 资源。
接下来让我们看看在实践中如何使用 Workspaces 来缓存 Maven 依赖,加速流水线的构建,这里我们使用的项目为:https://github.com/cnych/spring-petclinic。
要在流水线中构建 Maven 项目,当然需要定义一个 Maven 的 Task 任务,其实在 Tekton Catalog 里面就已经包含了这样的通用的 Task 了,但是这里我们需要对其进行一些修改来为 Maven 的依赖项添加 Workspaces 支持。
# workspace-mvn-task.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: mvn-task
spec:
workspaces:
- name: maven-repo
resources:
inputs:
- name: source
type: git
params:
- name: GOALS
description: The Maven goals to run
type: array
default: ["package"]
steps:
- name: mvn
image: cnych/cloud-builders-mvn:tekton
workingDir: /workspace/source
command: ["/usr/bin/mvn"]
args:
- -Dmaven.repo.local=$(workspaces.maven-repo.path)
- "$(inputs.params.GOALS)"
上面的任务中我们新增了一个名为 maven-repo
的 Workspace,该工作区规定无论何时运行该任务,都应该提供并配置一个卷来充当本地的 Maven 存储库,然后将工作区的路径传递给 Maven 命令,以便通过 -Dmaven.repo.local=$(workspaces.maven-repo.path)
命令将工作区的路径作为本地的 Maven 库,当然也可以配置 Workspace 挂载的路径,这里我们使用的是默认的路径。
接着我们来定义一个使用 Maven 任务构建 Java 应用程序的流水线 Pipeline,为了演示 Maven 依赖的缓存效果,这里的流水线我们运行3个 Maven 任务来执行构建、集成测试,并生成测试结果和代码覆盖率等报告。
流水线定义如下所示:
# workspace-mvn-pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: mvn-pipeline
spec:
workspaces: # 声明 workspaces
- name: local-maven-repo
resources: # 声明使用的资源
- name: app-git
type: git
tasks:
- name: build # 构建任务
taskRef:
name: mvn-task # 引用上面的 mvn 任务
resources: # 传递 resources 资源
inputs:
- name: source
resource: app-git
params: # 传递 params 参数
- name: GOALS
value: ["package"]
workspaces: # 传递 workspaces
- name: maven-repo
workspace: local-maven-repo
- name: int-test # 测试任务
taskRef:
name: mvn-task
runAfter: ["build"] # 需要 build 任务执行完成后
resources:
inputs:
- name: source
resource: app-git
params:
- name: GOALS
value: ["verify"]
workspaces:
- name: maven-repo
workspace: local-maven-repo
- name: gen-report # 测试报告
taskRef:
name: mvn-task
runAfter: ["build"] # 需要 build 任务执行完成后
resources:
inputs:
- name: source
resource: app-git
params:
- name: GOALS
value: ["site"]
workspaces:
- name: maven-repo
workspace: local-maven-repo
需要注意流水线中的 local-maven-repo
工作区的声明,它指出,当此流水线运行时,应提供一个卷并将其用作此工作区,然后将此工作区提供给此流水线中的每个任务,以便它们都共享相同的工作区。然后我们根据传入的 GOALS
参数来决定应该执行的任务。
流水线 Pipeline 声明完成后,现在我们就可以运行这个流水线来构建 Spring PetClinic
这个示例应用了,在启动流水线之前,需要先创建一个 PVC 来提供一个 Workspace 对 Maven 依赖项进行缓存。
# workspace-mvn-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mvn-repo-pvc
spec:
resources:
requests:
storage: 5Gi
volumeMode: Filesystem
storageClassName: nfs-storage # 使用 StorageClass 自动生成 PV
accessModes:
- ReadWriteOnce
这里我们使用了一个名为 nfs-storage
的 StorageClass,这样就可以自动生成一个对应的 PV 进行绑定,如果你没有需要自行创建一个对应的静态 PV。
现在我们就可以创建一个使用上述 PVC 作为流水线工作区的 PipelineRun 来执行流水线了:
# workspace-mvn-pipelinerun.yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: mvn-pipelinerun
spec:
pipelineRef:
name: mvn-pipeline
resources:
- name: app-git
resourceSpec:
type: git
params:
- name: url
value: https://github.com.cnpmjs.org/cnych/spring-petclinic
workspaces:
- name: local-maven-repo
persistentVolumeClaim:
claimName: mvn-repo-pvc
请注意 PVC 和为缓存 maven 依赖项而声明的工作区之间的映射,mvn-repo-pvc
被传递到流水线和相应的任务作为缓存文件和制品的共享卷。
第一次流水线运行将需要一些时间来下载依赖项执行任务,直接创建上面声明的几个资源对象,观察 PipelineRun 的执行过程:
kubectl apply -f workspace-mvn-task.yaml
kubectl apply -f workspace-mvn-pipeline.yaml
kubectl apply -f workspace-mvn-pvc.yaml
kubectl apply -f workspace-mvn-pipelinerun.yaml
当第一次执行流水线的时候会在执行 mvn
命令的时候消耗大量的时间,因为需要下载依赖包,我这里的环境差不多等了20分钟左右:
然后在执行后面的两个任务的时候就非常快了,因为前面任务执行完成后会把依赖项存入到 Workspace 声明的 PVC 中去,后面的任务直接使用了这个 Workspace,我们可以重新执行一次 PipelineRun,对比下前后两次的时间,在我的环境中,执行时间由37分钟减少到大约两分钟。
$ tkn pr list
NAME STARTED DURATION STATUS
mvn-pipelinerun-r-fgwf2 3 minutes ago 2 minutes Succeeded
mvn-pipelinerun 2 hours ago 37 minutes Succeeded
# 查看第一次pipelinerun的执行情况
$ tkn pr describe mvn-pipelinerun
Name: mvn-pipelinerun
Namespace: default
Pipeline Ref: mvn-pipeline
Service Account: default
Timeout: 1h0m0s
Labels:
tekton.dev/pipeline=mvn-pipeline
🌡️ Status
STARTED DURATION STATUS
2 hours ago 37 minutes Succeeded
......
🗂 Taskruns
NAME TASK NAME STARTED DURATION STATUS
∙ mvn-pipelinerun-int-test-mbppx int-test 2 hours ago 33 seconds Succeeded
∙ mvn-pipelinerun-gen-report-xlns9 gen-report 2 hours ago 36 minutes Succeeded
∙ mvn-pipelinerun-build-x9zkf build 2 hours ago 20 minutes Succeeded
# 查看第二次执行的详细信息
$ tkn pr describe mvn-pipelinerun-r-fgwf2
Name: mvn-pipelinerun-r-fgwf2
Namespace: default
Pipeline Ref: mvn-pipeline
Service Account: default
Timeout: 1h0m0s
Labels:
reruns=mvn-pipelinerun
tekton.dev/pipeline=mvn-pipeline
🌡️ Status
STARTED DURATION STATUS
4 minutes ago 2 minutes Succeeded
......
🗂 Taskruns
NAME TASK NAME STARTED DURATION STATUS
∙ mvn-pipelinerun-r-fgwf2-gen-report-n69g8 gen-report 4 minutes ago 1 minute Succeeded
∙ mvn-pipelinerun-r-fgwf2-int-test-4g58z int-test 4 minutes ago 32 seconds Succeeded
∙ mvn-pipelinerun-r-fgwf2-build-ftqgr build 4 minutes ago 38 seconds Succeeded
测试任务运行没有受到太大影响,因为它使用了大部分在构建任务运行中下载的依赖项,即使在第一次流水线运行中也是如此。
我们可以看到利用 Workspaces 功能可以对我们的流水线构建进行大幅度的优化,特别是对于依赖包特别大的应用,比如 Maven、NPM、Go Modules 等。