How to Setup a Tekton CI/CD Environment
By: Steele Ray Desmond
Tags: Guides And Tutorials, Kubernetes, Openshift, Ci Cd Pipeline, Tekton

A complete step-by-step guide to create a Tekton (v0.19.0) CI/CD solution on OpenShift 4 or Kubernetes 1.16+.

Overview

The goal of this guide is to demonstrate how to install, configure, and use Tekton to automate an application’s CI/CD lifecycle by providing an in-depth, walkthrough example. Tekton is a kubernetes-based pipeline tool, currently in beta, that provides a set of CI/CD building blocks. By the end of this guide, you will have a complete Tekton-based CI/CD environment that can hopefully be modularized and expanded to solve your development’s CI/CD needs.

The pipeline yaml files provided are used to build and deploy Java applications packaged with Maven but the walkthrough can be used as a starting point to build your own custom pipelines. You can swap out the specific pipeline and task files to suit your specific runtimes or build steps (See Tekton Hub for a catalog of premade resources). This pipeline demonstrates the use of shared Tekton Workspaces as well as Task Results, features released in Tekton beta.

Automating Application Deployment

Let’s first walk through the build and deployment steps needed for an example application so we can later translate the process to Tekton for automation.

I have generated an example Java application here. This app was generated with Quarkus and it comes with a maven pom.xml file and a Dockerfile under the path src/main/docker/Dockerfie.jvm . I modified the pom.xml file to require this second repository as a build dependency.

To build this application ourselves, we need to perform the following steps:

Clone the dependency and install into our local workspace with maven Clone the application’s source code repository Package the application source code into a jar file with maven Containerize the application jar using Docker/Buildah and its Dockerfile Push the image to an image registry Create the application’s latest manifest files Update the deployment manifest file to use the latest image

By the end of this guide, we will automate this process with a Pipeline. Instead, we will have two steps to deploy our code:

Commit our code Create a PipelineRun

The pipeline will work for any applications that have the same build and deployment process. With some additional work, this solution can also be expanded to automatically trigger the pipeline on pull requests, or to build specific branches for specific environments.

Prerequisites

Kubernetes-based cluster using v1.16+, such as OpenShift 4+, with admin access

If you don’t have a cluster yet, you can follow along with a sandbox kube cluster using kind, or minikube. If you need a production cluster, you can make a cloud-managed OpenShift cluster on your favorite cloud provider, such as IBM Cloud, AWS, Azure, or GCP. OpenShift is great with Tekton because it provides integrated UI support to visualize and assemble your pipelines, though there are other UIs currently in development for Tekton that you can install as well.

Feel free to follow along with this example project that includes all the resources created in this guide. With that, let’s get started.

Step 1. Install Tekton

Installing Tekton is as simple as logging in to your cluster and running 1–2 commands depending on your platform. You do not need the CLI tool to use Tekton but it can be useful to interact with your pipelines. I recommend following the official steps below.

Easy OpenShift TektonCD Install Example

You can also install Tekton using the Operator. As of this writing, it uses an older beta version (0.15.2 vs. 0.19.0) but it comes with a Tekton Dashboard and it might maintain some of pipeline artifacts for you in the future.

Step 2. Configure Tekton

With Tekton installed, we will need to configure it to work with our cluster. These steps can still vary depending on your Tekton version and cluster versions. In this section we will:

Create a ServiceAccount for our pipeline called pipeline Give our SA access to run pods as privileged Assign our SA to be used as the default SA for Tekton Grant our SA access to modify manifest files in our project’s namespace Create credentials to access our external image registry and assign them our SA Create credentials to access our git repository and assign them our SA

Note: In some versions of Tekton, a ServiceAccount may be partially configured for you automatically.

Create and Assign a ServiceAccount

To use the Buildah Task in this Pipeline (comparable to Docker) you will need to grant privileged access to the Pod executing the Task and creating containers. By default, Tekton uses the cluster’s default ServiceAccount.

We will grant access by creating our own pipeline ServiceAccount, assigning this account Privileged access, and setting it as our new default ServiceAccount. We will also set it explicitly as an argument in our PipelineRun.

Later, you will see this as a part of the steps in the build-maven-image Task where it requires this access:

securityContext:

privileged: true

To create this SA and apply the privileged SecurityContextConstraint with OpenShift, run the following:

oc create sa pipeline

oc adm policy add-scc-to-user privileged -z pipeline

Change our default SA with Tekton to use the pipeline SA:

kubectl create configmap config-defaults \

--from-literal=default-service-account=pipeline \

-o yaml -n tekton-pipelines \

--dry-run=client | kubectl replace -f -

Next, we will need to give the SA access to resources in its namespace by creating a ClusterRoleBinding. This is needed in our example to reapply the Image tag in the update-deployment Task. In this case, I will give the pipeline SA the cluster-admin role, though you may decide to create your own ClusterRole and scope the pipeline's access further. The SA, pipeline artifacts, and application artifacts will be deployed in the java-maven-demo namespace.

oc create clusterrolebinding pipeline --clusterrole=cluster-admin --serviceaccount=java-maven-demo:pipeline

Accessing the External Registry

We need to grant our pipeline access to our external image registry. To do this, create a secret and link it to the ServiceAccount created before. I’m using a docker registry at docker.io with an access token via Account Settings.

Here is an example command to create your Secret:

oc create secret docker-registry srd-docker-registry \

— docker-server=docker.io \

— docker-username=<username> \

— docker-password=<api_token> \

— docker-email=<email>

Once created, link the secret to your pipeline ServiceAccount like so:

oc secrets link pipeline srd-docker-registry

If you need to pull dependencies from an image registry in your pipeline, you will need to grant it this permission in addition to the previous command.

oc secrets link pipeline srd-docker-registry --for=pull

Accessing the Git Repositories

Similar to the external registry, we will create our git secret and link it to the ServiceAccount. Here is a yaml file example with an annotation used by Tekton.

You can save and modify this yaml file then create it with:

oc create -f gitsecret.yaml

Then link it to our pipeline SA:

oc secrets link pipeline gitsecret

Create Pipeline Storage

Lastly, our Pipeline will need a place to clone source code and share cached build artifacts. We will create a 5Gi PersistentVolumeClaim, which will later be given as a parameter in the PipelineRun to use when executing the Pipeline.

In the end, this will be mounted to the Pipeline via a workspace, and given as an argument in the PipelineRun for our application, like this:

workspaces:

- name: source

persistentVolumeClaim:

claimName: pipeline-pvc

—

Alternatively, instead of caching with the same PVC, we may find it preferable to generate a new PVC each time we run the pipeline. If you would like to take this approach (which will likely be slower), you will need to define this like so in your PipelineRun at the end of the guide:

workspaces:

- name: source

volumeClaimTemplate:

spec:

resources:

requests:

storage: 5Gi

volumeMode: Filesystem

accessModes:

- ReadWriteOnce

That is all for Tekton configuration. For more information on authentication with Tekton check out this helpful documentation.

Step 3. Setup Tekton Artifacts

It is recommended to use version control for your artifacts as a backup and for easy reference, modification or collaboration. Depending on the size of the project, some users decide to store their tekton artifacts alongside other kubernetes-related yaml files. I will create a stand-alone repository on GitHub to store tekton-related artifacts called common-tekton-artifacts .

The repository contains folders for:

resources/

tasks/

pipelines/

As our project grows and our CI/CD needs expand, we can use this layout to easily manage our pipelines and resources. Next, we will create our Pipeline’s Tasks.

Step 4. Create Tasks

A task contains a series of steps, where each step creates a container using an image provided and run commands or scripts specified in that step. Tekton creates Pods to execute Tasks, similar to many other cloud-native tools.

We will create Tasks to complete each manual step of the build and deployment process, and then we will link them together with a Pipeline. Our Tasks will be able to share resources with each other by using a shared Workspace, which is a Tekton property representing a PersistentVolumeClaim. We will create Tasks that can be modularized for use with other Pipelines.

Task git-clone: Clone the Source Code repository

The first step in our CI/CD solution is to clone the application’s source code and its dependency’s source code into a workspace. Here is a standalone task from the Tekton Catalog we can use:

The only required parameter for the git-clone Task is the url to our source code, i.e. https://github.com/tekton-example/java-app-example.git

We can specify additional parameters such as the branch to clone or a proxy to use. In our case, we will be using the subdirectory parameter to clone the source code into a folder in the workspace rather than at the root of the directory. We will also use the commit result later to tag our image. We can use this Task twice in our pipeline to clone our dependency and application repositories.

Task maven-build: Install Dependencies

The following Task uses an image with maven to run mvn commands by first generating or using an existing settings.xml file in the workspace to use and then executing in the source workspace.

The task will run mvn package by default and it can be passed a GOALS parameter to specify other arguments. We will use this Task to run mvn install on our dependency repository. While we could use this Task to package our application as well, but we will instead include this step in the next Task for our application source code since we will have similar steps to containerize and push the application to an image registry.

Task build-maven-image: Build, Containerize, and Push the Application Image

This Task will use the application source code to package, containerize, and push our application image to an external registry. If we were doing these steps manually using my own docker registry, it would look like this:

mvn package docker build -f src/main/docker/Dockerfile.jvm -t docker.io/sdesmond6/java-app-example:latest . docker push docker.io/sdesmond6/java-app-example:latest

The Task uses the buildah image to build the source code from the Dockerfile and push it to the registry image name provided. This Task will be ran with the ServiceAccount created previously that has the authorization to run buildah and push to the registry.

Task apply-manifests: Apply Kubernetes Manifest Files

For this example, we follow a common practice to store our application’s manifest files alongside our source code under a folder called k8s/ . These define how we expect the application to run inside our cluster. In this example application, this includes a Deployment, Service, and Route as it will be deployed on OpenShift. You can create your own manifest files for your application and store them in a k8s/ folder.

This Task uses oc CLI with the image origin-cli which also contains kubectl . Additionally, it runs an apply command, meaning that the manifests must be created already in the cluster before the first execution of the Task, i.e. kubectl create -f k8s/ .

Task update-deployment: Update Deployment Manifest to Use Latest Image

The last step in this Pipeline example will be to patch our Deployment file with the latest image. This step can be handled in a number of ways. We will be using the sha commit result of our git-clone Task to tag the image with our latest git commit sha string. This image name is passed to the previous build-maven-image task in the pipeline and then given to this task as a param to patch.

The patch will update the Deployment and trigger the application to redeploy using the image.

Task Recap

With our task files finished, we can add them all to our cluster: oc create -f tasks/ .

At this point, you can test each Task directly by creating TaskRun files, or by using the Tekton CLI to generate them, i.e.

tkn task start git-clone --param url= https://github.com/tekton-example/java-app-example.git --param revision=main --param subdirectory=application-source --workspace name=output,claimName=pipeline-pvc --showlog

Next, we will create a Pipeline to combine these Tasks to finish our CI/CD solution.

Step 6. Create the Pipeline

The Pipeline will represent a list of Tasks to execute, passing the necessary parameters to each Task. The pipeline can provide default arguments to Tasks and will also accept parameters via a PipelineRun. This means we can use the Pipeline to deploy any number of applications by creating a PipelineRun specific to each application.

Once we’ve wired our Pipeline up we can add it to the cluster: oc create -f <file_name> .

Note on Task Concurrency

You can build your pipeline to run tasks in parallel. If you do, keep in mind that there can be issues with trying to mount Workspaces concurrently if they are backed by the same PVC and it is ReadWriteOnce. While Tekton is in beta, I would suggest only running Tasks in parallel if your build process allows it and the Tasks don’t need a shared Workspace. You can do so by removing the runAfter property in the Pipeline under the Task.

Step 7. Create a PipelineRun

With our Tasks and our Pipeline created, we are now ready to define an execution of the pipeline for our example application. The PipelineRun will supply the required parameters for the Pipeline, which ultimately get used by the Tasks.

The PipelineRun ties everything together for this application. We provide the pipeline-pvc created previously, and explicitly assign the ServiceAccount which has access to our git repositories, the image registry, and cluster permissions, though this account will also be used by default.

If we needed, we could also parameterize our dependencies or application source code to use a specific branch by passing the values through the PipelineRun to the Pipeline and to the git-clone Tasks.

Step 8. Execute the Pipeline

With the pipeline in place, running it is simple. First commit your code to the default main branch, and then create the PipelineRun:

oc create -f java-app-example-pipelinerun.yaml

You can monitor the progress of the pipeline with the Tekton CLI:

tkn pr logs -Lf

Each PipelineRun created represents one execution of the Pipeline. You can look at all your runs with oc get pr , and describe each with oc describe pr .

Next Steps

Once you have a running pipeline for your applications, you can configure a webhook to trigger your pipeline automatically via a Pull Request with triggers. Also, there are many useful Tasks in the Tekton Catalog created by the community, such as one for a sonarqube security scanner, or one that creates a GitHub release. Take a look at other helpful Tekton resources and start customizing your pipeline to suit your organization’s needs.

Summary

That concludes this guide. We covered how to install, configure, and organize a Tekton CI/CD environment for your cluster, how to create and organize your artifacts, and then we created a complete Pipeline example for a Java application that utilizes Tekton’s latest workspaces and results features. If you completed this guide, I hope you have noticed how modular and extensible Tekton can be.

If you would like to contribute to the example project space please send me a message.

Connect with me on LinkedIn.

Thanks for reading.