任务¶
使用 Tekton 后你的 CI/CD 工作流中的每个操作都变成了一个 Step,使用指定的容器镜像来执行。Steps 然后组织在 Tasks 中,它在集群中作为 Kubernetes Pod 运行,还可以进一步组织 Tasks 变成成 Pipelines,还可以控制几个 Tasks 的执行顺序。
在这里我们使用一个简单的 Golang 应用,可以在仓库 https://github.com/cnych/tekton-demo 下面获取应用程序代码,测试以及 Dockerfile 文件。
首先第一个任务就是 Clone 应用程序代码进行测试,要创建一个 Task 任务,就需要使用到 Kubernetes 中定义的 Task 这个 CRD 对象,这里我们创建一个如下所示的资源文件,内容如下所示:
# task-test.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: test
spec:
resources:
inputs:
- name: repo
type: git
steps:
- name: run-test
image: golang:1.14-alpine
workingDir: /workspace/repo
command: ['go']
args: ['test']
其中 resources
定义了我们的任务中定义的 Step 所需的输入内容,这里我们的步骤需要 Clone 一个 Git 仓库作为 go test
命令的输入,目前支持 git、pullRequest、image、cluster、storage、cloudevent 等资源。
Tekton 内置的 git 资源类型,它会自动将代码仓库 Clone 到 /workspace/$input_name
目录中,由于我们这里输入被命名成 repo,所以代码会被 Clone 到 /workspace/repo
目录下面。
然后下面的 steps
就是来定义执行运行测试命令的步骤,这里我们直接在代码的根目录中运行 go test
命令即可,需要注意的是命令和参数需要分别定义。
定义完成后直接使用 kubectl 创建该任务:
$ kubectl apply -f task-test.yaml
task.tekton.dev/test created
现在我们定义完成了一个新建的 Task 任务,但是该任务并不会立即执行,我们必须创建一个 TaskRun
引用它并提供所有必需输入的数据才行。当然我们也可以直接使用 tkn
命令来启动这个 Task 任务,我们可以通过如下所示的命令来获取启动 Task 所需的资源对象:
$ tkn task start test --dry-run
no pipeline resource of type "git" found in namespace: default
Please create a new "git" resource for pipeline resource "repo"
? Enter a name for a pipeline resource : demo-git
? Enter a value for url : https://github.com/cnych/tekton-demo
? Enter a value for revision : master
New git resource "demo-git" has been created
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
creationTimestamp: null
generateName: test-run-
namespace: default
spec:
resources:
inputs:
- name: repo
resourceRef:
name: demo-git
serviceAccountName: ""
taskRef:
name: test
status:
podName: ""
由于我们这里的 Task 任务需要一个 git 代码仓库作为输入,所以需要一个 PipelineResource
对象来定义输入信息,上面的命令会自动创建一个名为 demo-git
的 PipelineResource
资源对象,如下所示:
$ kubectl get pipelineresource
NAME AGE
demo-git 3m37s
$ kubectl get pipelineresource demo-git -o yaml
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: demo-git
namespace: default
......
spec:
params:
- name: url
value: https://github.com/cnych/tekton-demo
- name: revision
value: master
type: git
当我们不知道如何创建 PipelineResource
的时候我们就可以参考上面的方式来创建,当然最后还需要创建 TaskRun
对象才可以真正执行这个 Task 任务,上面的 tkn task start
命令也为我们打印出对应的 TaskRun 资源,将其内容添加到 taskrun.yaml
文件中:
# taskrun.yaml
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: testrun
spec:
resources:
inputs:
- name: repo
resourceRef:
name: demo-git
taskRef:
name: test
这里的 taskRef
引用上面定义的 Task 和 git 仓库作为输入,resourceRef
也是引用上面定义的 PipelineResource
资源对象。现在我们创建这个资源对象过后,就会开始运行了:
$ kubectl apply -f taskrun.yaml
taskrun.tekton.dev/testrun created
Tekton 现在将开始运行您的 Task, 要查看最后一个 TaskRun 的日志,可以使用以下 tkn
命令:
tkn taskrun logs --last -f
此外我们还可以通过查看 TaskRun 资源对象的状态来查看构建状态:
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
testrun Unknown Pending 21s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
testrun-pod-l629c 0/2 Init:1/2 0 59s
$ kubectl describe pod testrun-pod-l629c
Name: testrun-pod-l629c
Namespace: default
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m53s default-scheduler Successfully assigned default/testrun-pod-l629c to node1
Normal Pulling 2m52s kubelet, node1 Pulling image "cnych/tekton-distroless-base:v0.24.1"
Normal Pulled 2m27s kubelet, node1 Successfully pulled image "cnych/tekton-distroless-base:v0.24.1" in 24.910571044s
Normal Created 2m27s kubelet, node1 Created container working-dir-initializer
Normal Started 2m27s kubelet, node1 Started container working-dir-initializer
Normal Pulling 2m27s kubelet, node1 Pulling image "cnych/tekton-entrypoint:v0.24.1"
Normal Pulled 2m kubelet, node1 Successfully pulled image "cnych/tekton-entrypoint:v0.24.1" in 27.120230223s
Normal Created 2m kubelet, node1 Created container place-tools
Normal Started 2m kubelet, node1 Started container place-tools
Normal Pulling 119s kubelet, node1 Pulling image "cnych/tekton-git-init:v0.24.1"
Normal Pulled 82s kubelet, node1 Successfully pulled image "cnych/tekton-git-init:v0.24.1" in 36.318235738s
Normal Created 82s kubelet, node1 Created container step-git-source-repo-jg7vz
Normal Started 82s kubelet, node1 Started container step-git-source-repo-jg7vz
Normal Pulling 82s kubelet, node1 Pulling image "golang:1.14-alpine"
Normal Pulled 28s kubelet, node1 Successfully pulled image "golang:1.14-alpine" in 54.587298174s
Normal Created 27s kubelet, node1 Created container step-run-test
Normal Started 27s kubelet, node1 Started container step-run-test
我们可以通过 kubectl describe
命令来查看任务运行的过程,首先会通过 tekton-git-init
拉取代码,然后会使用我们定义的 Task 任务中的 Steps 镜像来执行任务。当任务执行完成后, Pod 就会变成 Completed 状态了:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
testrun-r-n97ls-pod-7jvrd 0/2 Completed 0 4m27s
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
testrun-r-n97ls True Succeeded 16m 119s
我们可以查看容器的日志信息来了解任务的执行结果信息:
$ kubectl logs testrun-r-n97ls-pod-7jvrd --all-containers
2021/06/08 09:07:31 Copied /ko-app/entrypoint to /tekton/tools/entrypoint
{"level":"info","ts":1623144122.7787642,"caller":"git/git.go:169","msg":"Successfully cloned https://github.com/cnych/tekton-demo @ c6c2a85091d538a13c44f85bcee9e861c362b0d3 (grafted, HEAD, origin/master) in path /workspace/repo"}
{"level":"info","ts":1623144122.796532,"caller":"git/git.go:207","msg":"Successfully initialized and updated submodules in path /workspace/repo"}
PASS
ok _/workspace/repo 0.002s
我们可以看到我们的测试已经通过了。
Docker Hub 配置¶
为了能够构建 Docker 镜像,一般来说我们需要使用 Docker 来进行,我们这里是容器,所以可以使用 Docker In Docker 模式
,这种模式安全性不高,除了这种方式之外,我们还可以使用 Google 推出的 Kaniko 工具来进行构建,该工具可以在 Kubernetes 集群中构建 Docker 镜像而无需依赖 Docker 守护进程,之前我们已经介绍过 kaniko 这种形式,这里我们就介绍 DIND
这种模式。登录凭证可以保存到 Kubernetes 的 Secret 资源对象中,创建一个名为 harbor-auth.yaml
的文件,内容如下所示:
# harbor-auth.yaml
apiVersion: v1
kind: Secret
metadata:
name: harbor-auth
annotations:
tekton.dev/docker-0: http://harbor.k8s.local
type: kubernetes.io/basic-auth
stringData:
username: admin
password: Harbor12345
记得将 username 和 password 替换成你的 Harbor 仓库登录凭证。
我们这里在 Secret 对象中添加了一个 tekton.dev/docker-0
的 annotation,该注解信息是用来告诉 Tekton 这些认证信息所属的 Docker 镜像仓库。
然后创建一个 ServiceAccount 对象来使用上面的 docker-auth
这个 Secret 对象,创建一个名为 sa.yaml
的文件,内容如下所示:
# sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: build-sa
secrets:
- name: harbor-auth
然后直接创建上面两个资源对象即可:
$ kubectl apply -f harbor-auth.yaml
secret/harbor-auth created
$ kubectl apply -f sa.yaml
serviceaccount/build-sa created
创建完成后,我们就可以在运行 Tekton 的任务或者流水线的时候使用上面的 build-sa 这个 ServiceAccount
对象来进行 Docker Hub 的登录认证了。
创建镜像任务¶
现在我们创建一个 Task 任务来构建并推送 Docker 镜像,我们这里使用的示例应用根目录下面已经包含了一个 Dockerfile
文件了,所以我们直接 Clone 代码就可以获得:
FROM golang:1.14-alpine
WORKDIR /go/src/app
COPY . .
RUN go get -d -v ./...
RUN go install -v ./...
CMD ["app"]
创建一个名为 task-build-push.yaml
的文件,文件内容如下所示:
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: build-and-push
spec:
resources:
inputs: # 定义输入资源
- name: repo #输入资源,就是github的那个仓库
type: git
outputs: # 定义输出资源
- name: builtImage # 输出镜像名字
type: image
params:
- name: pathToDockerfile #指明 dockerfile 在仓库中的哪个位置
type: string
default: $(resources.inputs.repo.path)/Dockerfile # repo资源的路径
description: The path to the dockerfile to build
- name: pathToContext #指明构建上下文的路径
type: string
default: $(resources.inputs.repo.path) # repo资源的路径
description: the build context used by docker daemon
steps:
- name: build-and-push
image: docker:stable
script: |
#!/usr/bin/env sh
docker login harbor.k8s.local
docker build -t $(resources.outputs.builtImage.url) -f $(params.pathToDockerfile) $(params.pathToContext)
docker push $(resources.outputs.builtImage.url) # 这边的参数都是在 input 和 output 中定义的
volumeMounts:
- name: dockersock #将docker.sock文件挂载进来,使用宿主机docker daemon 构建镜像
mountPath: /var/run/docker.sock
volumes:
- name: dockersock
hostPath:
path: /var/run/docker.sock
和前面的测试任务类似,这里我们同样将 git 作为输入资源,此外还定义了 pathToDockerfile
与 pathToContext
参数,用来指定 Dockerfile 和构建上下文的路径,此外还定义了一个名为 builtImage
的镜像输出资源,用来定义 Docker 镜像的相关参数。然后定义了一个名为 build-and-push 的步骤,这里我们使用 DIND
的方式,将宿主机的 /var/run/docker.sock
文件挂载到 docker:stable
的容器中,然后执行 script
下面的 Docker 镜像构建推送的操作。同样直接创建上面的资源对象即可:
$ kubectl apply -f task-build-push.yaml
task.tekton.dev/build-and-push created
创建了 Task 任务过后,要想真正去执行这个任务,我们就需要创建一个对应的 TaskRun 资源对象。
执行任务¶
和前面一样,现在我们来创建一个 TaskRun 对象来触发任务,不同之处在于我们需要指定 Task 时需要的 ServiceAccount 对象。创建一个名为 taskrun-build-push.yaml
的文件,内容如下所示:
# taskrun-build-push.yaml
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: build-and-push
spec:
serviceAccountName: build-sa # 关联具有harbor认证信息的serviceaccount
taskRef:
name: build-and-push # 关联定义好的task
resources:
inputs:
- name: repo # 指定输入的仓库资源
resourceRef:
name: demo-git
outputs: # 指定输出的镜像资源
- name: builtImage
resourceRef:
name: harbor-image
params:
- name: pathToDockerfile #指明 dockerfile 在仓库中的哪个位置
value: $(resources.inputs.repo.path)/Dockerfile # repo资源的路径
- name: pathToContext # 指定构建上下文
value: $(resources.inputs.repo.path) # repo资源的路径
注意这里我们通过 serviceAccountName
属性指定了 Docker 认证信息的 ServiceAccount
对象,然后通过 taskRef
引用我们的任务,以及下面的 resourceRef
关联第一部分我们声明的输入资源,此外还需要定义一个关于输出镜像的 PipelineResource
资源:
# harbor-image-res.yaml
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: harbor-image
spec:
type: image
params:
- name: url
value: harbor.k8s.local/course/tekton-demo:v0.1.0 #构建完的镜像名称
然后直接创建这个资源对象即可:
$ kubectl apply -f harbor-image-res.yaml
pipelineresource.tekton.dev/harbor-image created
$ kubectl apply -f taskrun-build-push.yaml
taskrun.tekton.dev/build-and-push created
创建完成后就会触发任务执行了,我们可以通过查看 Pod 对象状态来了解进度:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
build-and-push-pod-fl65m 0/4 PodInitializing 0 9s
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
build-and-push Unknown Pending 26s
现在任务执行的 Pod 还在初始化容器阶段,我们可以看到 TaskRun 的状态处于 Pending,隔一会儿正常构建就会成功了,我们可以查看构建任务的 Pod 日志信息:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
build-and-push-pod-fl65m 0/4 PodInitializing 0 9s
$ tkn taskrun logs build-and-push
[git-source-repo-tvvgc] {"level":"info","ts":1623926725.8210459,"caller":"git/git.go:169","msg":"Successfully cloned https://github.com.cnpmjs.org/cnych/tekton-demo @ 5e1e3a1d0f167b9b639df5b802a0f0f81064d21e (grafted, HEAD, origin/master) in path /workspace/repo"}
[git-source-repo-tvvgc] {"level":"info","ts":1623926725.839979,"caller":"git/git.go:207","msg":"Successfully initialized and updated submodules in path /workspace/repo"}
[build-and-push] Authenticating with existing credentials...
[build-and-push] WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
[build-and-push] Configure a credential helper to remove this warning. See
[build-and-push] https://docs.docker.com/engine/reference/commandline/login/#credentials-store
[build-and-push]
[build-and-push] Login Succeeded
[build-and-push] Sending build context to Docker daemon 154.1kB
[build-and-push] Step 1/6 : FROM golang:1.14-alpine
......
[build-and-push] Step 6/6 : CMD ["app"]
[build-and-push] ---> Running in a85d1ac643bc
[build-and-push] Removing intermediate container a85d1ac643bc
[build-and-push] ---> 28ae504f7ad3
[build-and-push] Successfully built 28ae504f7ad3
[build-and-push] Successfully tagged harbor.k8s.local/course/tekton-demo:v0.1.0
[build-and-push] The push refers to repository [harbor.k8s.local/course/tekton-demo]
......
[build-and-push] 8bc1350a153c: Pushed
[build-and-push] v0.1.0: digest: sha256:d35ccb0bcc7e7324350242c67cf042ef731f62d251250e11c50785ffddb803db size: 2198
[image-digest-exporter-kw59f] {"severity":"INFO","timestamp":"2021-06-17T10:45:30.694365617Z","caller":"logging/config.go:116","message":"Successfully created the logger."}
[image-digest-exporter-kw59f] {"severity":"INFO","timestamp":"2021-06-17T10:45:30.694429032Z","caller":"logging/config.go:117","message":"Logging level set to: info"}
[image-digest-exporter-kw59f] {"severity":"INFO","timestamp":"2021-06-17T10:45:30.694526242Z","caller":"imagedigestexporter/main.go:59","message":"No index.json found for: builtImage","commit":"7ca5d61"}
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
build-and-push True Succeeded 15m 2m24s
我们可以看到 TaskRun 任务已经执行成功了。 这个时候其实我们可以在 Harbor 上找到我们的镜像了,当然也可以直接使用这个镜像进行测试: