In a previous article Aktion was released to the world. In that announcement we described how Aktion brings GitHub Actions to Kubernetes by using TektonCD Pipeline.
Given a GitHub Action workflow, Aktion will convert it into Tekton API objects: Pipeline, PipelineResources, Tasks and optional TaskRun. These objects are custom resources that the Tekton controller understands and transforms ultimately into Kubernetes Pods to perform, well, tasks. You can get a crash course on Tekton by following the introduction tutorial.
A simple hello world Action workflow would resemble something along the lines of:
workflow "Tekton test" {
on = "push"
resolves = [
"First Action",
]
}
action "First Action" {
uses = "docker://centos"
runs = "echo"
args = "Hello world"
}
Each action is required to specify a uses entry to denote the Docker container to run the specified command in. In the initial release of Aktion, the only supported entry is a pre-existing Docker image using the docker:// prefix. GitHub, however, allows for the specification of a local path inside a repository in the form of ./path/to/Dockerfile, or an external GitHub repository in the form of organization/project/path/to/Dockerfile. At GitHub, Actions take this path, and builds the container on the fly for you.
This means that Aktion also needs to be able to trigger a Docker image build. This technical post shows you how we added this feature and used Tekton objects to perform a container build.
Enter Aktion
Aktion was recently updated to enable support for the repo specific portion of referring to a Docker container. To support this, some fundamental changes have been made to how Aktion creates the TektonCD Pipeline CRDs:
1. The --repo flag has been replaced by a --git flag, and is now required for actions that utilize the ./path/to/Dockerfile local path approach.
1. Build tasks are now created to convert each uses component into a Docker container for use within the action.
1. Each action still corresponds to a step within a Pipeline Task and each Workflow is a corresponding Pipeline Task. With the introduction of the docker container build tasks, the tasks are now contained within a TektonCD Pipeline.
1. --taskrun has been renamed --pipelinerun and will create a TektonCD PipelineRun CRD.
Looking at a sample action:
workflow "local repo test" {
on = "push"
resolves = [
"First Action",
]
}
action "First Action" {
uses = "./samples/test-images"
runs = "echo"
args = "Hello world"
}
Inside the samples/test-images directory is a Dockerfile:
# A test image to verify the flow of Aktion
FROM alpine:latest
RUN echo "This is a test run"
With Aktion, you can translate the entire workflow into Tekton Objects like so:
$ aktion create -f samples/test-local.workflow --pipelinerun --git github.com/triggermesh/aktion
You will clearly see below that the HCL syntax of GitHub Action is much more terse and user friendly, while the Tekton objects in YAML are not meant for human consumption. We foresee that Action will be used heavily from a user standpoint but that Tekton will become the backend of choice for workflows.
For reference here are all the objects created by the transformation:
---
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
creationTimestamp: null
name: samples-test-images-git
spec:
params:
- name: revision
value: master
- name: url
value: github.com/triggermesh/aktion
type: git
status: {}
---
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
creationTimestamp: null
name: samples-test-images-image
spec:
params:
- name: url
value: knative.registry.svc.cluster.local/samples-test-images-image
type: image
status: {}
---
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
creationTimestamp: null
name: build-samples-test-images
spec:
inputs:
params:
- default: Dockerfile
name: pathToDockerFile
- name: pathToContext
resources:
- name: workspace
targetPath: ""
type: git
outputs:
resources:
- name: image
targetPath: ""
type: image
steps:
- args:
- --dockerfile=${inputs.params.pathToDockerFile}
- --destination=${outputs.resources.image.url}
- --context=${inputs.params.pathToContext}
- --insecure
- --insecure-registry
- --verbosity=debug
command:
- /kaniko/executor
image: gcr.io/kaniko-project/executor
name: build-and-push-samples-test-images
resources: {}
---
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
creationTimestamp: null
name: local-repo-test
spec:
params:
- name: revision
value: master
- name: url
value: github.com/triggermesh/aktion
type: git
status: {}
---
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
creationTimestamp: null
name: local-repo-test
spec:
inputs:
resources:
- name: local-repo-test
targetPath: ""
type: git
steps:
- args:
- Hello
- world
command:
- echo
image: knative.registry.svc.cluster.local/samples-test-images-image
name: first-action
resources: {}
---
apiVersion: tekton.dev/v1alpha1
kind: Pipeline
metadata:
creationTimestamp: "2019-05-03T22:58:00Z"
name: local-repo-test-pipeline
spec:
params: null
resources:
- name: samples-test-images-git
type: git
- name: samples-test-images-image
type: image
- name: local-repo-test
type: git
tasks:
- name: build-samples-test-images
params:
- name: pathToContext
value: /workspace/workspace/samples/test-images
resources:
inputs:
- name: workspace
resource: samples-test-images-git
outputs:
- name: image
resource: samples-test-images-image
taskRef:
name: build-samples-test-images
- name: local-repo-test
resources:
inputs:
- name: local-repo-test
resource: local-repo-test
outputs: null
taskRef:
name: local-repo-test
status: {}
---
apiVersion: tekton.dev/v1alpha1
kind: PipelineRun
metadata:
creationTimestamp: "2019-05-03T22:58:00Z"
name: local-repo-test-pipeline-run
spec:
Status: ""
params: null
pipelineRef:
name: local-repo-test-pipeline
resources:
- name: samples-test-images-image
resourceRef:
name: samples-test-images-image
- name: samples-test-images-git
resourceRef:
name: samples-test-images-git
- name: local-repo-test
resourceRef:
name: local-repo-test
serviceAccount: ""
trigger:
type: manual
status:
conditions: null
The breakdown on the components:
– PipelineResources corresponding to the input Github repo and output image
– A build Task using [Kaniko](https://github.com/GoogleContainerTools/kaniko)
– A third PipelineResource for the git repo to be referenced within the main task
– A main Task that is referencing the image built from the previous build task
– A Pipeline CRD that populates the appropriate references between the build
task and main task
– A PipelineRun CRD to perform the one-shot execute operation
To see the above example in operation on a Kubernetes cluster using TriggerMesh’s Kubernetes based Docker Registry. You can pipe the resulting objects via kubectl apply . Two pods will be created: one to build the image, and a second one to run the main task.
Applying the Tekton objects leads to:
$ ./aktion create -f samples/test-local.workflow --pipelinerun --git https://github.com/triggermesh/aktion|kubectl apply -f -
pipelineresource.tekton.dev/samples-test-images-git created
pipelineresource.tekton.dev/samples-test-images-image created
task.tekton.dev/build-samples-test-images created
pipelineresource.tekton.dev/local-repo-test created
task.tekton.dev/local-repo-test created
pipeline.tekton.dev/local-repo-test-pipeline configured
pipelinerun.tekton.dev/local-repo-test-pipeline-run created
You will be able to verify the status of the build using:
kubectl get pipelinerun.tekton.dev/local-repo-test-pipeline-run
NAME STATUS STARTTIME COMPLETIONTIME
local-repo-test-pipeline-run True 52s 16s
The pods created will be similar to:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
local-repo-test-pipeline-run-build-samples-test-images-s7mfh-pod-fe1332 0/3 Completed 0 110s
local-repo-test-pipeline-run-local-repo-test-7p9mx-pod-09a983 0/3 Completed 0 109s
Lastly, verifying the logs from local-repo-test-pipeline-run-local-repo-test-7p9mx-pod-09a983 which is the main task:
$ kubectl logs local-repo-test-pipeline-run-local-repo-test-7p9mx-pod-09a983 build-step-first-action
Hello world
To rerun the workflow, delete the pipelinerun.tekton.dev/local-repo-test-pipeline-run entity, and reapply the PipelineRun YAML component.
Aktion and remote GitHub repos
GitHub Action also supports referencing GitHub repos as well. Here is a sample workflow that references a remote repository: https://github.com/cab105/aktion-test.
workflow "local repo test" {
on = "push"
resolves = [
"Cat hello",
"Echo hello",
]
}
action "Cat hello" {
uses = "cab105/aktion-test/hello-repo/images@hello-repo"
runs = "cat"
args = "/hello.txt"
}
action "Echo hello" {
uses = "cab105/aktion-test/hello-repo/images@hello-repo"
runs = "echo"
args = "Hello from Aktion"
}
In this case, uses reflects the organization of cab105 and repo name of aktion-test followed by the path that contains a Dockerfile. The @hello-repo indicates the branch to find this directory, but can also reflect the repo tag or changeset ID.
Aktion does require the file workflow file to be present. To try this out, clone the repository https://github.com/cab105/aktion-test and checkout the hello-repo branch.
Since there are no local directories specified in this workflow (the uses arg starting with ./), the --git flag can be omitted.
To create the corresponding Tekton objects do:
$ aktion create -f ./hello-repo.workflow --pipelinerun
Enjoy
Don’t be overwhelmed by the face full of YAML syndrome. Keep in mind what this all means: You can execute GitHub Actions on your own Kubernetes cluster !!!! Think about this …

Sebastien GoasguenSebastien is a co-founder of TriggerMesh. He has been involved with open source for almost 20 years. Sebastien is an Apache Software Foundation committer and an early contributor to the Kubernetes ecosystem. Around 2014, he became fascinated by the meteoric rise of Docker and decided to learn as much as he could, which led him to author the O’Reilly Docker cookbook. It was this experience that introduced him to Kubernetes, and it was love at first Pod. Sebastien became an early and active participant in the CNCF space, contributing, building, teaching, and innovating. He founded TriggerMesh to push the boundaries of the serverless space and allow all companies to benefit from this new paradigm, even those with on-premises applications.