部署


部署crd和pipline服务:

https://tekton.dev/docs/getting-started/tasks/
kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml

查看:

~]# kubectl api-resources --api-group=tekton.dev
NAME                SHORTNAMES   APIVERSION            NAMESPACED   KIND
clustertasks                     tekton.dev/v1beta1    false        ClusterTask
pipelineresources                tekton.dev/v1alpha1   true         PipelineResource(已废弃)
pipelineruns        pr,prs       tekton.dev/v1beta1    true         PipelineRun
pipelines                        tekton.dev/v1beta1    true         Pipeline
runs                             tekton.dev/v1alpha1   true         Run
taskruns            tr,trs       tekton.dev/v1beta1    true         TaskRun
tasks                            tekton.dev/v1beta1    true         Task

部署dashboard:

https://tekton.dev/docs/dashboard/install/
kubectl apply -f https://storage.googleapis.com/tekton-releases/dashboard/latest/tekton-dashboard-release.yaml

部署问题:

{"severity":"fatal","timestamp":"2022-09-22T03:55:15.673Z","logger":"tekton-pipelines-controller","caller":"sharedmain/main.go:326","message":"Version check failed","commit":"9221ac3-dirty","error":"kubernetes version \"1.18.20\" is not compatible, need at least \"1.22.0-0\" (this can be overridden with the env var \"KUBERNETES_MIN_VERSION\")","stacktrace":"knative.dev/pkg/injection/sharedmain.CheckK8sClientMinimumVersionOrDie\n\tknative.dev/pkg@v0.0.0-20220818004048-4a03844c0b15/injection/sharedmain/main.go:326\nknative.dev/pkg/injection/sharedmain.MainWithConfig\n\tknative.dev/pkg@v0.0.0-20220818004048-4a03844c0b15/injection/sharedmain/main.go:228\nmain.main\n\tgithub.com/tektoncd/pipeline/cmd/controller/main.go:106\nruntime.main\n\truntime/proc.go:250"}

解决方法:

]# kubectl edit deployments.apps -n tekton-pipelines tekton-pipelines-controller
]# kubectl edit deployments.apps -n tekton-pipelines tekton-pipelines-webhook
...
        env:
        - name: KUBERNETES_MIN_VERSION
          value: v1.17.0+k3s.1
...

暴露dashboard:这里使用ingressgateway的方式进行暴露服务,需要安装istio。用ingress也行,istio并非必要。

---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: tekton-dashboard
  namespace: tekton-pipelines
spec:
  host: tekton-dashboard
  trafficPolicy:
    tls:
      mode: DISABLE
---
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: tekton-dashboard-gateway
  namespace: istio-system
spec:
  selector:
    app: istio-ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "tekton.ops.net"
    - "ci.ops.net"
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: tekton-dashboard-virtualservice
  namespace: tekton-pipelines
spec:
  hosts:
  - "tekton.ops.net"
  - "ci.ops.net"
  gateways:
  - istio-system/tekton-dashboard-gateway
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: tekton-dashboard
        port:
          number: 9097
---

自建集群需要在istio-ingressgateway的svc上配置externalIPs:然后将域名解析到这个IP。

spec:
  externalIPs:
  - 192.168.0.88

部署的服务:

 ~]# kubectl get pods -n tekton-pipelines 
NAME                                           READY   STATUS    RESTARTS   AGE
tekton-dashboard-6c97f9bdd5-kj4l8              1/1     Running   0          20d
tekton-pipelines-controller-5556479b76-252hn   1/1     Running   0          20d
tekton-pipelines-webhook-785478f957-7jt7t      1/1     Running   0          20d

部署CLI:

https://github.com/tektoncd/cli/releases
rpm -i https://github.com/tektoncd/cli/releases/download/v0.25.0/tektoncd-cli-0.25.0_Linux-64bit.rpm

常用命令:

tkn task list
tkn taskrun list
tkn pipeline list
tkn pipelinerun list
tkn task delete --all
tkn taskrun delete --all


Task,TaskRun

task示例:简单输出

apiVersion: tekton.dev/v1beta1
kind: task
metadata:
  name: world
spec:
  steps:
  - name: say-hello
    image: alpine:3.15
    command: ['/bin/sh']
    args: ['-c', 'echo hello'] # 输出hello
  - name: say-world
    image: alpine:3.15
    command: ['/bin/sh']
    args: ['-c', 'echo world'] # 输出world,多step情况下第一个结束才运行第二个,按顺序执行
---
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
  name: world-run-00001
spec:
  taskRef:
    kind: Task
    name: world
---
]# tkn taskrun logs world-run-00001 
[say-hello] hello

[say-world] world

task引用参数:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: hello-params
spec:
  params:
  - name: target #参数名称
    type: string # 参数类型
    description: name of somebody # 参数功能描述
    default: scriptjc.com # 参数默认值
  steps:
  - name: say-hello
    image: alpine:3.15
    command:
    - /bin/sh
    args: ['-c','echo hello $(params.target)'] #  $(params.target) 引用变量的方式

命令启动task任务:

]# tkn task start hello-params --showlog
? Value for param `target` of type `string`? (Default is `scriptjc.com`) scriptjc.com # 可以手动输入参数,不输入则使用默认值
TaskRun started: hello-params-run-9lk2n
Waiting for logs to be available...
[say-hello] hello scriptjc.com

命令行直接传参:-p选项可以传参,避免进入交互式界面

]# tkn task start hello-params --showlog -p target=www.scriptjc.com
TaskRun started: hello-params-run-q7p6d
Waiting for logs to be available...
[say-hello] hello www.scriptjc.com

task运行脚本:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: script
spec:
  steps:
  - name: step-with-script
    image: alpine:3.15
    script: |
      #!/bin/sh
      echo "step with script"
      apk add curl
      curl -s www.scriptjc.com && echo "success" || echo "fail"
      echo "All done"

task在脚本中引用参数:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: logger
spec:
  params:
  - name: text
    type: string
    description: something to log
    default: "-"
  steps:
  - name: log
    image: alpine:3.15
    script: |
      #!/bin/sh
      apk add -q tzdata
      cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 获取本地时间
      DATETIME=$(date "+%F %T")
      echo [$DATETIME] - $(params.text)


pipeline

定义task运行顺序:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: pipeline-demo
spec:
  tasks:
  - name: first-task
    taskRef:
      name: world
  - name: second-task
    taskRef:
      name: logger
    runAfter:
    - first-task # 定义了second-task 必须在first-task 运行完成之后再运行

查看详细信息:

]# tkn pipeline describe pipeline-demo 
Name:        pipeline-demo
Namespace:   default

  Tasks

 NAME            TASKREF   RUNAFTER     TIMEOUT   PARAMS
 ∙ first-task    world                  ---       ---
 ∙ second-task   logger    first-task   ---       ---

  PipelineRuns

 NAME                        STARTED         DURATION   STATUS
 ∙ pipeline-demo-run-j8wlq   2 minutes ago   16s        Succeeded

pipeline给task传参:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: pipeline-with-params
spec:
  params: # 定义参数,给task传值用的
  - name: height
    type: string
  tasks:
  - name: task-one
    taskRef:
      name: hello-params
  - name: task-two
    taskRef:
      name: logger
    params: # 给logger传参
    - name: text
      value: $(params.height) # 引用了上面定义的名为height的参数

启动pipelineRun:

]# tkn pipeline start pipeline-with-params -p height=1111 --showlog
PipelineRun started: pipeline-with-params-run-rln26
Waiting for logs to be available...
[task-one : say-hello] hello scriptjc.com

[task-two : log] [2022-08-16 14:36:40] - 1111


workspaces


Results:只能传递小于4096的数据

Workspace:工作空间、文件系统

    $(workspace.<name>.path)       挂载路径

    $(workspace.<name>.bound)    是否绑定

    $(workspace.<name>.claim)      pvc名称

    $(workspace.<name>.volume)  卷名称

引用方法:

/workspaces/<name>  默认挂载路径,自定义情况下访问不了

$(workspaces.<name>.path)  默认挂载路径和自定义挂载路径都可以访问到

task中使用workspace:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: workspace-demo
spec:
  params:
  - name: target
    type: string
    default: scriptjc.com
  steps:
  - name: write-message
    image: alpine:3.15
    script: |
      #!/bin/sh
      set -xe
      if [ "$(workspaces.message.bound)" == "true" ]; then # 如果已近绑定了PVC
          echo "hello $(params.target)" > $(workspaces.message.path)/message # 将信息写入到message文件中
      fi
      echo "Mount Path: $(workspaces.message.path)"
      echo "Volume Name: $(workspaces.message.volume)"
      echo "Claim Name: $(workspaces.message.claim)"
  workspaces:
  - name: message
    description: |
      如果写入成功 message 文件里面应该有信息
    optional: true
    mountPath: /data

-w name=message,emptyDir="" 意思是名为message的workspace提供一个emptyDir的存储卷。

]# tkn task start workspace-demo --showlog -p target="scriptjc.com" -w name=message,emptyDir=""
TaskRun started: workspace-demo-run-hl6cw
Waiting for logs to be available...
[write-message] + '[' true '==' true ]
[write-message] + echo 'hello scriptjc.com'
[write-message] + echo 'Mount Path: /data'
[write-message] + echo 'Volume Name: ws-27mms'
[write-message] Mount Path: /data
[write-message] Volume Name: ws-27mms
[write-message] Claim Name: 
[write-message] + echo 'Claim Name: '

task中多个steps共享workspaces:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: source-lister
spec:
  params:
  - name: git-repo
    type: string
    description: Git repo to be cloned
  workspaces:
  - name: source
  steps:
  - name: git-clone
    image: alpine/git:v2.36.1
    script: git clone -v $(params.git-repo) $(workspaces.source.path)/source
  - name: list-files
    image: alpine:3.15
    command:
    - /bin/sh
    args:
    - '-c'
    - 'ls $(workspaces.source.path)/source'

运行:

]# tkn task start --showlog source-lister -w name=source,emptyDir="" -p git-repo=https://gitee.com/mageedu/spring-boot-helloWorld.git
TaskRun started: source-lister-run-qf9b9
Waiting for logs to be available...
[git-clone] Cloning into '/workspace/source/source'...
[git-clone] POST git-upload-pack (175 bytes)
[git-clone] POST git-upload-pack (367 bytes)

[list-files] Dockerfile
[list-files] Jenkinsfile
[list-files] LICENSE
[list-files] README.md
[list-files] deploy
[list-files] pom.xml
[list-files] src

pipeline中使用workspaces和传参:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: pipeline-source-lister
spec:
  workspaces:
  - name: codebase
  params:
  - name: git-url
    type: string
    description: Git repo url to be cloned
  tasks:
  - name: git-clone
    taskRef:
      name: source-lister # 引用的是那个task
    workspaces:
    - name: source
      workspace: codebase # 给这个task的workspace赋值
    params:
    - name: git-repo
      value: $(params.git-url) # 给task中的这个参数赋值

启动任务:

]# tkn pipeline start --showlog pipeline-source-lister -w name=codebase,emptyDir="" -p git-url=https://gitee.com/mageedu/spring-boot-helloWorld.git
PipelineRun started: pipeline-source-lister-run-pm28l
Waiting for logs to be available...
[git-clone : git-clone] Cloning into '/workspace/source/source'...
[git-clone : git-clone] POST git-upload-pack (175 bytes)
[git-clone : git-clone] POST git-upload-pack (367 bytes)

[git-clone : list-files] Dockerfile
[git-clone : list-files] Jenkinsfile
[git-clone : list-files] LICENSE
[git-clone : list-files] README.md
[git-clone : list-files] deploy
[git-clone : list-files] pom.xml
[git-clone : list-files] src

使用pvc启动pipelineRun:

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: pipeline-source-lister-run-00001
spec:
  pipelineRef:
    name: pipeline-source-lister
  params:
  - name: git-url
    value: https://gitee.com/mageedu/spring-boot-helloWorld.git
  workspaces:
  - name: codebase
    volumeClaimTemplate:
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 1Gi
        storageClassName: managed-nfs-storage

pipeline编译maven项目:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: source-2-package
spec:
  params: # pipeline级别的参数
  - name: git-url
    type: string
  workspaces: # pipeline级别的工作空间
  - name: codebase
  tasks: # 在pipeline中直接写入task字段
  - name: fetch-from-source # task1
    params: # 定义 task1 用到的参数
    - name: url
      value: $(params.git-url)
    workspaces: # 定义 task1 用到的工作空间
    - name: source
      workspace: codebase
    taskSpec:
      workspaces:
      - name: source
      params:
      - name: url
      steps:
      - name: git-clone
        image: alpine/git:v2.36.1
        script: git clone -v $(params.url) $(workspaces.source.path)/source
  - name: build # task 2
    workspaces: # task2用到的工作空间
    - name: source
      workspace: codebase
    runAfter: # 定义task2的运行顺序
    - fetch-from-source
    taskSpec:
      workspaces: # 定义用到的工作空间
      - name: source
      steps:
      - name: build
        image: maven:3.8-openjdk-11-slim
        workingDir: $(workspaces.source.path)/source
        script: |
          mvn clean install
---
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: source-to-build-run-00001
spec:
  pipelineRef:
    name: source-2-package
  params:
  - name: git-url
    value: https://gitee.com/mageedu/spring-boot-helloWorld.git
  workspaces:
  - name: codebase
    volumeClaimTemplate:
      spec:
        storageClassName: managed-nfs-storage
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 1Gi

固定的volume:支持夸pipelineRun共享(生命周期同pipeline)。workspace不支持夸pipelinerun共享(生命周期同pipelineRun)。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: maven-cache # 这个pvc是做缓存用的,在task里直接引用,而不是在pipelineRun里引用,需要手动创建
spec:
  storageClassName: managed-nfs-storage
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
---
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: source-2-package
spec:
  params:
    - name: git-url
      type: string
  workspaces:
    - name: codebase
  tasks:
    - name: fetch-from-source
      params:
        - name: url
          value: $(params.git-url)
      taskSpec:
        workspaces:
          - name: source
        params:
          - name: url
        steps:
          - name: git-clone
            image: alpine/git:v2.36.1
            script: git clone -v $(params.url) $(workspaces.source.path)/source
      workspaces:
        - name: source
          workspace: codebase
    - name: build-package
      runAfter:
        - fetch-from-source
      taskSpec:
        steps:
          - name: build
            image: maven:3.8-openjdk-11-slim
            workingDir: $(workspaces.source.path)/source
            volumeMounts:
              - name: m2 # 挂载使用
                mountPath: /root/.m2
            script: mvn clean install
        workspaces:
          - name: source
        volumes:
          - name: m2
            persistentVolumeClaim:
              claimName: maven-cache # 和pipeline绑定在一起,作为缓存使用,多个pipelineRun可共享多路读写
      workspaces:
        - name: source
          workspace: codebase
---
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: source-2-package-run-002
spec:
  pipelineRef:
    name: source-2-package
  params:
    - name: git-url
      value: https://gitee.com/mageedu/spring-boot-helloWorld.git
  workspaces:
    - name: codebase
      volumeClaimTemplate:
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 1Gi
          storageClassName: managed-nfs-storage


Results


可在steps之间、在tasks之间共享数据。

$(results.<result_name>.path) 保存result的文件路径。

取值方法:

script: |

    RET=$(cat /tekton/results/<result_name>)

tasks中引用:results.<result_name>.path

跨tasks引用:tasks.<taskName>.results.<result_name>

跨step使用:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: generate-buildid
spec:
  params:
  - name: version
    type: string
    description: the version of application
    default: "v0.9"
  results:
  - name: datetime
    description: The current datetime
  - name: buildId
    description: The build ID
  steps:
  - name: generate-datetime
    image: ikubernetes/admin-box:v1.2
    script: |
      #!/usr/bin/env bash
      datetime=`date +%Y%m%d-%H%M%S`
      echo -n ${datetime} | tee $(results.datetime.path) # 将结果存入 results.datetime 
  - name: generate-buildid
    image: ikubernetes/admin-box:v1.2
    script: |
      #!/usr/bin/env bash
      buildDatetime=`cat $(results.datetime.path)` # 使用 results.datetime 的值
      buildId=$(params.version)-${buildDatetime}
      echo -n ${buildId} | tee $(results.buildId.path) # 将结果存入 results.buildId


其他语句


when:执行条件

conditions(已弃用):when代替

timeout:超时时长

runAfter:定义执行顺序

finally:最终任务

retries:定义重试次数

when、input、operator:

tasks:
- name: first-create-file
  when:
  - input: "$(params.path)"
    operator: in # 只有in和notin两种值
    values: ["README.md"]
  taskRef:
    name: first-create-file
---
tasks:
- name: echo-file-exists
  when:
  - input: "$(tasks.check-file.results.exists)"
    operator: in
    values: ["yes"]
  taskRef:
    name: echo-file-exists
---
tasks:
- name: deploy-in-blue
  when:
  - input: "blue"
    operator: in
    values: ["$(params.deployment[*])"]
  taskRef:
    name: deployment
---
tasks:
- name: manual-approval
  runAfter:
  - tests
  when:
  - input: $(params.git-action)
    operator: in
    values: ['merge']
  taskRef:
    name: manual-approval
- name: build-image
  when:
  - input: $(oarams.git-action)
    operator: in
    values: ['merge']
  runAfter:
  - manual-approval
  taskRef:
    name: build-image
- name: deploy-image
  when:
  - input: $(params.git-action)
    operator: in
    values: ['merge']
  runAfter:
  - build-image
  taskRef:
    name: deploy-image
- name: slack-msg
  params:
  - name: approver
    value: $(tasks.manual-approval.results.approver)
  taskRef:
    name: slack-msg

finally:

finally:
- name:
  taskRef:
  taskSpec:

示例:

spec:
  workspaces:
    - name: shared-workspace
  tasks:
    - name: clone-app-source
      taskRef:
        name: clone-app-repo-to-workspace
      workspaces:
        - name: shared-workspace
          workspace: shared-workspace
  finally:
    - name: cleanup-workspace
      taskRef:
        name: cleanup-workspace
      workspaces:
        - name: shared-workspace
          workspace: shared-workspace


构建镜像


打镜像并推送:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: maven-cache # 这个pvc是做缓存用的,在task里直接引用,而不是在pipelineRun里引用,需要手动创建
spec:
  storageClassName: managed-nfs-storage
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: git-clone
spec:
  description: "克隆代码到工作空间"
  params:
  - name: url
    type: string
    description: "仓库地址"
    default: ""
  - name: branch
    type: string
    description: "代码分支"
    default: "main"
  workspaces:
  - name: source
    description: "代码下载到挂载的工作空间磁盘上"
  steps:
  - name: git-clone
    image: alpine/git:v2.36.1
    script: git clone -b $(params.branch) -v $(params.url) $(workspaces.source.path)/source
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-to-package
spec:
  description: "编译"
  workspaces:
  - name: source
    description: "代码下载目录"
  steps:
  - name: build
    image: maven:3.8-openjdk-11-slim
    workingDir: $(workspaces.source.path)/source
    volumeMounts:
    - name: m2
      mountPath: /root/.m2
    script: mvn clean install
  volumes:
  - name: m2
    persistentVolumeClaim:
      claimName: maven-cache # 这个pvc是做缓存用的,在task里直接引用,而不是在pipelineRun里引用,需要手动创建
---
# 镜像版本号和日期后缀
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: generate-build-id
spec:
  params:
  - name: version
    type: string
    description: "应用版本号"
  results:
  - name: datetime
    description: "当前时间"
  - name: buildId
    description: "构建ID"
  steps:
  - name: generate-datetime
    image: ikubernetes/admin-box:v1.2
    script: |
      #!/usr/bin/env bash
      datetime=`date +%Y%m%d-%H%M%S`
      echo -n ${datetime} | tee $(results.datetime.path)
  - name: generate-buildid
    image: ikubernetes/admin-box:v1.2
    script: |
      #!/usr/bin/env bash
      buildDatetime=`cat $(results.datetime.path)`
      buildId=$(params.version)-${buildDatetime}
      echo -n ${buildId} | tee $(results.buildId.path)
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: image-build
spec:
  description: "打镜像"
  params:
  - name: dockerfile
    description: "dockerfile 文件"
    default: Dockerfile
  - name: image-url
    description: "镜像地址"
  - name: image-tag
    description: "image tag"
    default: latest
  workspaces:
  - name: source
  - name: dockerconfig # docker登陆信息的配置文件config.json 
    mountPath: /kaniko/.docker # config.json挂载目录
  steps:
  - name: build-and-push-image
    image: gcr.io/kaniko-project/executor:debug # 用于在docker中打镜像的镜像
    securityContext:
      runAsUser: 0
    env:
      - name: DOCKER_CONFIG
        value: /kaniko/.docker
    command:
    - /kaniko/executor
    args:
    - --dockerfile=$(params.dockerfile) # dockerfile文件位置
    - --context=$(workspaces.source.path)/source # 上下文,也就是构建需要的文件,如代码文件等
    - --destination=$(params.image-url):$(params.image-tag) # 构建出来的镜像直接推送,要给出推送的地址
    #- --no-push # 打镜像,但不推送
---
# pipeline 把前面的task都串起来
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: source-to-image
spec:
  params:
    - name: git-url
    - name: pathToContext
      description: "workspace中构建镜像的路径,默认就 ."
      default: .
    - name: image-url
      description: "image url"
    - name: version
      default: "v0.9"
      type: string
      description: "image version"
  workspaces:
    - name: codebase # 定义工作空间一
    - name: docker-config # 定义工作空间二
  tasks:
    - name: git-clone
      taskRef:
        name: git-clone
      params:
        - name: url
          value: "$(params.git-url)"
      workspaces:
        - name: source
          workspace: codebase
    - name: build-to-package
      taskRef:
        name: build-to-package
      workspaces:
        - name: source
          workspace: codebase
      runAfter:
        - git-clone
    - name: generate-build-id
      taskRef: 
        name: generate-build-id
      params:
      - name: version
        value: "$(params.version)"
      runAfter:
      - git-clone
    - name: image-build
      taskRef:
        name: image-build
      params:
        - name: image-url
          value: "$(params.image-url)"
        - name: image-tag
          value: "$(tasks.generate-build-id.results.buildId)"
      workspaces:
        - name: source
          workspace: codebase
        - name: dockerconfig
          workspace: docker-config
      runAfter:
        - build-to-package
        - generate-build-id
---
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: s2i-image-push-run-00010
spec:
  pipelineRef:
    name: source-to-image
  params:
    - name: git-url
      value: https://gitee.com/mageedu/spring-boot-helloWorld.git
    - name: image-url
      value: zhuqiyang/spring-boot-helloworld
    - name: version
      value: v0.9.2
  workspaces:
    - name: codebase
      volumeClaimTemplate:
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 1Gi
          storageClassName: managed-nfs-storage
    - name: docker-config
      secret:
        secretName: docker-config # push镜像用的配置文件,账号和密码等信息,默认位置 /root/.docker/config.json
                                  # 创建命令 kubectl create secret generic docker-config --from-file=/root/.docker/config.json

打镜像并推送并部署到集群:

---
# ServiceAccount 和 ClusterRoleBinding 是为了将资源文件部署到k8s使用的
apiVersion: v1
kind: ServiceAccount
metadata:
  name: helloworld-admin
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: helloworld-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: helloworld-admin
  namespace: default
---
# 这个pvc是做缓存用的,在task里直接引用,而不是在pipelineRun里引用,需要手动创建
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: maven-cache
spec:
  storageClassName: managed-nfs-storage
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: git-clone
spec:
  description: "克隆代码到工作空间"
  params:
  - name: url
    type: string
    description: "仓库地址"
    default: ""
  - name: branch
    type: string
    description: "代码分支"
    default: "main"
  workspaces:
  - name: source
    description: "代码下载到挂载的工作空间磁盘上"
  steps:
  - name: git-clone
    image: alpine/git:v2.36.1
    script: git clone -b $(params.branch) -v $(params.url) $(workspaces.source.path)/source
---
# 打包
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-to-package
spec:
  description: "编译"
  workspaces:
  - name: source
    description: "代码下载目录"
  steps:
  - name: build
    image: maven:3.8-openjdk-11-slim
    workingDir: $(workspaces.source.path)/source
    volumeMounts:
    - name: m2
      mountPath: /root/.m2
    script: mvn clean install
  volumes:
  - name: m2
    persistentVolumeClaim:
      claimName: maven-cache # 这个pvc是做缓存用的,在task里直接引用,而不是在pipelineRun里引用,需要手动创建
---
# 镜像版本号和日期后缀
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: generate-build-id
spec:
  params:
  - name: version
    type: string
    description: "应用版本号"
  results:
  - name: datetime
    description: "当前时间"
  - name: buildId
    description: "构建ID"
  steps:
  - name: generate-datetime
    image: ikubernetes/admin-box:v1.2
    script: |
      #!/usr/bin/env bash
      datetime=`date +%Y%m%d-%H%M%S`
      echo -n ${datetime} | tee $(results.datetime.path)
  - name: generate-buildid
    image: ikubernetes/admin-box:v1.2
    script: |
      #!/usr/bin/env bash
      buildDatetime=`cat $(results.datetime.path)`
      buildId=$(params.version)-${buildDatetime}
      echo -n ${buildId} | tee $(results.buildId.path)
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: image-build
spec:
  description: "打镜像"
  params:
  - name: dockerfile
    description: "dockerfile 文件"
    default: Dockerfile
  - name: image-url
    description: "镜像地址"
  - name: image-tag
    description: "image tag"
    default: latest
  workspaces:
  - name: source
  - name: dockerconfig # docker登陆信息的配置文件config.json 
    mountPath: /kaniko/.docker # config.json挂载目录
  steps:
  - name: build-and-push-image
    image: gcr.io/kaniko-project/executor:debug # 用于在docker中打镜像的镜像
    securityContext:
      runAsUser: 0
    env:
      - name: DOCKER_CONFIG
        value: /kaniko/.docker
    command:
    - /kaniko/executor
    args:
    - --dockerfile=$(params.dockerfile) # dockerfile文件位置
    - --context=$(workspaces.source.path)/source # 上下文,也就是构建需要的文件,如代码文件等
    - --destination=$(params.image-url):$(params.image-tag) # 构建出来的镜像直接推送,要给出推送的地址
    #- --no-push # 打镜像,但不推送
---
# 上面的 ServiceAccount 和 ClusterRoleBinding 就是为这里定义的
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: deploy-using-kubectl
spec:
  workspaces:
  - name: source
    description: "git仓库的工作空间,里面有deployment部署文件"
  params:
  - name: deploy-config-file
    description: "deployment部署文件路径"
  - name: image-url
    description: "镜像地址"
  - name: image-tag
    description: "镜像标签"
  steps:
  - name: update-yaml
    image: alpine:3.16
    command: ["sed"]
    args:
    - "-i"
    - "-e"
    - "s@__IMAGE__@$(params.image-url):$(params.image-tag)@g"
    - "$(workspaces.source.path)/source/deploy/$(params.deploy-config-file)"
  - name: run-kubectl
    image: lachlanevenson/k8s-kubectl # 这个镜像有kubectl命令
    command: ["kubectl"]
    args:
    - "apply"
    - "-f"
    - "$(workspaces.source.path)/source/deploy/$(params.deploy-config-file)"
---
# pipeline 把前面的task都串起来
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: source-to-image
spec:
  params:
    - name: git-url
    - name: pathToContext
      description: "workspace中构建镜像的路径,默认就 ."
      default: .
    - name: deploy-config-file
      description: "yaml文件名称"
      default: all-in-one.yaml
    - name: image-url
      description: "image url"
    - name: version
      default: "v0.9"
      type: string
      description: "image version"
  workspaces:
    - name: codebase # 定义工作空间一
    - name: docker-config # 定义工作空间二
  tasks:
    - name: git-clone
      taskRef:
        name: git-clone
      params:
        - name: url
          value: "$(params.git-url)"
      workspaces:
        - name: source
          workspace: codebase
    - name: build-to-package
      taskRef:
        name: build-to-package
      workspaces:
        - name: source
          workspace: codebase
      runAfter:
        - git-clone
    - name: generate-build-id
      taskRef: 
        name: generate-build-id
      params:
      - name: version
        value: "$(params.version)"
      runAfter:
      - git-clone
    - name: image-build
      taskRef:
        name: image-build
      params:
        - name: image-url
          value: "$(params.image-url)"
        - name: image-tag
          value: "$(tasks.generate-build-id.results.buildId)"
      workspaces:
        - name: source
          workspace: codebase
        - name: dockerconfig
          workspace: docker-config
      runAfter:
        - build-to-package
        - generate-build-id
    - name: deploy-to-cluster
      taskRef:
        name: deploy-using-kubectl
      workspaces:
        - name: source
          workspace: codebase
      params:
      - name: deploy-config-file
        value: "$(params.deploy-config-file)"
      - name: image-url
        value: "$(params.image-url)"
      - name: image-tag
        value: "$(tasks.generate-build-id.results.buildId)"
      runAfter:
      - image-build
---
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: s2i-image-push-run-00015
spec:
  serviceAccountName: default # 默认其他task的运行用户,默认可不写
  taskRunSpecs:
  - pipelineTaskName: deploy-to-cluster # 特指某个task,这个名称是pipeline中任务的名称,不是task名称
    taskServiceAccountName: helloworld-admin # 指定task使用的账号
  pipelineRef:
    name: source-to-image
  params:
    - name: git-url
      value: https://gitee.com/mageedu/spring-boot-helloWorld.git
    - name: image-url
      value: zhuqiyang/spring-boot-helloworld
    - name: version
      value: v0.9.2
  workspaces:
    - name: codebase
      volumeClaimTemplate:
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 1Gi
          storageClassName: managed-nfs-storage
    - name: docker-config
      secret:
        secretName: docker-config # push镜像用的配置文件,账号和密码等信息,默认位置 /root/.docker/config.json
                                  # 创建命令 kubectl create secret generic docker-config --from-file=/root/.docker/config.json


部署trigger


interceptor:对特定平台的事件源进行处理,处理完了给trigger,是个crd资源。

trigger:监控推送等事件,触发pipeline执行,会起个pod叫xx-event-listener。

triggerBinding:将事件中的值提取出并赋值给tiggerTemplate。

tiggerTemplate:pipelinerun的模板,模板的值就是triggerBinding传过来的。

Trigger部署文件:

kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml

部署事件源interceptor:

kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml

查看资源:

]# kubectl api-resources --api-group=triggers.tekton.dev
NAME                     SHORTNAMES   APIGROUP              NAMESPACED   KIND
clusterinterceptors      ci           triggers.tekton.dev   false        ClusterInterceptor
clustertriggerbindings   ctb          triggers.tekton.dev   false        ClusterTriggerBinding
eventlisteners           el           triggers.tekton.dev   true         EventListener
triggerbindings          tb           triggers.tekton.dev   true         TriggerBinding
triggers                 tri          triggers.tekton.dev   true         Trigger
triggertemplates         tt           triggers.tekton.dev   true         TriggerTemplate
]# kubectl get ClusterInterceptors
NAME        AGE
bitbucket   3d21h
cel         3d21h
github      3d21h
gitlab      3d21h

暴露事件接收服务 el-gitlab-event-listener:用来接收gitlab发来的事件,通过ingress或nodeport方式都可。

default            el-gitlab-event-listener-65d4c47f8d-tfztp            1/1     Running     0          28m
el-gitlab-event-listener   ClusterIP   10.43.238.123   <none>        8080/TCP,9000/TCP   34m

暴露服务文件:每个流水线都要新建一个event listener,每个event listener都要去暴露。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: el-gitlab-event-listener.ops.net
  namespace: default
spec:
  rules:
  - host: el-gitlab-event-listener.ops.net
    http:
      paths:
      - backend:
          serviceName: el-gitlab-event-listener
          servicePort: 8080

在gitlab中设置webhook:

image.png

20220927153957.png

事件body:这些值可以在pipeline中引用。

{
	"object_kind": "push",
	"event_name": "push",
	"before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
	"after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
	"ref": "refs/heads/master",
	"checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
	......
	"project":{
		"id": 15,
		"name":"Diaspora",
		"description":"",
		"web_url":"http://example.com/mike/diaspora",
		"avatar_url":null,
		"git_ssh_url":"git@example.com:mike/diaspora.git",
		"git_http_url":"http://example.com/mike/diaspora.git",
		... 
	},
	"repository":{
		"name": "Diaspora",
		"url": "git@example.com:mike/diaspora.git",
		"description": "",
		"homepage": "http://example.com/mike/diaspora",
		"git_http_url":"http://example.com/mike/diaspora.git",
		"git_ssh_url":"git@example.com:mike/diaspora.git",
		"visibility_level":0
	},
	"commits": [
		{
			"id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
			"message": "Update Catalan translation to e38cb41.\n\nSee https://gitlab.com/gitlab-org/gitlab for more information",
			"title": "Update Catalan translation to e38cb41.",
			......
			"added": ["CHANGELOG"],
			"modified": ["app/controller/application.rb"],
			"removed": []
		}
	],
	"total_commits_count": 4
}

流水线脚本:

apiVersion: v1
kind: Secret
metadata:
  name: gitlab-webhook-token
type: Opaque
stringData:
  # Generated by command "openssl rand -base64 12"
  webhookToken: "DXeqvozMlTA67aQB" # 这个值就是在gitlab中webhook中要填写的secret令牌
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tekton-triggers-gitlab-sa
secrets:
- name: gitlab-webhook-token
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tekton-triggers-gitlab-minimal
rules:
  # Permissions for every EventListener deployment to function
  - apiGroups: ["triggers.tekton.dev"]
    resources: ["eventlisteners", "triggerbindings", "triggertemplates"]
    verbs: ["get"]
  - apiGroups: [""]
    # secrets are only needed for Github/Gitlab interceptors, serviceaccounts only for per trigger authorization
    resources: ["configmaps", "secrets", "serviceaccounts"]
    verbs: ["get", "list", "watch"]
  # Permissions to create resources in associated TriggerTemplates
  - apiGroups: ["tekton.dev"]
    resources: ["pipelineruns", "pipelineresources", "taskruns"]
    verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: tekton-triggers-gitlab-binding
subjects:
  - kind: ServiceAccount
    name: tekton-triggers-gitlab-sa
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: tekton-triggers-gitlab-minimal
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tekton-triggers-gitlab-minimal
rules:
  - apiGroups: ["triggers.tekton.dev"]
    resources: ["clusterinterceptors"]
    verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tekton-triggers-gitlab-binding
subjects:
  - kind: ServiceAccount
    name: tekton-triggers-gitlab-sa
    namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: tekton-triggers-gitlab-minimal
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: helloworld-admin
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: helloworld-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: helloworld-admin
  namespace: default
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: maven-cache
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  storageClassName: managed-nfs-storage
  volumeMode: Filesystem
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: git-clone
spec:
  description: Clone the code repository to the workspace. 
  params:
    - name: git-repo-url
      type: string
      description: git repository url to clone
    - name: git-revision
      type: string
      description: git revision to checkout (branch, tag, sha, ref)
  workspaces:
    - name: source
      description: The git repo will be cloned onto the volume backing this workspace
  steps:
    - name: git-clone
      image: alpine/git:v2.36.1
      script: | 
        git clone -v $(params.git-repo-url) $(workspaces.source.path)/source
        cd $(workspaces.source.path)/source && git reset --hard $(params.git-revision)
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-to-package
spec:
  description: build application and package the files to image
  workspaces:
    - name: source
      description: The git repo that cloned onto the volume backing this workspace
  steps:
    - name: build
      image: maven:3.8-openjdk-11-slim
      workingDir: $(workspaces.source.path)/source
      volumeMounts:
        - name: m2
          mountPath: /root/.m2
      script: mvn clean install
  volumes:
    - name: m2
      persistentVolumeClaim:
        claimName: maven-cache
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: generate-build-id
spec:
  params:
    - name: version
      description: The version of the application
      type: string
  results:
    - name: datetime
      description: The current date and time
    - name: buildId
      description: The build ID
  steps:
    - name: generate-datetime
      image: ikubernetes/admin-box:v1.2
      script: |
        #!/usr/bin/env bash
        datetime=`date +%Y%m%d-%H%M%S`
        echo -n ${datetime} | tee $(results.datetime.path)
    - name: generate-buildid
      image: ikubernetes/admin-box:v1.2
      script: |
        #!/usr/bin/env bash
        buildDatetime=`cat $(results.datetime.path)`
        buildId=$(params.version)-${buildDatetime}
        echo -n ${buildId} | tee $(results.buildId.path)
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: image-build-and-push
spec:
  description: package the application files to image
  params:
    - name: dockerfile
      description: The path to the dockerfile to build (relative to the context)
      default: Dockerfile
    - name: image-url
      description: Url of image repository
    - name: image-tag
      description: Tag to apply to the built image
  workspaces:
    - name: source
    - name: dockerconfig
      mountPath: /kaniko/.docker
  steps:
    - name: image-build-and-push
      image: gcr.io/kaniko-project/executor:debug
      securityContext:
        runAsUser: 0
      env:
        - name: DOCKER_CONFIG
          value: /kaniko/.docker
      command:
        - /kaniko/executor
      args:
        - --dockerfile=$(params.dockerfile)
        - --context=$(workspaces.source.path)/source
        - --destination=$(params.image-url):$(params.image-tag)
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: deploy-using-kubectl
spec:
  workspaces:
    - name: source
      description: The git repo
  params:
    - name: deploy-config-file
      description: The path to the yaml file to deploy within the git source
    - name: image-url
      description: Image name including repository
    - name: image-tag
      description: Image tag
  steps:
    - name: update-yaml
      image: alpine:3.16
      command: ["sed"]
      args:
        - "-i"
        - "-e"
        - "s@__IMAGE__@$(params.image-url):$(params.image-tag)@g"
        - "$(workspaces.source.path)/source/deploy/$(params.deploy-config-file)"
    - name: run-kubectl
      image: lachlanevenson/k8s-kubectl
      command: ["kubectl"]
      args:
        - "apply"
        - "-f"
        - "$(workspaces.source.path)/source/deploy/$(params.deploy-config-file)"
---
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: source-to-image
spec:
  params:
    - name: git-repo-url
      type: string
      description: git repository url to clone
    - name: git-revision
      type: string
      description: git revision to checkout (branch, tag, sha, ref)
      default: main
    - name: image-build-context
      description: The path to the build context, used by Kaniko - within the workspace
      default: .
    - name: image-url
      description: Url of image repository
    - name: version
      description: The version of the application
      type: string
      default: "v0.9" 
    - name: deploy-config-file
      description: The path to the yaml file to deploy within the git source
      default: all-in-one.yaml
  #results:
  #  - name: datetime
  #    description: The current date and time
  #  - name: buildId
  #    description: The build ID
  workspaces:
    - name: codebase
    - name: docker-config
  tasks:
    - name: git-clone
      taskRef:
        name: git-clone
      params:
        - name: git-repo-url
          value: "$(params.git-repo-url)"
        - name: git-revision
          value: "$(params.git-revision)"
      workspaces:
        - name: source
          workspace: codebase
    - name: build-to-package
      taskRef:
        name: build-to-package
      workspaces:
        - name: source
          workspace: codebase
      runAfter:
        - git-clone
    - name: generate-build-id
      taskRef:
        name: generate-build-id
      params:
        - name: version
          value: "$(params.version)"
      runAfter:
        - git-clone
    - name: image-build-and-push
      taskRef:
        name: image-build-and-push
      params:
        - name: image-url
          value: "$(params.image-url)"
        - name: image-tag
          value: "$(tasks.generate-build-id.results.buildId)"
      workspaces:
        - name: source
          workspace: codebase
        - name: dockerconfig
          workspace: docker-config
      runAfter:
        - generate-build-id
        - build-to-package
    - name: deploy-to-cluster
      taskRef:
        name: deploy-using-kubectl
      workspaces:
        - name: source
          workspace: codebase
      params:
        - name: deploy-config-file
          value: $(params.deploy-config-file)
        - name: image-url
          value: $(params.image-url)
        - name: image-tag
          value: "$(tasks.generate-build-id.results.buildId)"
      runAfter:
        - image-build-and-push
---
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding # 这里的值会传给TriggerTemplate,不需要传递值则可省略TriggerTemplate
metadata:
  name: s2i-binding
spec:
  params:
  - name: git-revision
    value: $(body.checkout_sha) # 这里的值引用的就是上面列出的事件body中的值
  - name: git-repo-url
    value: $(body.repository.git_http_url)
  - name: image-url
    value: zhuqiyang/spring-boot-helloworld
  - name: version
    value: v0.9
---
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerTemplate
metadata:
  name: s2i-tt
spec:
  params:  # 定义参数,这里的值是由TriggerBinding传过来的
  - name: git-revision
  - name: git-repo-url
  - name: image-url
  - name: version
  resourcetemplates: # 资源模板
  - apiVersion: tekton.dev/v1beta1
    kind: PipelineRun # 可以是pipelinerun,也可以是taskrun
    metadata:
      generateName: s2i-trigger-run-  # pipelineRun 名称前缀
    spec:
      serviceAccountName: default
      ## PipelineRun.Spec.ServiceAccountNames field is deprecated; use PipelineRun.Spec.TaskRunSpecs instead.
      #serviceAccountNames:
      #  - taskName: deploy-to-cluster
      #    serviceAccountName: helloworld-admin
      pipelineRef:
        name: source-to-image # 引用的哪个pipeline
      taskRunSpecs:
        - pipelineTaskName: deploy-to-cluster
          taskServiceAccountName: helloworld-admin
      #    taskPodTemplate:
      #      nodeSelector:
      #        disktype: ssd
      params:
        - name: git-repo-url
          value: $(tt.params.git-repo-url) # 这里的tt指的就是TriggerTemplate
        - name: git-revision
          value: $(tt.params.git-revision)
        - name: image-url
          value: $(tt.params.image-url)
        - name: version
          value: $(tt.params.version)
      workspaces:
        - name: codebase
          volumeClaimTemplate:
            spec:
              accessModes:
                - ReadWriteOnce
              resources:
                requests:
                  storage: 1Gi
              storageClassName: managed-nfs-storage
        - name: docker-config
          secret:
            secretName: docker-config
---
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: s2i-listener 
spec:
  serviceAccountName: tekton-triggers-gitlab-sa
  triggers: # 定义trigger,trigger可以单独定义,也可以内联定义,这里是内联定义
  - name: gitlab-push-events-trigger
    interceptors:
    - ref:
        name: "gitlab" # gitlab实例化的interceptor
      params:
      - name: "secretRef" # 访问gitlab使用的账号和密码
        value:
          secretName: gitlab-webhook-token 
          secretKey: webhookToken
      - name: "eventTypes" # 监视仓库的那些事件
        value: 
          - "Push Hook"
          - "Tag Push Hook"
          - "Merge Request Hook" # 事件列表
    bindings: # 将triggerbinding和triggertemplate绑定起来
    - ref: s2i-binding # triggerbinding
    template:
      ref: s2i-tt # triggertemplate
---


tekton hub


这里有别人定义好的Task:

https://hub.tekton.dev/

catalog:

https://github.com/tektoncd/catalog

克隆代码:

https://github.com/tektoncd/catalog/tree/main/task/git-clone