Tekton Pipelines (v0.11.0-rc2)
Old school cool tekton pipelines.
Looking for a Cloud Native CI/CD pipelines for your Kubernetes cluster? Check out the tekton-pipelines
project from the great folks over at Tekton.
Tekton bridges the ever-decreasing gap between Development and Operations a little bit more with their state of the art pipeline runners and command line interface (which lacks in most modern CI/CD solutions imho, finally start
and stop
pipeline executions from the CLI)! In this first series of blog posts on tekton
(part 1 of 3) we'll setup a Tekton Pipeline
to release our public tkn
docker
image.
Setup
Let's deploy a quick kind
cluster using our public kind
image, install tekton-pipelines
then setup a Pipeline
and related tekton
resources to build
and push
our docker
images using kaniko
after passing our Dockerfile through the Haskell Dockerfile Linter.
Tekton
tekton-pipelines
v0.11.0-rc2
is available for download from the projects releases.
KinD
Our kind
in dind
container mounts the local ${PWD}/tekton
directory which should contain the tekton-pipelines
release.yaml
to the containers /tekton
directory. The users docker
config (${HOME}/.docker/config.json
) also needs to be mounted in the container:
docker run --rm -d \
--privileged \
-v ${HOME}/.docker/config.json:/root/.docker/config.json \
-v ${PWD}/tekton:/tekton \
--name tekton \
docker.pkg.github.com/lazybit-ch/kind/kind:v0.7.0
Create the kind
cluster: docker exec -it tekton kind create cluster --name tekton
then install tekton-pipelines
: docker exec -it tekton kubectl apply -f /tekton/release.yaml
.
Tip: watch the pods being created in the tekton-pipelines
namespace: docker exec -it tekton kubectl get pod -n tekton-pipelines -w
.
We need to create an Opaque
Secret for the kaniko
to access the private registry that we'll be pushing our images to: docker exec -it tekton kubectl -n tekton-pipelines create generic regcred --from-file=/root/.docker/config.json
.
Note: if you don't have a docker
registry consider running one alongside the kind
cluster i.e.: docker exec -it tekton docker run -d -p 5000:5000 --name registry -e REGISTRY_STORAGE_DELETE_ENABLED=true registry:2
. In this blog post we'll be pushing images to our official registry
.
Tekton Pipelines
tekton-pipelines
extends Kubernetes with the Tasks
, ClusterTasks
, TaskRuns
, PipelineResources
, Pipelines
and PipelineRuns
Custom Resources. Breaking down our release pipeline we need to:
- checkout the source code
- lint the Dockerfile
build
thedocker
imagelogin
to theregistry
push
thedocker
image
Task
Tasks
define steps to execute and each step of the Task
is run in a container. The lint
Task
uses the git
PipelineResource
as input to the Task
then lints the Dockerfile inside a hadolint/hadolint
container (tip: by default objects are created under /workspace
and the git
repository is cloned to the directory named from the spec.inputs.resources[0].name
):
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
name: hadolint
spec:
inputs:
resources:
- name: source
type: git
steps:
- name: hadolint
image: hadolint/hadolint
command: ["/bin/ash"]
args:
- -c
- |
hadolint /workspace/source/Dockerfile
Tip: splitting the lint
and build
logic into separate Tasks
allows us to share the Tasks
in various Pipelines
.
The build
Task
specifies Task
scoped parameters with defaults, the Task
input (git
repository clone) and output (image
) resources
and the steps
to build our image using kaniko
:
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: kaniko
namespace: tekton-pipelines
spec:
params:
- name: dockerfile
type: string
description: The path to the Dockerfile to build
default: /workspace/source/Dockerfile
- name: context
type: string
description: The Kaniko build context
default: /workspace/source
resources:
inputs:
- name: source
type: git
outputs:
- name: image
type: image
steps:
- name: build-and-push
image: gcr.io/kaniko-project/executor:v0.17.1
command:
- /kaniko/executor
args:
- --dockerfile=$(params.dockerfile)
- --destination=$(resources.outputs.image.url)
- --context=$(params.context)
- --cache=false
env:
- name: DOCKER_CONFIG
value: /tekton/home/.docker
volumeMounts:
- mountPath: /tekton/home/.docker/
name: regcred
readOnly: true
volumes:
- name: regcred
secret:
secretName: regcred
Note: our regcred
secret is mounted in the Task
spec with the required environment set for kaniko
to push the docker
image.
TaskRuns
PipelineResources
are bound to Tasks
during TaskRuns
. We can simulate our Pipeline
by creating the PipelineResources
and TaskRun
definitions then applying them to the cluster ordering the execution manually:
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: tkn-git
namespace: tekton-pipelines
spec:
type: git
params:
- name: url
value: https://github.com/lazybit-ch/tkn.git
- name: revision
value: master
---
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: hadolint
namespace: tekton-pipelines
spec:
taskRef:
name: hadolint
resources:
inputs:
- name: source
resourceRef:
name: tkn-git
After the Dockerfile is linted the PipelineResources
and TaskRun
for our build can be created then applied to the cluster:
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: tkn-image
namespace: tekton-pipelines
spec:
type: image
params:
- name: url
value: lazybit.ch/tkn:latest
---
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: kaniko
namespace: tekton-pipelines
spec:
taskRef:
name: kaniko
resources:
inputs:
- name: source
resourceRef:
name: tkn-git
outputs:
- name: image
resourceRef:
name: tkn-image
Note: the kaniko
TaskRun
reuses the tkn-git
PipelineResources
that were used in the hadolint
TaskRun
.
Pipelines
Tekton Pipeline
resources order our TaskRuns
so we don't have to. Our Pipeline
definition will ensure that our lint
task is executed before executing our build
:
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: tkn
spec:
resources:
- name: source
type: git
- name: image
type: image
tasks:
- name: lint
taskRef:
name: hadolint
resources:
inputs:
- name: source
resource: source
- name: build
taskRef:
name: kaniko
resources:
inputs:
- name: source
resource: source
outputs:
- name: image
resource: image
PipelineRuns
The Pipelines
are triggered by PipelineRuns
. Similar to TaskRuns
the PipelineRun
objects bind Tasks
with PipelineResources
and executes the Tasks
steps
in the order specified in the Pipeline
:
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: tkn-run
spec:
pipelineRef:
name: tkn
resources:
- name: source
resourceRef:
name: tkn-git
- name: image
resourceRef:
name: tkn-image
Conclusion
This blog post barely scratches the surface of tekton-pipelines
but pretty, pretty cool. I like an "all the things in docker
" approach to everything and it only makes sense for modern CI/CD (why use Jenkins to build code in docker
containers when you could use the docker
containers directly). Hard-coded like it is makes it such that the Pipeline
will only execute once, we need to delete then create a new PipelineRun
to trigger another execution - stay tuned for our future blog post on tekton-triggers
to template PipelineRuns
with EventListeners
!