Tekton’s Pipeline resource can run multiple Tasks in parallel, controlled by parameters, to create dynamic and efficient CI/CD workflows.
Let’s see this in action. Imagine you have a set of microservices, each needing to be built and pushed to a registry. Instead of a single Pipeline with a hardcoded list of services, you want to parameterize this so you can build any subset, or all, of them at once.
Here’s a Pipeline that does just that:
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: matrix-build-push
spec:
params:
- name: services
description: A comma-separated list of services to build and push.
type: string
default: "service-a,service-b,service-c"
tasks:
- name: build-and-push-service
taskRef:
name: build-and-push # Assumes you have a Task named build-and-push
params:
- name: service-name
value: $(tasks.build-and-push-service.params.service-name)
matrix:
# This is the magic: iterate over each service provided in the input param
services: $(split(params.services, ','))
# The 'when' condition ensures this task only runs if the matrix iteration is active
when: $(tasks.build-and-push-service.matrix.services != '')
And here’s a simplified Task that build-and-push-service might reference:
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: build-and-push
spec:
params:
- name: service-name
description: The name of the service to build.
type: string
steps:
- name: build-and-push
image: gcr.io/cloud-builders/docker
script: |
echo "Building and pushing $(params.service-name)..."
# In a real scenario, this would be actual docker build and push commands
# e.g., docker build -t my-registry/$(params.service-name):latest .
# e.g., docker push my-registry/$(params.service-name):latest
sleep 10 # Simulate work
echo "Finished $(params.service-name)."
When you trigger a PipelineRun for matrix-build-push, you can pass in the services parameter. For example, to build only service-a and service-c:
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: matrix-build-push-run-a-c
spec:
pipelineRef:
name: matrix-build-push
params:
- name: services
value: "service-a,service-c"
Tekton will then instantiate the build-and-push-service task twice, once for service-a and once for service-c. The matrix field in the Pipeline definition tells Tekton to take the value of $(split(params.services, ',')) (which is an array of strings like ["service-a", "service-c"]) and create a separate instance of the task for each element in that array. Each instance gets its own unique value for $(tasks.build-and-push-service.matrix.services), which is then used to dynamically set the service-name parameter for that specific task instance. The when condition is crucial here; it ensures that the task logic only executes when the services matrix variable actually has a value assigned to it for that specific task instance. This is how Tekton allows you to generate multiple task runs from a single Pipeline definition based on input parameters.
The core problem this solves is avoiding repetitive Pipeline definitions for similar, but slightly different, workloads. Instead of having build-service-a, build-service-b, and build-service-c PipelineRuns, you have one Pipeline and dynamic PipelineRuns. This dramatically reduces YAML sprawl and makes your CI/CD system far more maintainable and adaptable. The matrix feature, combined with parameterization and the split function, lets you define a template that can generate a variable number of parallel task executions on the fly.
A common point of confusion is how the params within the matrix section of the task work. When you define value: $(tasks.build-and-push-service.params.service-name) inside the matrix block, you’re telling Tekton that for each iteration of the matrix, the service-name parameter for that specific instance of the build-and-push-service task should be set to the value of $(tasks.build-and-push-service.matrix.services). This is how each parallel task instance receives its unique input. The $(tasks.build-and-push-service.matrix.services) expression refers to the current element being processed by the matrix loop.
The next thing you’ll likely run into is needing to aggregate results or perform a subsequent step only after all parallel tasks have completed.