Cloud Native 16 min read

Build Your First Tekton CI/CD Pipeline: A Step‑by‑Step Guide

This tutorial walks you through creating a complete Tekton pipeline—from installing the git‑clone task and defining unit‑test, build‑push, and deployment tasks, to assembling them into a Pipeline, configuring secrets, and running a PipelineRun that uses commit IDs as image tags—complete with all necessary YAML and command examples.

Ops Development Stories
Ops Development Stories
Ops Development Stories
Build Your First Tekton CI/CD Pipeline: A Step‑by‑Step Guide

In the previous article we installed Tekton and covered its theory; now we will practice by building our first pipeline.

The overall pipeline flow is simple and consists of four main tasks.

We use the

git-clone

task from Tekton Hub to pull the source code. It can be installed via

kubectl

or the

tkn

client.

<code>kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/git-clone/0.5/git-clone.yaml</code>
<code>tkn hub install task git-clone</code>

After installation, verify the task:

<code># tkn hub install task git-clone
Task git-clone(0.5) installed in default namespace
# kubectl get task | grep git-clone
git-clone        54s</code>

Test the task with a

TaskRun

using a public repository:

<code>apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
  name: test-git-clone
  namespace: default
spec:
  workspaces:
  - name: output
    emptyDir: {}
  params:
  - name: url
    value: "https://gitee.com/coolops/tekton-install.git"
  - name: revision
    value: "master"
  - name: gitInitImage
    value: "registry.cn-hangzhou.aliyuncs.com/coolops/tekton-git-init:v0.29"
  taskRef:
    name: git-clone</code>

Running it pulls the code successfully.

Unit Test

The unit‑test task simply runs

go test ./...

in a Go environment.

<code>go test ./...
ok      devops-hello-world      0.313s
ok      devops-hello-world/pkg  (cached)</code>
<code>apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: unit-test
spec:
  workspaces:
  - name: source
  steps:
  - name: unit-test
    workingDir: $(workspaces.source.path)
    image: golang:1.17.5
    env:
    - name: GOPROXY
      value: https://goproxy.cn
    command: ['go']
    args:
    - "test"
    - "./..."</code>

Build Image / Push

We use a multi‑stage build with Kaniko, so a single task handles both building and pushing the image.

<code>apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-push-image
spec:
  params:
  - name: pathToDockerfile
    description: The path to the Dockerfile to build (relative to the context)
    default: Dockerfile
  - name: imageUrl
    description: URL of the image repository
  - name: imageTag
    description: Tag to apply to the built image
    default: latest
  workspaces:
  - name: source
  - name: dockerconfig
    mountPath: /kaniko/.docker
  steps:
  - name: build-and-push
    image: registry.cn-hangzhou.aliyuncs.com/coolops/kaniko-executor:v1.5.0
    workingDir: $(workspaces.source.path)
    command: ['/kaniko/executor']
    args:
    - --dockerfile=$(params.pathToDockerfile)
    - --destination=$(params.imageUrl):$(params.imageTag)
    - --context=$(workspaces.source.path)</code>

Create a Docker config secret for Kaniko:

<code>kubectl create secret docker-registry dockerhub \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username=[USERNAME] \
  --docker-password=[PASSWORD] \
  --dry-run=client -o json | jq -r '.data.".dockerconfigjson"' | base64 -d > /tmp/config.json && \
  kubectl create secret generic docker-config --from-file=/tmp/config.json && \
  rm -f /tmp/config.json</code>

If

jq

is missing, install it:

<code>yum install jq -y</code>

Deploy Application

Deploy the application using a Kubernetes

Deployment

via

kubectl

. Mount the kubeconfig as a secret.

<code>kubectl create secret generic kubernetes-config --from-file=/root/.kube/config</code>
<code>apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: deploy-to-k8s
spec:
  workspaces:
  - name: source
  - name: kubernetesconfig
    mountPath: /root/.kube
  params:
  - name: pathToYamlFile
    description: The path to the yaml file to deploy within the git source
    default: deployment.yaml
  - name: IMAGE
  - name: TAG
  steps:
  - name: run-kubectl
    image: registry.cn-hangzhou.aliyuncs.com/coolops/kubectl:1.19.16
    workingDir: $(workspaces.source.path)
    script: |
      sed -i s#IMAGE#$(params.IMAGE)#g $(params.pathToYamlFile)
      sed -i s#TAG#$(params.TAG)#g $(params.pathToYamlFile)
      kubectl apply -f $(params.pathToYamlFile)</code>

Combine into a Pipeline

All tasks are assembled into a

Pipeline

with required workspaces and parameters.

<code>apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: devops-hello-world-pipeline
spec:
  workspaces:
  - name: go-repo-pvc
  - name: docker-config
  - name: kubernetes-config
  params:
  - name: git_url
  - name: revision
    type: string
    default: "master"
  - name: gitInitImage
    type: string
    default: "registry.cn-hangzhou.aliyuncs.com/coolops/tekton-git-init:v0.29"
  - name: pathToDockerfile
    description: The path to the build context used by Kaniko
    default: .
  - name: imageUrl
    description: Url of image repository
  - name: imageTag
    description: Tag to apply to the built image
    default: latest
  tasks:
  - name: clone
    taskRef:
      name: git-clone
    workspaces:
    - name: output
      workspace: go-repo-pvc
    params:
    - name: url
      value: $(params.git_url)
    - name: revision
      value: $(params.revision)
    - name: gitInitImage
      value: $(params.gitInitImage)
  - name: unit-test
    taskRef:
      name: unit-test
    workspaces:
    - name: source
      workspace: go-repo-pvc
    runAfter:
    - clone
  - name: build-push-image
    taskRef:
      name: build-push-image
    params:
    - name: pathToDockerfile
      value: $(params.pathToDockerfile)
    - name: imageUrl
      value: $(params.imageUrl)
    - name: imageTag
      value: $(params.imageTag)
    workspaces:
    - name: source
      workspace: go-repo-pvc
    - name: dockerconfig
      workspace: docker-config
    runAfter:
    - unit-test
  - name: deploy-to-k8s
    taskRef:
      name: deploy-to-k8s
    params:
    - name: pathToYamlFile
      value: deployment.yaml
    - name: IMAGE
      value: $(params.imageUrl)
    - name: TAG
      value: $(params.imageTag)
    workspaces:
    - name: source
      workspace: go-repo-pvc
    - name: kubernetesconfig
      workspace: kubernetes-config
    runAfter:
    - build-push-image</code>

Run Tests

Create the necessary secrets, service account, and role binding before running the pipeline.

<code>apiVersion: v1
kind: Secret
metadata:
  name: gitlab-auth
  annotations:
    tekton.dev/git-0: https://gitee.com/
type: kubernetes.io/basic-auth
stringData:
  username: xxxx
  password: xxxx
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tekton-build-sa
secrets:
- name: gitlab-auth
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: tekton-clusterrole-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: edit
subjects:
- kind: ServiceAccount
  name: tekton-build-sa
  namespace: default</code>

Create a

PipelineRun

with parameters and workspaces:

<code>apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: devops-hello-world-pipeline-run
spec:
  pipelineRef:
    name: devops-hello-world-pipeline
  params:
  - name: revision
    value: master
  - name: git_url
    value: https://gitee.com/coolops/devops-hello-world.git
  - name: imageUrl
    value: registry.cn-hangzhou.aliyuncs.com/coolops/devops-hello-world
  - name: imageTag
    value: latest
  - name: pathToDockerfile
    value: Dockerfile
  workspaces:
  - name: go-repo-pvc
    volumeClaimTemplate:
      spec:
        accessModes: [ReadWriteOnce]
        storageClassName: openebs-hostpath
        resources:
          requests:
            storage: 1Gi
  - name: docker-config
    secret:
      secretName: docker-config
  - name: kubernetes-config
    secret:
      secretName: kubernetes-config
  serviceAccountName: tekton-build-sa</code>

The pipeline runs successfully in the Tekton Dashboard, and the application starts correctly.

To make the image tag dynamic, we use the commit ID output from the

git-clone

task.

<code>apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: devops-hello-world-pipeline
spec:
  ...
  tasks:
  - name: build-push-image
    params:
    - name: imageTag
      value: $(tasks.clone.results.commit)
    ...
  - name: deploy-to-k8s
    params:
    - name: TAG
      value: $(tasks.clone.results.commit)
    ...</code>

Running the updated pipeline results in images tagged with the latest commit ID.

The final commit ID can be viewed as shown.

Conclusion

The pipeline is straightforward, but debugging can be time‑consuming due to parameter passing; careful attention to task inputs and outputs is essential.

All code and YAML manifests are available at https://gitee.com/coolops/devops-hello-world for further exploration.

CI/CDkubernetesDevOpsPipelineTektonkaniko
Ops Development Stories
Written by

Ops Development Stories

Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.