Building in Kubernetes Using Tekton

31 minute read     Updated:

Joseph Eshiett %
Joseph Eshiett

This article explores how Earthly enhances Tekton CI/CD workflows with reproducible builds. Earthly ensures consistent results for those using Tekton pipelines. Check it out.

Continuous integration/continuous delivery (CI/CD) principles offer multiple benefits to software organizations, including faster time to market, higher-quality code, and simpler and faster fault isolation. Applications built using CI/CD pipeline best practices tend to see a huge increase in users over time, necessitating a migration from a large codebase and low-scalability monolithic architecture to a more manageable and efficient microservice architecture.

Kubernetes is one of the most popular platforms for automating the management, deployment, and scaling processes of microservice applications. Because Kubernetes is complex, though, a framework can help developers and operations teams use the platform to follow CI/CD practices in building applications. This is where Tekton comes in.

Tekton is an open source framework that’s designed to help you optimize your CI/CD practices. With it, you can build customizable and reusable CI/CD pipelines as well as orchestrate workflows across on-premise systems and multiple cloud providers.

In this tutorial, you’ll learn how to create a customizable CI/CD workflow with Tekton to deploy a sample application to your Kubernetes cluster.

What Is Tekton?

Tekton is a Kubernetes-native CI/CD framework used by engineers and developers to construct workflows that build, test, and deploy code to a cloud or on-premise Kubernetes cluster.

Tekton consists of five main concepts:

  • Steps: A step is the smallest unit for building workflows with Tekton. Block elements such as arguments, images, and commands are defined in steps.
  • Tasks: A task element is a combination of steps that are expressed in sequential order.
  • Pipelines: A pipeline is a combination of tasks ordered sequentially. It can be set up to run concurrently depending on the use case. The inputs, outputs, and workflow parameters can be specified as well.
  • TaskRuns: As the name implies, a TaskRun element instantiates specific tasks. It also specifies details required for a task to run a Git repository as well as container registry information.
  • PipelineRuns: A PipelineRun is the final element in the hierarchy. Similar to TaskRun, it instantiates specific pipeline elements. It also specifies the desired runtime information such as a Git repository and container registry.

All of these Tekton elements are configured as custom resource definitions (CRDs) on Kubernetes. These CRDs are customizable and reusable, thereby extending your Kubernetes capabilities. You can use these CRDs to orchestrate workflows such as checking out code from a Git repository, linting the codebase, checking for vulnerabilities in the container images, deploying to a container registry with the appropriate tags, and updating the actual state of the cluster with changes from the new state configurations. This build workflow can be performed across multiple providers and on-premise systems.

To learn more about Tekton, check out the official documentation.

Building an Application Using Tekton

Application Building

For this example, you’re going to package a Node.js application using Docker, push it to Docker Hub, then automatically deploy it to Kubernetes using Tekton. For a sample Node.js application, check the GitHub repository for this tutorial.

Prerequisites

For this tutorial, you’ll need the following.

A Kubernetes Cluster

You’ll need a cluster to run Tekton and deploy the sample Node.js application. This tutorial uses the lightweight Kubernetes distribution MicroK8s version 1.23.5, installed on Ubuntu 20.04 via Snap package manager. This cluster must have role-based access control (RBAC) enabled and ClusterRole and ClusterRoleBinding definitions created for the cluster-admin user so that you can create any resource and freely interact with your cluster.

Note that if you’re using MicroK8s, you’ll have to configure it to export its kubectl configuration to $HOME/.kube/config with this command:

microk8s.kubectl config view --raw > $HOME/.kube/config

This Kubernetes cluster must have MetalLB enabled so the load balancer service can attach an IP address to the Node.js deployment. This makes it accessible outside the cluster.

This cluster must also have a storage class enabled so that PersistentVolumeClaim definitions can be created and used by the Tekton pipeline.

Kubectl

You need the kubectl command line utility to be able to interact with your Kubernetes cluster. This tutorial uses kubectl version 1.23.

A Docker Hub Account

You need access to a public registry on Docker Hub to deploy your built image to. You can create an account if you haven’t already.

Installing Tekton Pipelines

Tekton Pipelines is a Kubernetes extension and the core of the Tekton project. With Tekton Pipelines installed in your cluster, you can define the Kubernetes custom resources needed to build your CI/CD pipeline.

Run the following command to install:

kubectl apply --filename \
https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
Applying Tekton Pipelines resources to the cluster

Verify the Tekton controller and webhook pods are up and running:

kubectl get po -n tekton-pipelines
Tekton controller and webhook deployed

Installing the Tekton CLI (Tkn)

Although you can still interact with Tekton using kubectl, for this tutorial you’ll use the Tekton CLI (tkn) to install tasks from the Tekton Catalog, which contains reusable Tekton tasks that you’ll be using.

To install tkn on your operating system, follow the instructions. This tutorial uses the Ubuntu 20.04 operating system with tkn version 0.23.1.

Installing Tasks From Tekton Hub

You don’t need to create your tasks from scratch. There are plenty of Tekton resources, such as tasks and pipelines, that are available at the Tekton Hub and frequently updated by contributors.

To build and deploy the sample Node.js application, you need to install the following tasks from the Tekton Hub.

Install git-clone to clone the Node.js project Git repository:

tkn hub install task git-clone --version 0.6

Next, install buildah to build and push the image to Docker Hub:

tkn hub install task buildah --version 0.3

Lastly, install kubernetes-actions to apply the Kubernetes deployment manifest to the cluster:

tkn hub install task kubernetes-actions --version 0.2

To confirm the tasks have been installed successfully, list them:

tkn task list
Listing installed tasks

Creating the Secret and ServiceAccount Manifest

You’re going to create the secrets that the Buildah runtime will use to push the image to your public Docker registry on Docker Hub.

Create a file called secret-sa.yml in the tekton directory and add the following code snippet, replacing DOCKERHUB_USER and DOCKERHUB_PASSWORD with your Docker Hub credentials:

apiVersion: v1
kind: Secret
metadata:
  name: docker-secret
  annotations:
    tekton.dev/docker-0: https://index.docker.io/
type: kubernetes.io/basic-auth
stringData:
  username: DOCKERHUB_USER
  password: DOCKERHUB_PASSWORD
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: docker-login
secrets:
  - name: docker-secret

Apply the Secret and ServiceAccount configuration to your cluster:

kubectl apply --filename secret-sa.yml
Secret and ServiceAccount created

Creating ClusterRole and ClusterRoleBinding

To apply the Tekton resources successfully, you need to set a Kubernetes Role and RoleBinding to the docker-login service account. This service account is what Tekton will use to deploy to the cluster.

Create a file named role.yml and add the following code snippet:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: nodejs-pipeline-role
rules:
- apiGroups: ["extensions", "apps", ""]
  resources: ["services", "deployments", "pods","pvc","job"]
  verbs: ["get", "create", "update", "patch", "list", "delete"]
---

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: nodejs-pipeline-role-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: nodejs-pipeline-role
subjects:
- kind: ServiceAccount
  name: docker-login

Apply the role.yml configuration to your cluster:

kubectl apply --filename role.yml
Role and RoleBinding applied to cluster

Creating a Pipeline

Now you’re going to create a pipeline resource to build and push the Node.js application to Docker Hub, then deploy to your Kubernetes cluster. This pipeline should reference the installed tasks.

Clone the GitHub project repository containing server.js, package.json, Dockerfile, and the Kubernetes deployment manifest, deploy.yml:

git clone https://github.com/joeshiett/tekton-test.git
GitHub project directory

Navigate into the GitHub project directory, tekton-test, and create a directory named tekton-practice to store your Tekton pipeline resources. Create a file named pipeline.yml in the tekton-practice directory and add the following code snippet:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: nodejs-pipeline
spec:
  params:
    - name: IMAGE
      description: Image description
      type: string
      default: "docker.io/joeshiett/nodejs-app"
    - name: TAG
      description: Preferred tag
      default: latest
  workspaces:
    - name: linked-workspace
  tasks:
    - name: fetch-repository
      taskRef:
        name: git-clone
      workspaces:
        - name: output
          workspace: linked-workspace
      params:
        - name: url
          value: https://github.com/joeshiett/tekton-test
        - name: subdirectory
          value: ""
        - name: deleteExisting
          value: "true"
    - name: build-push-image
      taskRef:
        name: buildah
      runAfter:
        - fetch-repository
      workspaces:
        - name: source
          workspace: linked-workspace
      params:
        - name: IMAGE
          value: "$(params.IMAGE):$(params.TAG)"
        - name: CONTEXT
          value: "src"
        - name: FORMAT
          value: "docker"
    - name: create-deployment
      taskRef:
        name: kubernetes-actions
      runAfter:
        - build-push-image
      params:
        - name: script
          value: |
            kubectl apply --filename https://raw.githubusercontent.com/Joeshiett/tekton-test/main/manifest/deploy.yml
      workspaces:
        - name: manifest-dir
          workspace: linked-workspace

Replace the IMAGE default params above with your Docker Hub repository name. This tutorial uses docker.io/joeshiett/nodejs-app.

Next, apply the pipeline.yml configuration to the cluster:

kubectl apply --filename pipeline.yml
Pipeline configuration created

Confirm that the Tekton pipeline you created has been added to the cluster:

tkn pipeline list
Pipeline successfully added to cluster

Editing the Deploy.yml Manifest

To deploy the built Docker image to your own public repository on Docker Hub, edit the deploy.yml file in the manifest directory. Replace DOCKERHUB_USER with your Docker Hub username and APP_NAME with the name of your built Docker image:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs-deployment
spec:
 replicas: 2
 selector:
   matchLabels:
     app: nodejs-app
 template:
   metadata:
     labels:
       app: nodejs-app
   spec:                  
     containers:
     - name: nodejs-app
       image: <DOCKERHUB_USER>/<APP_NAME>:latest
       ports:
       - containerPort: 8081
---
apiVersion: v1
kind: Service
metadata:
  name: nodejs-service
spec:
  type: LoadBalancer
  selector:
    app: nodejs-app
  ports:
    - port: 80
      targetPort: 8081
      nodePort: 30001

Creating PipelineRun

Your pipeline has been successfully added to the cluster, but at the moment it’s not running. To instantiate it, you need to create a PipelineRun configuration.

Create a file named pipeline-run.yml and add the following code snippet:

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  generateName: nodejs-pipelinerun-
spec:
  serviceAccountName: docker-login
  pipelineRef:
    name: nodejs-pipeline
  params:
    - name: IMAGE
      value: joeshiett/nodejs-app
    - name: TAG
      value: latest
  workspaces:
    - name: linked-workspace
      volumeClaimTemplate:
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 10Gi

Kickstart the deployment with the following command:

kubectl apply --filename pipeline-run.yml
Instantiated pipeline

Your pipeline should be up and running. To see the pipeline logs, run the following command:

tkn pipeline logs -f

You should see the pipeline-run.yml configuration applied and started:

nodejs-pipelinerun-jxm91 Pipeline logs

Tekton automatically creates pods in the default namespace that run different containers for the pipeline. To see these pods and the Node.js application deployment, run the following command:

kubectl get pods

As shown by the output below, nodejs-pipelinerun- ran successfully and also created two nodejs-deployment- pods running the Node.js application.

Node.js application successfully deployed

Testing Node.js Deployment

Your Node.js application should be up and running in your cluster.

To confirm your deployment, retrieve your load balancer IP address, store it in a variable LB_IP, and access it via the curl command:

export LB_IP=$(kubectl get svc/nodejs-service -o=jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl ${LB_IP} -w "\n"

The curl command should output the following:

Node.js deployment up and running

Conclusion

In this tutorial, you’ve learned how to use Tekton to streamline application building in Kubernetes. We covered setting up a basic Tekton workflow and deploying an application via Tekton CI/CD pipeline. Tekton not only speeds up your deployment process, but also enhances flexibility and scalability in your Kubernetes projects.

Looking to further fine-tune your app building process? Give Earthly a try for an even smoother development process. This open-source build automation tool can complement your Tekton workflows, offering reproducible, portable, and parallel builds for modern applications.

Earthly Cloud: Consistent, Fast Builds, Any CI
Consistent, repeatable builds across all environments. Advanced caching for faster builds. Easy integration with any CI. 6,000 build minutes per month included.

Get Started Free

Joseph Eshiett %
Joseph Eshiett
Joseph is a DevOps practitioner who is fascinated by cloud technologies, software development, and deployment processes. He is also a technical writer who enjoys documenting the processes and learning new things.

Updated:

Published:

Get notified about new articles!
We won't send you spam. Unsubscribe at any time.