Slowly going mad with power using Tekton
Published on , 1228 words, 5 minutes to read
Starting the slow migration off of GitHub, one CI pipeline at a time

- I'm not feeling good about the future of GitHub
- https://red-squares.cian.lol/
- I have a bunch of repos I'd like to migrate off of GitHub
GitHub has been the foundation of my career since before it began. I've been using it since 2010 (almost half my life, wow) and I have over 400 repos there from all stages of competence and my career. I'm not feeling good about its future given how unreliable it's been lately.
- One small problem: all my CI flows assume I'm using GitHub Actions
- I mean this is reasonable, I've used GitHub Actions for most of my career
- All my GitHub Actions flows only really work on GitHub
- I've moved some of them off of GitHub in the past, but it requires some significant modification and hacking because my actions really rely on GitHub platform features
As part of the planning for a post-GitHub future, I've been looking at one of the hardest things to move: GitHub Actions. I've used GitHub Actions for most of my career and only have vague memories of Jenkins, Travis, and the other things I've used before. Most of my repos only have GitHub Actions (with a small fraction of them having Gitea Actions from when I experimented with that) and a lot of my assumptions around how CI works are centered around the implementation of GitHub actions.
The other thing I'm gonna have a hard time getting over is all the basically free compute that GitHub Actions provides, which is significant when you look at how much Anubis does per commit. However, I do have a mildly absurd amount of compute laying around, so that may just even itself out I guess.
If I have to move my CI to a new solution that's going to require me to rewrite it all from scratch or adapt it over, I really want to settle on a vendor-neutral implementation that isn't beholden to a single platform. This would make me slightly more resilient against future enshittification (it's a matter of when, not if).
Tekton and you
- Recently I discovered Tekton
- Tekton is a kubernetes operator that lets you create CI/CD systems on top of Kubernetes clusters
Recently I discovered Tekton, a Kubernetes operator (read: plugin) that breaks CI/CD pipelines into Kubernetes objects. It's also as far as I can tell the only real vendor-neutral CI/CD pipeline solution. There's also a really neat bridge between Tangled.org and Tekton called Tack.
For the sake of this post, let's say that I'm going to settle on the TTT stack: Tangled, Tack, and Tekton.
Tekton in a nutshell
In order to help you understand where/why Tekton fits into the stack, let's think about it like this: Tekton is the layer under something like GitHub Actions. Tekton takes CI pipeline jobs and schedules them onto Kubernetes. Kubernetes handles scheduling pods across machines, durable storage between steps in the pipeline, networking between pods, logging, and other orchestration that you need in practice.
In Tekton, your CI/CD jobs are broken out into a few basic categories:
- Tasks: Individual things to do (clone repo, run
go test, build docker image, etc.) - Pipelines: Sequences of Tasks (think your entire CI pipeline of cloning the repo, running
go test, and building the docker image)
Here's an example Task:
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: kubectl-apply
labels:
app.kubernetes.io/version: "0.1"
spec:
description: |
Run kubectl apply
params:
- name: image
description: Docker image for kubectl
default: "cgr.dev/chainguard/kubectl:latest"
- name: args
description: Args to pass to `kubectl apply`
type: array
default: []
- name: subfolder
description: subfolder that the repo is actually in
default: "/repo"
workspaces:
- name: repo
description: Repo to operate in
steps:
- name: chown-chmod
image: $(params.image)
workingDir: $(workspaces.source.path)$(params.subfolder)
args:
- apply
- $(params.args[*])
securityContext:
runAsUser: 0
This looks like a lot, but that's mostly because Tekton is completely unopinionated. It places a lot of the assumptions that GitHub Actions or Travis makes out of the way so you build up your desired pipeline from scratch. I copied this Task out of my Tekton tasks repo in case you want to dig through the other ones I use.
Those Tasks are then chained together into Pipelines like this:
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: xeiaso-net-alrest-autoapply
namespace: ci
spec:
description: |
Autoapply for xeiaso.net/alrest manifests.
params:
- name: repo-url
type: string
description: "Git repo to clone"
default: "https://tangled.org/xeiaso.net/alrest"
- name: "commit"
type: string
description: "Git revision to check out"
workspaces:
- name: repo
description: |
Cloned repo files.
tasks:
- name: fix-permissions
taskRef:
name: fix-permissions
workspaces:
- name: dir
workspace: repo
- name: clone-repo
runAfter: ["fix-permissions"]
taskRef:
name: git-clone
workspaces:
- name: output
workspace: repo
params:
- name: url
value: $(params.repo-url)
- name: revision
value: $(params.commit)
- name: kubectl-apply
runAfter: ["clone-repo"]
taskSpec:
workspaces:
- name: repo
steps:
- name: do-apply
workingDir: $(workspaces.repo.path)/repo
image: cgr.dev/chainguard/kubectl:latest
args:
- apply
- -k
- $(workspaces.repo.path)/repo
You would then invoke this Pipeline by creating a PipelineRun object:
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: tack-autoapply-yaml-7c5e518b6c60
namespace: ci
spec:
params:
- name: commit
value: 838e7e0e83df04d54c035b9c60b852ff56852b4a
pipelineRef:
name: xeiaso-net-alrest-autoapply
taskRunTemplate:
serviceAccountName: xeiaso-net-alrest-autoapply
timeouts:
pipeline: 1h0m0s
workspaces:
- name: repo
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi
You can see how this fits together, right? PipelineRuns bind values to Pipelines, which bind values to Tasks, which sequence running Pods in your cluster. This then gets turned into green and red commit statuses in the Tangled UI via Tack.
Walking through a single pipeline
From here I think it'd be useful to walk through a single CI pipeline, the one I use in kefka (a work-in-progress port of just-bash to Go, I plan to write more about it soon, I just need the energy/time). I had to make some compromises in my cluster because of how my storage system works, but you'll see more when it's time.
- A simple pipeline from Kefka
- Fix permissions on the PVC with repo clone
- Why?
- Clone repo
- Go tests
- Shared Go mod cache PVC
- Why?
- Docker image building
- Fix permissions on the PVC with repo clone
- Docker image building
- Setting up automatic builds for ChainGuard's fork of Kaniko
go buildexiting with a vague VCS error
- Spawning sub-clusters with Kubernetes
Facts and circumstances may have changed since publication. Please contact me before jumping to conclusions if something seems wrong or unclear.
Tags: