任务¶
使用 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
$ kubectl get task
NAME AGE
test 12s
现在我们定义完成了一个新建的 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 : git-res
? Enter a value for url : https://github.com/cnych/tekton-demo
? Enter a value for revision : master
New git resource "git-res" has been created
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
creationTimestamp: null
generateName: test-run-
namespace: default
spec:
resources:
inputs:
- name: repo
resourceRef:
name: git-res
serviceAccountName: ""
taskRef:
name: test
status:
podName: ""
由于我们这里的 Task 任务需要一个 git 代码仓库作为输入,所以需要一个 PipelineResource
对象来定义输入信息,上面的命令会自动创建一个名为 git-res
的 PipelineResource
资源对象,如下所示:
$ kubectl get pipelineresource
NAME AGE
git-res 22s
$ kubectl get pipelineresource git-res -o yaml
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
creationTimestamp: "2022-07-17T08:26:30Z"
generation: 1
name: git-res
namespace: default
resourceVersion: "1420356"
uid: ad12dd42-7426-467b-9eee-b52692b8fa31
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: git-res
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 0/2 Init:0/2 0 19s
$ kubectl describe pod testrun-pod
Name: testrun-pod
Namespace: default
# ......
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 4m12s default-scheduler Successfully assigned default/testrun-pod to node2
Normal Pulling 4m11s kubelet Pulling image "cnych/tekton-entrypoint:v0.37.2"
Normal Pulled 3m23s kubelet Successfully pulled image "cnych/tekton-entrypoint:v0.37.2" in 48.193573226s
Normal Created 3m23s kubelet Created container prepare
Normal Started 3m23s kubelet Started container prepare
Normal Pulling 3m22s kubelet Pulling image "cnych/tekton-workingdirinit:v0.37.2"
Normal Pulling 2m8s kubelet Pulling image "cnych/tekton-git-init:v0.37.2"
Normal Pulled 2m8s kubelet Successfully pulled image "cnych/tekton-workingdirinit:v0.37.2" in 1m13.783261878s
Normal Started 2m8s kubelet Started container working-dir-initializer
Normal Created 2m8s kubelet Created container working-dir-initializer
Normal Pulled 46s kubelet Successfully pulled image "cnych/tekton-git-init:v0.37.2" in 1m21.495056864s
Normal Created 46s kubelet Created container step-git-source-repo-k26dd
Normal Started 46s kubelet Started container step-git-source-repo-k26dd
Normal Pulling 46s kubelet Pulling image "golang:1.14-alpine"
Normal Pulled 3s kubelet Successfully pulled image "golang:1.14-alpine" in 42.621264017s
我们可以通过 kubectl describe
命令来查看任务运行的过程,首先会通过 tekton-git-init
拉取代码,然后会使用我们定义的 Task 任务中的 Steps 镜像来执行任务。当任务执行完成后, Pod 就会变成 Completed 状态了:
$ kubectl get podskubectl get pods
NAME READY STATUS RESTARTS AGE
testrun-pod 0/2 Completed 0 62s
$ kubectl get taskrunkubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
testrun True Succeeded 76s 68s
我们可以查看容器的日志信息来了解任务的执行结果信息:
$ kubectl logs testrun-pod --all-containers
{"level":"warn","ts":1658047190.915279,"caller":"git/git.go:273","msg":"URL(\"https://github.91chi.fun/https://github.com/cnych/tekton-demo.git\") appears to need SSH authentication but no SSH credentials have been provided"}
{"level":"info","ts":1658047194.4869733,"caller":"git/git.go:178","msg":"Successfully cloned https://github.91chi.fun/https://github.com/cnych/tekton-demo.git @ 5e1e3a1d0f167b9b639df5b802a0f0f81064d21e (grafted, HEAD, origin/master) in path /workspace/repo"}
{"level":"info","ts":1658047194.5030694,"caller":"git/git.go:217","msg":"Successfully initialized and updated submodules in path /workspace/repo"}
PASS
ok _/workspace/repo 0.002s
2022/07/17 08:39:48 Entrypoint initialization
我们可以看到我们的测试已经通过了。
在 Dashboard 中也看到对应的信息。
Docker Hub 配置¶
我们仍然使用之前的 Harbor 来作为镜像仓库,其登录凭证可以保存到 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
的文件,文件内容如下所示:
# 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 中定义的
env:
- name: DOCKER_HOST
value: tcp://docker-dind.kube-ops:2375
和前面的测试任务类似,这里我们同样将 git 作为输入资源,此外还定义了 pathToDockerfile
与 pathToContext
参数,用来指定 Dockerfile 和构建上下文的路径,此外还定义了一个名为 builtImage
的镜像输出资源,用来定义 Docker 镜像的相关参数。然后定义了一个名为 build-and-push
的步骤,由于我们的节点上使用的是 Containerd 这种容器运行时,所以之前将宿主机的 docker.sock 文件挂载到容器中来行不通了,之前我们在 kube-ops
章节中单独用 Pod 运行了一个 Docker Daemon 的服务,我们可以直接通过 DOCKER_HOST
环境变量来远程连接到该 Daemon 进行构建镜像。
$ kubectl get pods -n kube-ops -l app=docker-dind
NAME READY STATUS RESTARTS AGE
docker-dind-67594b47d9-bn849 1/1 Running 8 (7h5m ago) 6d19h
$ kubectl get svc -n kube-ops -l app=docker-dind
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
docker-dind ClusterIP 10.110.50.90 <none> 2375/TCP 14d
所以我们添加了一个 DOCKER_HOST
的环境变量,其值为 tcp://docker-dind.kube-ops:2375
,然后执行 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: git-res
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 0/4 Init:1/2 0 4s
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
build-and-push Unknown Pending 20s
现在任务执行的 Pod 还在初始化容器阶段,我们可以看到 TaskRun 的状态处于 Pending,隔一会儿正常构建就会成功了,我们可以查看构建任务的 Pod 日志信息:
$ tkn taskrun logs build-and-pushtkn taskrun logs build-and-push
[create-dir-builtimage-xm2zz] 2022/07/17 09:13:18 warning: unsuccessful cred copy: ".docker" from "/tekton/creds" to "/home/nonroot": unable to create destination directory: mkdir /home/nonroot: permission denied
[git-source-repo-xcf9q] {"level":"warn","ts":1658049269.6505384,"caller":"git/git.go:273","msg":"URL(\"https://github.91chi.fun/https://github.com/cnych/tekton-demo.git\") appears to need SSH authentication but no SSH credentials have been provided"}
[git-source-repo-xcf9q] {"level":"info","ts":1658049272.8632014,"caller":"git/git.go:178","msg":"Successfully cloned https://github.91chi.fun/https://github.com/cnych/tekton-demo.git @ 5e1e3a1d0f167b9b639df5b802a0f0f81064d21e (grafted, HEAD, origin/master) in path /workspace/repo"}
[git-source-repo-xcf9q] {"level":"info","ts":1658049272.8807545,"caller":"git/git.go:217","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 157.7kB
[build-and-push] Step 1/6 : FROM golang:1.14-alpine
[build-and-push] 1.14-alpine: Pulling from library/golang
[build-and-push] 4c0d98bf9879: Pulling fs layer
[build-and-push] 9e181322f1e7: Pulling fs layer
[build-and-push] 6422294da7d3: Pulling fs layer
[build-and-push] eb57f1833670: Pulling fs layer
[build-and-push] bd57f1a80d4e: Pulling fs layer
[build-and-push] eb57f1833670: Waiting
[build-and-push] bd57f1a80d4e: Waiting
[build-and-push] 6422294da7d3: Verifying Checksum
[build-and-push] 6422294da7d3: Download complete
[build-and-push] 9e181322f1e7: Verifying Checksum
[build-and-push] 9e181322f1e7: Download complete
[build-and-push] 4c0d98bf9879: Verifying Checksum
[build-and-push] 4c0d98bf9879: Download complete
[build-and-push] 4c0d98bf9879: Pull complete
[build-and-push] 9e181322f1e7: Pull complete
[build-and-push] 6422294da7d3: Pull complete
[build-and-push] bd57f1a80d4e: Verifying Checksum
[build-and-push] bd57f1a80d4e: Download complete
$ kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
build-and-push True Succeeded 5m14s 3s
我们可以看到 TaskRun 任务已经执行成功了。 这个时候其实我们可以在 Harbor 上找到我们的镜像了,当然也可以直接使用这个镜像进行测试。
同样 Dashboard 中也可以看到对应的执行任务信息。