This post go over the installation, basic resource, and a use cases of Tekton Pipelines.
More information can be found on there website tekton.dev.
Tekton Installation
Pipeline
To install Tekton Pipeline on your cluster just run the following kubectl command.
kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
If you want to deploy a specific release replace latest
with previous/vX.Y.Z
. Releases can be found on there GitHub here.
Operator
It can also be installed using the Tekton Operator which can be found here
kubectl apply -f https://storage.googleapis.com/tekton-releases/operator/latest/release.yaml
Dashboard
Tekton also offers a web dashboard to view Tekton resources. Documentation and installation can be found here.
Tekton Concepts
I recommend you checkout the official documentation here since its better than my explanation.
Steps
At the lowest level, Tekton implements a Step
resource. A Tekton Step
defines an atomic operation in a pipeline, such as cloning down a Git repository, kicking off a build, and so forth.
Tasks
Defines a series of Steps that run sequentially to perform logic Every Task runs as a pod on the Kubernetes cluster, with each step running in its own container.
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: hello
spec:
steps:
- name: echo
image: alpine
script: |
#!/bin/sh
echo "Hello World"
TaskRuns
TaskRun execute the steps in a task.
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
name: hello-task-run
spec:
taskRef:
name: hello
View logs of output kubectl logs --selector=tekton.dev/taskRun=hello-task-run
Pipelines
A Pipeline defines an ordered series of Tasks arranged in a specific execution order as part of the CI/CD workflow
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: hello-goodbye
spec:
tasks:
- name: hello
taskRef:
name: hello
- name: goodbye
runAfter:
- hello
taskRef:
name: goodbye
PipelineRuns
A PipelineRun, represented in the API as an object, sets the value for the parameters and executes a Pipeline
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: hello-goodbye-run
spec:
pipelineRef:
name: hello-goodbye
Params
Unless you want the task to run with the same input every time parameters can be passed in from the Pipeline, TaskRuns or PipelineRuns.
Documentation on parameters in tasks.
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: task-with-parameters
spec:
params:
- name: foo
description: description for the parameter
default: test
type: string
steps:
- name: echo
image: alpine
script: |
#!/bin/sh
echo "$(params.foo)"
Docs on parameters in Pipelines can be found here.
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: pipeline-with-parameters
spec:
params:
- name: input
type: string
description: Value for task
default: default
tasks:
- name: task-with-parameters
taskRef:
name: task-with-parameters
params:
- name: foo
value: $(params.input)
Calling the pipeline with parameters documentation.
Workspaces
Workspaces allow you to pass in storage that will be used by your tasks. General documentation can be found here.
In this example the task will be passed a workspace that will be used to store files.
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: read-from-directory
spec:
workspaces:
- name: source
description: location to share files between tasks
steps:
- name: read-dir
image: docker.io/library/bash
script: |
#!/bin/sh
ls "$(workspaces.source.path)"
When a PipelineRun is triggered a workspace will be passed in which will be passed to the Pipeline and then the tasks associated if they request it.
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: read-from-directory-pipeline
spec:
workspaces:
- name: shared-data
description: location to share files between tasks
tasks:
- name: read-from-directory
taskRef:
name: read-from-directory
workspaces:
- name: source
workspace: shared-data
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: read-from-directory-pipeline-run-1
spec:
pipelineRef:
name: read-from-directory-pipeline
workspaces:
- name: shared-data
emptyDir:
sizeLimit: 2Gi
Tekton also supports workspaces from secrets, configmaps, PVC, volume claims, and emptyDir.
Triggering Jobs
Pipelines can be triggered manually with PipelineRuns but that does not scale well.
Tekton Triggers
Documentation can be found here.
kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml
kubectl apply --filename https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml
Release can be found here on Github.
Useful Resources
- https://pkg.go.dev/github.com/tektoncd/pipeline/pkg/apis/pipeline/v1
- https://pkg.go.dev/github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1
Use Cases
Automated Image Building
I want to highlight the combination of pulling from GitHub and building images using Kaniko.
The task is build on the following tektoncd catalogs git-clone and kaniko
This task could be separated into two tasks but then you would need a persistent storage between them.
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: clone-build-push
spec:
description: |
This task will Clone your Git repository and then build the image with kaniko before pushing it to your artifact repository.
The steps are a combination of the Tekton catalogs git-clone & kaniko.
workspaces:
- name: ssh-directory
optional: true
description: |
A .ssh directory with private key, known_hosts, config, etc. Copied to
the user's home before git commands are executed. Used to authenticate
with the git remote when performing the clone. Binding a Secret to this
Workspace is strongly recommended over other volume types.
- name: basic-auth
optional: true
description: |
A Workspace containing a .gitconfig and .git-credentials file. These
will be copied to the user's home before any git commands are run. Any
other files in this Workspace are ignored. It is strongly recommended
to use ssh-directory over basic-auth whenever possible and to bind a
Secret to this Workspace over other volume types.
- name: ssl-ca-directory
optional: true
description: |
A workspace containing CA certificates, this will be used by Git to
verify the peer with when fetching or pushing over HTTPS.
- name: source
description: The git repo will be cloned onto the volume backing this Workspace.
- name: dockerconfig
description: Includes a docker `config.json`
optional: true
mountPath: /kaniko/.docker
params:
- name: IMAGE
description: Name (reference) of the image to build.
- name: DOCKERFILE
description: Path to the Dockerfile to build.
default: ./Dockerfile
- name: CONTEXT
description: The build context used by Kaniko.
default: ./
- name: EXTRA_ARGS
type: array
default: []
- name: BUILDER_IMAGE
description: The image on which builds will run (default is v1.5.1)
default: gcr.io/kaniko-project/executor:v1.5.1@sha256:c6166717f7fe0b7da44908c986137ecfeab21f31ec3992f6e128fff8a94be8a5
- name: url
description: Repository URL to clone from.
type: string
- name: revision
description: Revision to checkout. (branch, tag, sha, ref, etc...)
type: string
default: ""
- name: refspec
description: Refspec to fetch before checking out revision.
default: ""
- name: submodules
description: Initialize and fetch git submodules.
type: string
default: "true"
- name: depth
description: Perform a shallow clone, fetching only the most recent N commits.
type: string
default: "1"
- name: sslVerify
description: Set the `http.sslVerify` global git config. Setting this to `false` is not advised unless you are sure that you trust your git remote.
type: string
default: "true"
- name: crtFileName
description: file name of mounted crt using ssl-ca-directory workspace. default value is ca-bundle.crt.
type: string
default: "ca-bundle.crt"
- name: subdirectory
description: Subdirectory inside the `output` Workspace to clone the repo into.
type: string
default: ""
- name: sparseCheckoutDirectories
description: Define the directory patterns to match or exclude when performing a sparse checkout.
type: string
default: ""
- name: deleteExisting
description: Clean out the contents of the destination directory if it already exists before cloning.
type: string
default: "true"
- name: httpProxy
description: HTTP proxy server for non-SSL requests.
type: string
default: ""
- name: httpsProxy
description: HTTPS proxy server for SSL requests.
type: string
default: ""
- name: noProxy
description: Opt out of proxying HTTP/HTTPS requests.
type: string
default: ""
- name: verbose
description: Log the commands that are executed during `git-clone`'s operation.
type: string
default: "true"
- name: gitInitImage
description: The image providing the git-init binary that this Task runs.
type: string
default: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:v0.40.2"
- name: userHome
description: |
Absolute path to the user's home directory.
type: string
default: "/home/git"
results:
- name: commit
description: The precise commit SHA that was fetched by this Task.
- name: url
description: The precise URL that was fetched by this Task.
- name: committer-date
description: The epoch timestamp of the commit that was fetched by this Task.
- name: IMAGE_DIGEST
description: Digest of the image just built.
- name: IMAGE_URL
description: URL of the image just built.
steps:
- name: clone
image: "$(params.gitInitImage)"
env:
- name: HOME
value: "$(params.userHome)"
- name: PARAM_URL
value: $(params.url)
- name: PARAM_REVISION
value: $(params.revision)
- name: PARAM_REFSPEC
value: $(params.refspec)
- name: PARAM_SUBMODULES
value: $(params.submodules)
- name: PARAM_DEPTH
value: $(params.depth)
- name: PARAM_SSL_VERIFY
value: $(params.sslVerify)
- name: PARAM_CRT_FILENAME
value: $(params.crtFileName)
- name: PARAM_SUBDIRECTORY
value: $(params.subdirectory)
- name: PARAM_DELETE_EXISTING
value: $(params.deleteExisting)
- name: PARAM_HTTP_PROXY
value: $(params.httpProxy)
- name: PARAM_HTTPS_PROXY
value: $(params.httpsProxy)
- name: PARAM_NO_PROXY
value: $(params.noProxy)
- name: PARAM_VERBOSE
value: $(params.verbose)
- name: PARAM_SPARSE_CHECKOUT_DIRECTORIES
value: $(params.sparseCheckoutDirectories)
- name: PARAM_USER_HOME
value: $(params.userHome)
- name: WORKSPACE_OUTPUT_PATH
value: $(workspaces.source.path)
- name: WORKSPACE_SSH_DIRECTORY_BOUND
value: $(workspaces.ssh-directory.bound)
- name: WORKSPACE_SSH_DIRECTORY_PATH
value: $(workspaces.ssh-directory.path)
- name: WORKSPACE_BASIC_AUTH_DIRECTORY_BOUND
value: $(workspaces.basic-auth.bound)
- name: WORKSPACE_BASIC_AUTH_DIRECTORY_PATH
value: $(workspaces.basic-auth.path)
- name: WORKSPACE_SSL_CA_DIRECTORY_BOUND
value: $(workspaces.ssl-ca-directory.bound)
- name: WORKSPACE_SSL_CA_DIRECTORY_PATH
value: $(workspaces.ssl-ca-directory.path)
securityContext:
runAsNonRoot: true
runAsUser: 65532
resources:
limits:
cpu: 200m
memory: 1Gi
requests:
cpu: 100m
memory: 500Mi
script: |
#!/usr/bin/env sh
set -eu
if [ "${PARAM_VERBOSE}" = "true" ] ; then
set -x
fi
if [ "${WORKSPACE_BASIC_AUTH_DIRECTORY_BOUND}" = "true" ] ; then
cp "${WORKSPACE_BASIC_AUTH_DIRECTORY_PATH}/.git-credentials" "${PARAM_USER_HOME}/.git-credentials"
cp "${WORKSPACE_BASIC_AUTH_DIRECTORY_PATH}/.gitconfig" "${PARAM_USER_HOME}/.gitconfig"
chmod 400 "${PARAM_USER_HOME}/.git-credentials"
chmod 400 "${PARAM_USER_HOME}/.gitconfig"
fi
if [ "${WORKSPACE_SSH_DIRECTORY_BOUND}" = "true" ] ; then
cp -R "${WORKSPACE_SSH_DIRECTORY_PATH}" "${PARAM_USER_HOME}"/.ssh
chmod 700 "${PARAM_USER_HOME}"/.ssh
chmod -R 400 "${PARAM_USER_HOME}"/.ssh/*
fi
if [ "${WORKSPACE_SSL_CA_DIRECTORY_BOUND}" = "true" ] ; then
export GIT_SSL_CAPATH="${WORKSPACE_SSL_CA_DIRECTORY_PATH}"
if [ "${PARAM_CRT_FILENAME}" != "" ] ; then
export GIT_SSL_CAINFO="${WORKSPACE_SSL_CA_DIRECTORY_PATH}/${PARAM_CRT_FILENAME}"
fi
fi
CHECKOUT_DIR="${WORKSPACE_OUTPUT_PATH}/${PARAM_SUBDIRECTORY}"
cleandir() {
# Delete any existing contents of the repo directory if it exists.
#
# We don't just "rm -rf ${CHECKOUT_DIR}" because ${CHECKOUT_DIR} might be "/"
# or the root of a mounted volume.
if [ -d "${CHECKOUT_DIR}" ] ; then
# Delete non-hidden files and directories
rm -rf "${CHECKOUT_DIR:?}"/*
# Delete files and directories starting with . but excluding ..
rm -rf "${CHECKOUT_DIR}"/.[!.]*
# Delete files and directories starting with .. plus any other character
rm -rf "${CHECKOUT_DIR}"/..?*
fi
}
if [ "${PARAM_DELETE_EXISTING}" = "true" ] ; then
cleandir || true
fi
test -z "${PARAM_HTTP_PROXY}" || export HTTP_PROXY="${PARAM_HTTP_PROXY}"
test -z "${PARAM_HTTPS_PROXY}" || export HTTPS_PROXY="${PARAM_HTTPS_PROXY}"
test -z "${PARAM_NO_PROXY}" || export NO_PROXY="${PARAM_NO_PROXY}"
ls -al "${PARAM_USER_HOME}"/.ssh
git config --global --add safe.directory "${WORKSPACE_OUTPUT_PATH}"
/ko-app/git-init \
-url="${PARAM_URL}" \
-revision="${PARAM_REVISION}" \
-refspec="${PARAM_REFSPEC}" \
-path="${CHECKOUT_DIR}" \
-sslVerify="${PARAM_SSL_VERIFY}" \
-submodules="${PARAM_SUBMODULES}" \
-depth="${PARAM_DEPTH}" \
-sparseCheckoutDirectories="${PARAM_SPARSE_CHECKOUT_DIRECTORIES}"
cd "${CHECKOUT_DIR}"
RESULT_SHA="$(git rev-parse HEAD)"
EXIT_CODE="$?"
if [ "${EXIT_CODE}" != 0 ] ; then
exit "${EXIT_CODE}"
fi
RESULT_COMMITTER_DATE="$(git log -1 --pretty=%ct)"
printf "%s" "${RESULT_COMMITTER_DATE}" > "$(results.committer-date.path)"
printf "%s" "${RESULT_SHA}" > "$(results.commit.path)"
printf "%s" "${PARAM_URL}" > "$(results.url.path)"
- name: build-and-push
workingDir: $(workspaces.source.path)
image: $(params.BUILDER_IMAGE)
args:
- $(params.EXTRA_ARGS)
- --dockerfile=$(params.DOCKERFILE)
- --context=$(workspaces.source.path)/$(params.CONTEXT)
- --destination=$(params.IMAGE)
- --digest-file=$(results.IMAGE_DIGEST.path)
securityContext:
runAsUser: 0
resources:
limits:
cpu: '3'
memory: 12Gi
requests:
cpu: 500m
memory: 1Gi
- name: write-url
image: docker.io/library/bash:5.1.4@sha256:c523c636b722339f41b6a431b44588ab2f762c5de5ec3bd7964420ff982fb1d9
resources:
limits:
cpu: 50m
memory: 100Mi
requests:
cpu: 25m
memory: 50Mi
script: |
set -e
image="$(params.IMAGE)"
echo -n "${image}" | tee "$(results.IMAGE_URL.path)"
This Pipeline only calls the clone-build-push
task, it could also include a task that calls a webhook to notify that a new image has been created.
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: git-build-pipeline
spec:
description: |
git-build-pipeline takes a git repository and a commit SHA and validates that cloning the revision succeeds.
It then will build a image and push it to a artifact repository.
params:
- name: base
type: string
description: the service name
- name: branch
type: string
description: The git branch name
- name: commit
type: string
description: The git commit to fetch.
- name: image
type: string
description: the image to build
- name: url
type: string
description: The git repository URL to clone from
workspaces:
- name: shared-data
description: |
This workspace will receive the cloned git repo and be passed
to the next Task for the commit to be checked.
- name: git-credentials
description: github ssh credentials
tasks:
- name: clone-build-push
taskRef:
name: clone-build-push
workspaces:
- name: source
workspace: shared-data
- name: ssh-directory
workspace: git-credentials
params:
- name: url
value: $(params.url)
- name: revision
value: $(params.commit)
- name: depth
value: '0'
- name: IMAGE
value: $(params.image)
- name: CONTEXT
value: $(params.base)/app
This is an example PipelineRun that could be used. I would recommend that you use Tekton Triggers or some other form of automated creation of the run.
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: build-manual-pipelineRun
spec:
pipelineRef:
name: git-build-pipeline
serviceAccountName: tekton-service-account
podTemplate:
securityContext:
fsGroup: 65532
workspaces:
- name: shared-data
emptyDir:
sizeLimit: 2Gi
- name: git-credentials
secret:
secretName: github-secrets
params:
- name: base
value: SERVICE
- name: branch
value: BRANCH
- name: commit
value: COMMIT_HASH
- name: image
value: IMAGE_REPOSITORY:TAG
- name: url
value: [email protected]:ORG/REPO.git
What the example is looking for file structure in the Git repository
git-root
- SERVICE
- app
- Dockerfile
- SERVICE-2