Skip to main content

Command Palette

Search for a command to run...

Hands-on Demonstration of Tekton Pipelines on OpenShift

Published
8 min read
H

I am here for giving wonderful knowledge to society

Introduction

Modern CI/CD requires pipelines that are cloud‑native, Kubernetes‑native, scalable, and vendor‑neutral. Tekton Pipelines, a CNCF project, fits perfectly into this space. Red Hat OpenShift provides first‑class support for Tekton through the OpenShift Pipelines Operator.

In this blog, we’ll walk through a step‑by‑step demonstration of a Tekton Pipeline on OpenShift, from installation to execution, using a simple build pipeline.


What is Tekton?

Tekton is a Kubernetes‑native framework for creating CI/CD pipelines. Everything in Tekton is defined as Kubernetes CRDs (Custom Resource Definitions).

Core Tekton Concepts

  • Task – A single unit of work (e.g., build, test, push image)

  • TaskRun – Execution of a Task

  • Pipeline – A collection of Tasks connected together

  • PipelineRun – Execution of a Pipeline

  • Workspace – Shared storage between tasks

  • Trigger – Event‑based pipeline execution (GitHub webhook, etc.)


Why Tekton on OpenShift?

  • Native integration with OpenShift

  • Built‑in web console visualization

  • Secure by default (SCC, RBAC)

  • Operator‑based lifecycle management

  • Works seamlessly with OpenShift Builds, ImageStreams, and Routes


Prerequisites

Before starting, ensure you have:

  • OpenShift cluster (4.x)

  • Cluster admin or sufficient privileges

  • oc CLI installed and configured

  • Basic understanding of Kubernetes concepts


Step 1: Install OpenShift Pipelines Operator

  1. Log in to the OpenShift Web Console

  2. Navigate to Operators → OperatorHub

  3. Search for OpenShift Pipelines

  4. Click Install (default settings are fine)

Verify installation:

oc get pods -n openshift-pipelines

1️⃣ Tekton Task – The Building Block

What is a Task?

A Task is the smallest unit of work in Tekton. Each task:

  • Runs inside its own Kubernetes Pod

  • Contains one or more steps

  • Uses container images to execute commands

Example Tasks in Our Demo

In our Node.js CI/CD setup, we use multiple tasks:

  • git-clone-task → Clones source code from Git

  • nodejs-build-test → Installs dependencies, runs tests, builds the app

  • deploy-to-openshift → Deploys the app to OpenShift

Key Characteristics of Tasks

  • Tasks are reusable

  • Tasks are independent

  • Tasks do not share data by default

This last point is very important and leads us to workspaces.


2️⃣ Workspace – Sharing Data Between Tasks

Why Workspaces Are Needed

In Tekton:

  • Each task runs in a separate pod

  • Each pod has its own filesystem

But in CI/CD pipelines:

  • One task clones code

  • Another task builds the same code

  • Another task deploys it

A Workspace solves this problem by providing shared storage.

Simple Definition

A Workspace is a shared directory mounted into multiple task pods.


Workspace at Different Levels

Task Level

A task declares that it needs a workspace:

workspaces:

- name: source


Pipeline Level

The pipeline connects workspaces between tasks:

workspaces:

- name: shared-data

workspaces:

- name: source

workspace: shared-data


PipelineRun Level

The PipelineRun provides the actual storage:

workspaces:

- name: shared-data

emptyDir: {}


3️⃣ Tekton Pipeline – Orchestrating Tasks

What is a Pipeline?

A Pipeline defines:

  • Which tasks should run

  • In what order they should run

  • How data flows between them

A pipeline itself does not execute anything.


Example: Node.js CI/CD Pipeline Flow

Our pipeline performs the following steps:

  1. Clone source code from Git

  2. Build and test the Node.js application

  3. Deploy the application to OpenShift


Pipeline Parameters

params:

- name: git-url

- name: git-revision

- name: app-name

Parameters make pipelines:

  • Configurable

  • Reusable across environments


Task Execution Order

runAfter:

- fetch-source

This ensures tasks run sequentially in the correct order.


4️⃣ Tekton PipelineRun – Executing the Pipeline

What is a PipelineRun?

A PipelineRun triggers the actual execution of a pipeline.

Think of it as:

  • Pipeline → Blueprint

  • PipelineRun → Actual run


Example PipelineRun

kind: PipelineRun

spec:

pipelineRef:

name: nodejs-cicd-pipeline

This tells Tekton which pipeline to execute.


Passing Runtime Parameters

params:

- name: git-url

value: "https://github.com/nodeshift-starters/nodejs-rest-http"

- name: app-name

value: "my-nodejs-app"

Parameters can be overridden without changing the pipeline definition.


5️⃣ Understanding emptyDir in PipelineRun

What is emptyDir?

emptyDir is a Kubernetes volume type.

In Tekton PipelineRun:

  • It creates temporary shared storage

  • Storage exists only for the lifetime of the PipelineRun

  • Automatically deleted after execution

workspaces:

- name: shared-data

emptyDir: {}


Why emptyDir Is Ideal for CI/CD

  • No storage provisioning required

  • Fast and lightweight

  • Perfect for short-lived pipelines

Most CI/CD pipelines do not require persistent data.


Step 2: Create a Project

oc project demo-app


Step 3: Create a Tekton Task

---
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: hello-world
spec:
  params:
    - name: message
      type: string
      default: "Hello from Tekton!"
  steps:
    - name: echo-message
      image: registry.redhat.io/ubi8/ubi-minimal:latest
      command:
        - echo
      args:
        - "$(params.message)"
    - name: show-date
      image: registry.redhat.io/ubi8/ubi-minimal:latest
      command:
        - date

---
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: git-clone-task
spec:
  params:
    - name: url
      type: string
    - name: revision
      type: string
      default: "main"
  workspaces:
    - name: output
  steps:
    - name: clone
      image: registry.redhat.io/ubi8/ubi:latest
      script: |
        #!/bin/bash
        yum install -y git
        cd $(workspaces.output.path)
        git clone $(params.url) .
        git checkout $(params.revision)
        echo "Source code cloned successfully"

---
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: nodejs-build-test
spec:
  workspaces:
    - name: source
  steps:
    - name: install-dependencies
      image: registry.access.redhat.com/ubi8/nodejs-16:latest
      workingDir: $(workspaces.source.path)
      script: |
        #!/bin/bash
        echo "Installing Node.js dependencies..."
        npm install
        echo "Dependencies installed successfully"

    - name: run-tests
      image: registry.access.redhat.com/ubi8/nodejs-16:latest
      workingDir: $(workspaces.source.path)
      script: |
        #!/bin/bash
        echo "Running tests..."
        npm test || echo "No tests found, skipping..."
        echo "Tests completed"

    - name: build-application
      image: registry.access.redhat.com/ubi8/nodejs-16:latest
      workingDir: $(workspaces.source.path)
      script: |
        #!/bin/bash
        echo "Building Node.js application..."
        npm run build || echo "No build script found, skipping..."
        echo "Build completed successfully"

---
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: build-push-image
spec:
  params:
    - name: IMAGE
      type: string
    - name: DOCKERFILE
      type: string
      default: "Dockerfile"
  workspaces:
    - name: source
  steps:
    - name: build-and-push
      image: registry.redhat.io/rhel8/buildah:latest
      workingDir: $(workspaces.source.path)
      securityContext:
        privileged: true
      script: |
        #!/bin/bash
        echo "Building container image..."

        # Create Dockerfile if it doesn't exist
        if [ ! -f "$(params.DOCKERFILE)" ]; then
          echo "Creating default Dockerfile..."
          cat > Dockerfile << EOF
        FROM registry.access.redhat.com/ubi8/nodejs-16:latest
        WORKDIR /app
        COPY package*.json ./
        RUN npm install
        COPY . .
        EXPOSE 3000
        CMD ["npm", "start"]
        EOF
        fi

        buildah bud -t $(params.IMAGE) -f $(params.DOCKERFILE) .
        buildah push $(params.IMAGE)
        echo "Image built and pushed: $(params.IMAGE)"

---
apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: deploy-to-openshift
spec:
  params:
    - name: APP_NAME
      type: string
    - name: IMAGE
      type: string
  steps:
    - name: deploy
      image: registry.redhat.io/openshift4/ose-cli:latest
      script: |
        #!/bin/bash
        set -e
        echo "Deploying Node.js application to OpenShift..."

        # Create deployment
        cat <<EOF | oc apply -f -
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: $(params.APP_NAME)
        spec:
          replicas: 1
          selector:
            matchLabels:
              app: $(params.APP_NAME)
          template:
            metadata:
              labels:
                app: $(params.APP_NAME)
            spec:
              containers:
              - name: app
                image: gcr.io/google-samples/hello-app:1.0
                ports:
                - containerPort: 8080
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: $(params.APP_NAME)
        spec:
          selector:
            app: $(params.APP_NAME)
          ports:
          - port: 8080
            targetPort: 8080
        ---
        apiVersion: route.openshift.io/v1
        kind: Route
        metadata:
          name: $(params.APP_NAME)
        spec:
          to:
            kind: Service
            name: $(params.APP_NAME)
          port:
            targetPort: 8080
        EOF

        # Wait for deployment
        oc rollout status deployment/$(params.APP_NAME) --timeout=300s

        # Get route URL
        ROUTE_URL=$(oc get route $(params.APP_NAME) -o jsonpath='{.spec.host}')
        echo "Application deployed successfully!"
        echo "Access URL: http://$ROUTE_URL"

oc apply -f /home/rhel/tekton-blog-demo/all-tasks-manifest.yaml


Step 4: Create a Tekton Pipeline

This pipeline uses the hello-task.

apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: nodejs-cicd-pipeline
spec:
  params:
    - name: git-url
      type: string
      default: "https://github.com/nodeshift-starters/nodejs-rest-http"
    - name: git-revision
      type: string
      default: "main"
    - name: app-name
      type: string
      default: "nodejs-app"

  workspaces:
    - name: shared-data

  tasks:
    # 1. Clone source code
    - name: fetch-source
      taskRef:
        name: git-clone-task
      workspaces:
        - name: output
          workspace: shared-data
      params:
        - name: url
          value: $(params.git-url)
        - name: revision
          value: $(params.git-revision)

    # 2. Build and test Node.js application
    - name: build-test
      taskRef:
        name: nodejs-build-test
      runAfter:
        - fetch-source
      workspaces:
        - name: source
          workspace: shared-data

    # 3. Deploy to OpenShift
    - name: deploy-app
      taskRef:
        name: deploy-to-openshift
      runAfter:
        - build-test
      params:
        - name: APP_NAME
          value: $(params.app-name)
        - name: IMAGE
          value: "gcr.io/google-samples/hello-app:1.0"

Apply the pipeline:

oc apply -f /home/rhel/tekton-blog-demo/nodejs-cicd-pipeline.yaml

Step 5: Run the Pipeline

What the ServiceAccount Controls in a Pipeline

A ServiceAccount determines:

1️⃣ Permissions (RBAC)

What the pipeline pods can do:

  • Create deployments

  • Create services or routes

  • Read/write Kubernetes resources

  • Access the OpenShift API

oc adm policy add-scc-to-user privileged -z pipeline


Step 6: Run the Pipeline

Create a PipelineRun:

apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  generateName: nodejs-cicd-run-
spec:
  pipelineRef:
    name: nodejs-cicd-pipeline
  params:
    - name: git-url
      value: "https://github.com/nodeshift-starters/nodejs-rest-http"
    - name: app-name
      value: "my-nodejs-app"
  workspaces:
    - name: shared-data
      emptyDir: {}

Apply it:

oc create -f nodejs-cicd-pipelinerun.yaml


Step 7: Monitor Pipeline Execution

Using OpenShift Console

  • Go to Pipelines → Pipelines

  • Select hello-pipeline

  • View execution graph and logs

Using CLI

oc get pipelineruns

oc logs -l tekton.dev/pipelineRun=hello-pipeline-run --all-containers

Expected output:

Hello from Tekton on OpenShift!


Step 8: Visualizing Pipelines in OpenShift

OpenShift provides a graphical pipeline view:

  • Task dependencies

  • Execution status

  • Logs per step

  • Duration metrics

This makes debugging and monitoring extremely easy compared to traditional CI tools.


Real‑World Use Case Example

In real projects, Tekton pipelines typically:

  • Clone source code from Git

  • Run unit tests

  • Build container images

  • Push images to OpenShift Image Registry

  • Deploy applications using kubectl or oc


Best Practices

  • Use Workspaces instead of emptyDir for shared data

  • Avoid hardcoding secrets – use Kubernetes Secrets

  • Version control pipeline YAMLs

  • Reuse tasks from Tekton Hub

  • Use Triggers for GitOps workflows


Common Troubleshooting Tips

  • Check SCC permissions if pods fail

  • Verify service account RBAC

  • Inspect TaskRun logs

  • Ensure correct API version (tekton.dev/v1)


Conclusion

Tekton Pipelines on OpenShift provide a powerful, Kubernetes‑native CI/CD solution that integrates seamlessly with modern DevOps workflows. With operator‑based installation and a rich UI, OpenShift makes Tekton easy to adopt and operate.

If you’re building cloud‑native applications on OpenShift, Tekton is a must‑have tool in your DevOps toolkit.