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