Containerization has transformed the landscape of software development and deployment, with Docker being a popular choice for packaging applications into isolated containers. In Kubernetes environments, efficiently building and pushing Docker images is crucial for maintaining a streamlined development and deployment pipeline. In this blog post, I’ll explore how to achieve this using Tekton which is an open-source framework designed for building CI/CD systems that run on Kubernetes and Kaniko which is an offers a solution by enabling containerized builds without the need for a Docker daemon. Let’s get started.

Setup

First we need to pull the latest changes from the git repository. For this we can use the git-clone task but imagine if we need to pull the source code from a private Git repository. In that case we need to add some custom configurations to our tekton pipeline. In here I am using GitHub repository and I am going to use a SSH key to authenticate with the GitHub repository.

Generate SSH Key

First, we need to generate a SSH key pair. We can do that by using the following command.

1
ssh-keygen -t rsa -b 4096 -C "[email protected]"

This will ask some questions like passphrase and location to save key pair. You can leave them as default by pressing enter. After that, you will get a public key and a private key in your default location. In my case, it is in the ~/.ssh directory. The public key is id_rsa.pub and the private key is id_rsa.

Encode SSH Key

Before create a secret in Kubernetes, we need to encode the private key. We can do that by using the following command.

1
cat ~/.ssh/id_rsa | base64

This will give you a base64 encoded string. Copy that string and save it in a file. We will use that in the next step.

Create Secret

Now we need to create a secret in Kubernetes. We are going to use following YAML file to create the secret.

1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Secret
metadata:
name: git-ssh-key
annotations:
tekton.dev/git-0: github.com
type: kubernetes.io/ssh-auth
data:
ssh-privatekey: <base64 encoded private key> ### Base64 encoded private key

Now we need to apply this YAML file to create the secret.

1
kubectl create -f secret.yaml

Now we have created the secret. Let’s move on to the next step.

Add Public key to GitHub

Now we need to add the public key to GitHub. You need to copy the public key by below command.

1
cat ~/.ssh/id_rsa.pub

Now goto GitHub and add the public key to your GitHub account. You can do that by following below steps.

  1. Go to your GitHub account settings.
  2. Click on SSH and GPG keys.
  3. Click on New SSH key.
  4. Give a name and paste the public key.
  5. Click on Add SSH key.

Now we have added the public key to GitHub. Let’s move on to the next step.

Create Service Account

Now we need to create a service account to use in the pipeline. We can do that by using the following YAML file.

1
2
3
4
5
6
apiVersion: v1
kind: ServiceAccount
metadata:
name: git-service-account
secrets:
- name: git-ssh-key ### Secret name we created in previous step

Now we need to apply this YAML file to create the service account.

1
kubectl create -f service-account.yaml

Now we have finished creating the service account. Now you can use this service account in your pipelineRun to clone the private Git repository.

Create Git Clone Task

Now we need to create a task to clone our sorce code. For this we can use the git-clone task in Tekton Hub. You can find the task in here or you can use the following YAML file to create the task in your cluster.

Create Pipeline

Now we need to create a pipeline with git-clone task to clone repo. We can do that by using the following YAML file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: clone-build-push
spec:
params:
- name: repo-url
type: string
workspaces:
- name: shared-data
tasks:
- name: fetch-source
taskRef:
name: git-clone
workspaces:
- name: output
workspace: shared-data
params:
- name: url
value: $(params.repo-url)
- name: deleteExisting
value: "true"

Now we need to apply this YAML file to create the pipeline.

1
kubectl create -f pipeline.yaml

Now we have created the pipeline. Let’s move on to the next step. Now we need to create a pipelineRun to run the pipeline we created in the previous step. We can do that by using the following YAML file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: clone-build-push-run-
spec:
pipelineRef:
name: clone-build-push
podTemplate:
securityContext:
fsGroup: 65532
workspaces:
- name: shared-data
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
params:
- name: repo-url
value: [email protected]:dinushchathurya/tekton-triggers.git
serviceAccountName: git-service-account

Now we need to apply this YAML file to create the pipelineRun.

1
kubectl create -f pipeline-run.yaml

Now we have created the pipelineRun and now you can see it is running on Tekton dashboard. Now we have finished with the git clone task. Let’s move on to the next step.

Show files in workspace

Now we need to check whether the files are cloned to the workspace. We can do that by using a custom task. We can use the following YAML file to create the task.

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: list-source
spec:
workspaces:
- name: source
steps:
- name: list-source
image: ubuntu
workingDir: $(workspaces.source.path)
script: >
ls

Now we need to apply this YAML file to create the task.

1
kubectl create -f list-source.yaml

Now we need to update the pipeline we created in the previous step to run this task. We can do that by using the following YAML file.

1
2
3
4
5
6
7
8
9
10

tasks:
...
- name: list-source
runAfter: ["fetch-source"]
taskRef:
name: list-source
workspaces:
- name: source
workspace: shared-data

Create Docker Credentials

Now we need to create a secret to store Docker credentials. We can do that by using the following YAML file.

1
2
3
4
5
6
apiVersion: v1
kind: Secret
metadata:
name: docker-credential
data:
config.json: <docker-config-json>

Then you need to apply this YAML file to create the secret.

1
kubectl create -f docker-credential.yaml

Build and Push Docker Image

Now we need to build and push the Docker image. For this we can use the kaniko task in Tekton Hub. You can find the task in here or you can use the following YAML file to create the task in your cluster.
Now we need to update the pipeline by adding image-reference & image-tag to the params section in the pipeline.

1
2
3
4
5
params: 
- name: image-reference
type: string
- name: image-tag
type: string

And we need to add workspaces to the pipeline level.

1
2
workspaces:
- name: docker-credentials

Now we need to update the pipeline by adding build-push task to the pipeline. We can do that by using the following YAML file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
tasks:
...
- name: build-push
runAfter: ["list-source"]
taskRef:
name: kaniko
workspaces:
- name: source
workspace: shared-data
- name: dockerconfig
workspace: docker-credentials
params:
- name: IMAGE
value: $(params.image-reference):$(params.image-tag)

After you updated pipeline with this task, final pipeline will look like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: clone-build-push
spec:
params:
- name: repo-url
type: string
- name: image-reference
type: string
- name: image-tag
type: string
workspaces:
- name: shared-data
- name: docker-credentials
tasks:
- name: fetch-source
taskRef:
name: git-clone
workspaces:
- name: output
workspace: shared-data
params:
- name: url
value: $(params.repo-url)
- name: deleteExisting
value: "true"
- name: list-source
runAfter: ["fetch-source"]
taskRef:
name: list-source
workspaces:
- name: source
workspace: shared-data
- name: build-push
runAfter: ["list-source"]
taskRef:
name: kaniko
workspaces:
- name: source
workspace: shared-data
- name: dockerconfig
workspace: docker-credentials
params:
- name: IMAGE
value: $(params.image-reference):$(params.image-tag)

Now we need to apply this YAML file to update the pipeline.

1
kubectl apply -f pipeline.yaml

Now we need to update the pipelineRun to run the pipeline we created in the previous step. This is the final pipelineRun we are going to use to build and push the Docker image.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: clone-build-push-run-
spec:
params:
- name: repo-url
value: [email protected]:dinushchathurya/tekton-triggers.git
- name: image-reference
value: dinushchathurya/tekton-nodejs
- name: image-tag
value: v1.0.0
pipelineRef:
name: clone-build-push
podTemplate:
securityContext:
fsGroup: 65532
workspaces:
- name: shared-data
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
- name: docker-credentials
secret:
secretName: docker-credential
serviceAccountName: git-service-account

Now we need to apply this YAML file to create the pipelineRun.

1
kubectl create -f pipeline-run.yaml

Now you can see the pipelineRun is running on Tekton dashboard. Now we have finished building and pushing the Docker image. Let’s check whether the image is pushed to Docker Hub. You can see the image is pushed to Docker Hub. Now we have finished building and pushing the Docker image.

Conclusion

In this article, we learned how to build and push Docker images using Tekton and Kaniko. You can find the all the related scripts and code for this tutorial from here. If you have any issue regarding this tutorial, mention your issue in the comment section or reach me through my E-mail.

Happy Coding