Exploring building images with Tekton

ยท

5 min read

Why did I write this?

I think it's always a good idea to have a tl;dr. I'd been following Tekton for some time and I enjoy automating things, so I thought it would be interesting to see what it can do. The following article shows how you can run "Tasks" as Kubernetes native resources (a huge bonus!), to build and push container images to a container registry. The code and manifests used in this blog can be found here.

Introduction

If you are not familiar with Tekton, it is bagged as an "open source framework for creating CI/CD systems". It also has pretty good support for other tools in the ecosystem such as Jenkins, Skaffold etc. So simply, we'll use Tekton to compile a simple Go program within a container image, and store that image in a (local) container registry.

Environment

I'm using KinD with a local (insecure) registry for demonstration purposes only. There were a number of configuration items that needed to be added in order to get traffic flowing and pulling of images to work (given containerD related configurations for insecure registries should be set). Largely, these can be seen in the excerpt from the shell script in the manifests/tektoncd directory of the git repository:

With the above patches, everything below should work as intended. Otherwise, you will likely encounter "ErrImagePull" when deploying your newly created container image from the local registry.

Tekton resources

As outlined within the "Getting Started" interactive tutorial, there are a few things that are needed in order for Tekton to actually do something:

  • PipelineResources
  • Task
  • TaskRun
PipelineResources

We'll be making use of two resource "types" which allow us to give the Task input, and any other relevant resource values for artefacts that the pipeline or task produces ("resource outputs"). The first of which is our source git repository, which the task needs to compile our Go binary:

The second, is our target container registry which is where our built image will be pushed to by the Task:

You can use either kubectl apply -f <filename> or tkn resource create --template <filename>. The Tekton controller, which is responsible for managing and reconciling the state of any Tekton resources, will create the PipelineResource within your cluster.

Now let's move on to our Task definition.

Task

Much like its AWS ECS namesake, we need to tell Tekton to perform a series of, or a one-off, action(s). For this specific example I've utilised the use case within the interactive tutorial, which uses Kaniko to build a container image (resource.output) using the code within our git repository (resource.input) as defined by our Dockerfile. Importantly, we've also said that the Task accepts two parameters, "pathToDockerFile" and "pathToContext" (which will be familiar to anyone who has touched Docker):

Some important call outs:

  • If you are developing locally, you can probably get away with using the --insecure flag without any consequences (but I wouldn't recommend this in any other scenario).
  • Build arguments are passed to Kaniko in exactly the same way as they are in Docker. But of course, any sensitive parameters shouldn't be hardcoded within the "default" field of the Task.
  • It's also unnecessary to pass everything in context, which I've done above, this should be scoped to only the files needed to complete the build.

Before we move on to initiating a TaskRun resource, which acts like a Job, on KinD I also needed to add a ConfigMap resource which tells Tekton how to request the storage it needs. Both the Tekton repo and the documentation detail this requirement.

If you do not do this, when you create a TaskRun, the pod that conducts the work detailed in our Task will be stuck in the "Unknown - Pending" state. However, there is no indication that it is because it cannot bind to the persistent volume claim (there is already a Github issue for this). You might be wondering why this happens, it's because the controller doesn't continuously try to reconcile the TaskRun, to make it aware that a persistent volume claim is later available.

TaskRun

Deploying the container image

So, we have everything in place to be able to deploy our newly built container image from our local registry. You can use the following command to generate the template for the Pod (or Deployment, the choice is yours):

kubectl run example --image localhost:5000/myregistry/noteapp --port 8010 --hostport 80 --dry-run=client -o yaml

The --port value should be the same as what was passed to the "Task" when invoking the "TaskRun". This will yield output similar to the following:

Now, apply the manifest with cat <filename> | kubectl apply -f -, ensure that the Pod is running with kubectl get po -w. Finally, if everything went smoothly, expose the container using the following: kubectl expose pod/example2 --port 80 --target-port 8010.

At this point, we should have a functioning application that we're able to curl against. For that, I used Postman pointing at localhost:8010/signup, and sending a POST request with the following JSON payload:

(Note: please do feel free to play with the --build-args and add certs for TLS if necessary, omitted for brevity here.)

Does the fun stop here?

Absolutely not! You can add additional steps into a Pipeline (collection of Tasks) in the exact same way, and use a PipelineRun to start it. You can also add a number of Triggers to start these processes too, you can setup EventListeners to listen to events externally, and react to those events by creating Tekton resources.

To build on this, you can start here. ๐ŸŽ‰๐Ÿป

ย