Create multi-step CI/CD workflows for multi-container applications by using Red Hat OpenShift Pipelines.
Outcomes
Use OpenShift Pipelines to run common tasks when building an application.
Create a pipeline that builds a container image from application source code.
Create a custom task for running NPM commands.
Use the custom task to perform quality check tasks on the code before building the container image.
Manually run pipelines by using the Tekton CLI tool (tkn).
As the student user on the workstation machine, use the lab command to prepare
your environment for this exercise, and to ensure that all required resources are available.
[student@workstation ~]$ lab start pipelines-creation
Instructions
The application includes a Containerfile that uses registry.ocp4.example.com:8443/ubi8/nodejs-16:latest as the base image.
To prevent permission problems in the pipeline execution, make this image public in the internal Quay registry.
Open the Quay website at https://registry.ocp4.example.com:8443 and log in with the developer user and the developer password.
Search for the ubi8/nodejs-16 repository.
In the section of the ubi8/nodejs-16 settings, make the repository public.
Create a pipeline that builds a container image from the application code.
Log in to OpenShift by using the developer user account.
[student@workstation ~]$ oc login -u developer -p developer \
https://api.ocp4.example.com:6443
Login successful.
...output omitted...Change to the pipelines-creation project.
[student@workstation ~]$ oc project pipelines-creation
...output omitted...
Already on project "pipelines-creation" on server "https://api.ocp4.example.com:6443".Change to the directory containing the initial resource manifests.
[student@workstation ~]$cd ~/DO288/labs/pipelines-creationno output expected
View the pipeline.yaml file.
Observe that the pipeline accepts parameters relating to the location of the application source code.
Additionally, observe that the pipeline defines a singular workspace called shared, which shares files and state between tasks.
---
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: nodejs-build
spec:
workspaces:
- name: shared
params:
- name: IMAGE_NAME
type: string
default: "exchange"
- name: GIT_REPO
type: string
default: "https://git.ocp4.example.com/developer/DO288-apps"
- name: GIT_REVISION
type: string
default: "master"
- name: APP_PATH
type: string
default: "apps/pipelines-creation/exchange"
tasks:
- name: TODOUpdate the pipeline by adding a git-clone task that clones the application code from the classroom GitLab repository.
The task uses the shared workspace so that later tasks can access the retrieved source code files.
...output omitted...
spec:
...output omitted...
tasks:
- name: fetch-repository
taskRef:
name: git-clone
kind: ClusterTask
params:
- name: url
value: $(params.GIT_REPO)
- name: revision
value: $(params.GIT_REVISION)
- name: subdirectory
value: ""
- name: deleteExisting
value: "true"
- name: sslVerify
value: "false"
workspaces:
- name: output
workspace: sharedAdd a buildah task that builds a container image from the application source code.
For organization purposes, add this task after the clone task from the previous step.
...output omitted...
spec:
...output omitted...
tasks:
- name: fetch-repository
...output omitted...
- name: build-image
taskRef:
name: buildah
kind: ClusterTask
params:
- name: IMAGE
value: registry.ocp4.example.com:8443/developer/$(params.IMAGE_NAME):latest
- name: DOCKERFILE
value: Containerfile
- name: CONTEXT
value: $(params.APP_PATH)
- name: TLSVERIFY
value: "false"
- name: SKIP_PUSH
value: "true"
runAfter:
- fetch-repository
workspaces:
- name: source
workspace: sharedCreate the pipeline within the cluster and view the pipeline diagram.
Apply the pipeline.yaml manifest file to the cluster to create the pipeline resource object.
[student@workstation pipelines-creation]$ oc apply -f pipeline.yaml
pipeline.tekton.dev/nodejs-build createdIn a browser window, navigate to the OpenShift web console via the https://console-openshift-console.apps.ocp4.example.com URL.
Log in as the developer user and open the developer perspective.
Use the left side menu to navigate to and select the nodejs-build pipeline.
The pipeline details page shows a diagram illustrating the flow of the tasks within the pipeline.
![]() |
If you do not have a project selected, then select the pipelines-creation project from the selection menu at the top of the right pane.
Manually run the pipeline by using the tkn Tekton CLI and view the logs.
In a terminal window, run the pipeline by using the tkn start command.
Use the provided volume-template.yaml file to specify the storage for the shared pipeline workspace.
This file outlines how the cluster should create a persistent volume claim that can store files as they move between tasks.
When prompted, accept the defaults for each of the four pipeline parameters by pressing Enter for each one. Note that the name of your pipeline run differs from the following.
[student@workstation pipelines-creation]$tkn p start nodejs-build \ -w name=shared,volumeClaimTemplateFile=volume-template.yaml? Value for paramIMAGE_NAMEof typestring? (Default isexchange) exchange ...output omitted... PipelineRun started: nodejs-build-run-wh4v5 ...output omitted...
Use the watch command to wait for the pipeline to complete.
The pipeline is done when the STATUS column reads anything other than Running.
[student@workstation pipelines-creation]$watch -n 2 tkn p list...output omitted... NAME ...output omitted... DURATION STATUS nodejs-build ...output omitted... 2m51sSucceeded
After the pipeline finishes, stop the watch command by pressing Ctrl+c.
View the logs for the pipeline to observe that git cloned the code repository and that the container image was built successfully.
[student@workstation pipelines-creation]$ tkn p logs nodejs-build
...output omitted...
{"level":"info" ... "Successfully cloned ... redhattraining/DO288-apps ..."}
...output omitted...
Successfully tagged registry.ocp4.example.com:8443/developer/exchange:latest
...output omitted...Create a custom task for running NPM commands and apply it to the cluster.
View the npm-task.yaml file that defines a task with a single step.
Observe that the workspaces and params sections define storage and task parameters, respectively.
The task uses the ARGS command within its step to run specified NPM commands.
Additionally, the task defines an output result to store the output of the task.
--- apiVersion: tekton.dev/v1beta1 kind: Task metadata: name: npm spec:workspaces: - name: sourceresults: - name: outputparams: - name: CONTEXT type: string default: "." - name: ARGS type: string - name: NODE_IMAGE type: string default: "registry.access.redhat.com/ubi8/nodejs-16:latest"steps: - name: TODO
Update the file to add a step that runs the command provided by the ARGS parameter.
The script uses the tee command to store the output of the command in the output result.
spec:
...output omitted...
steps:
- name: npm-run
image: $(params.NODE_IMAGE)
script: |
npm $(params.ARGS) | tee $(results.output.path)
workingDir: $(workspaces.source.path)/$(params.CONTEXT)
env:
- name: CI
value: "true"
securityContext:
runAsNonRoot: true
runAsUser: 65532The securityContext section instructs the pod to run the command as a regular user.
This is to match the configured user in the cluster tasks that are used in the pipeline.
Apply the task manifest to the cluster within the pipelines-creation project.
[student@workstation pipelines-creation]$ oc apply -f npm-task.yaml
task.tekton.dev/npm createdUpdate the pipeline so that it runs NPM commands via the custom NPM task and then run the pipeline.
Update the pipeline.yaml file to add a task that runs npm install with the custom NPM task to fetch Node.js dependencies for the application.
This task goes between the fetch-repository and build-image tasks.
The file should match the following excerpt.
---
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: nodejs-build
spec:
...output omitted...
tasks:
- name: fetch-repository
...output omitted...
- name: npm-install
taskRef:
name: npm
kind: Task
workspaces:
- name: source
workspace: shared
params:
- name: CONTEXT
value: $(params.APP_PATH)
- name: ARGS
value: install --no-package-lock
runAfter:
- fetch-repository
- name: build-image
...output omitted...Because YAML syntax is sensitive to indentation, make sure that your file exactly matches the preceding excerpt.
Similar to the previous step, add tasks that run the application tests and a linter to check for code quality.
These tasks go between the npm-install and build-image steps and have the same indentation level.
- name: npm-test
taskRef:
name: npm
kind: Task
workspaces:
- name: source
workspace: shared
params:
- name: CONTEXT
value: $(params.APP_PATH)
- name: ARGS
value: test
runAfter:
- npm-install
- name: npm-lint
taskRef:
name: npm
kind: Task
workspaces:
- name: source
workspace: shared
params:
- name: CONTEXT
value: $(params.APP_PATH)
- name: ARGS
value: run lint
runAfter:
- npm-installAdd a task that uses the custom NPM task to retrieve the version of the application.
This task goes between the npm-lint and build-image tasks and has the same indentation level.
- name: app-version
taskRef:
name: npm
kind: Task
workspaces:
- name: source
workspace: shared
params:
- name: CONTEXT
value: $(params.APP_PATH)
- name: ARGS
value: version|grep exchange|cut -d\' -f2|tr -d '\n'
runAfter:
- fetch-repositoryUpdate the build-image task so that it runs after the quality checks and tags the image with the version number.
The build-image should look like the following excerpt.
- name: build-image
taskRef:
name: buildah
kind: ClusterTask
params:
- name: IMAGE
value: registry.ocp4.example.com:8443/developer/$(params.IMAGE_NAME):$(tasks.app-version.results.output)
- name: DOCKERFILE
value: Containerfile
- name: CONTEXT
value: $(params.APP_PATH)
- name: TLSVERIFY
value: "false"
- name: SKIP_PUSH
value: "true"
runAfter:
- npm-test
- npm-lint
- app-version
workspaces:
- name: source
workspace: sharedUpdate the pipeline in the cluster by applying the updated pipeline.yaml file.
[student@workstation pipelines-creation]$ oc apply -f pipeline.yaml
pipeline.tekton.dev/nodejs-build configuredIn the browser window, refresh the pipeline details page to observe the updated pipeline diagram.

Notice that the diagram matches the ordering of the tasks as specified by the runAfter fields in the pipeline manifest.
Run the updated pipeline and view the logs.
In a terminal window, run the pipeline by using the same tkn command as before.
When prompted, accept the defaults for each of the four pipeline parameters by pressing Enter for each one. Note that the name of your pipeline run differs from the following.
[student@workstation pipelines-creation]$tkn p start nodejs-build \ -w name=shared,volumeClaimTemplateFile=volume-template.yaml? Value for paramIMAGE_NAMEof typestring? (Default isexchange) exchange ...output omitted... PipelineRun started: nodejs-build-run-4nspb ...output omitted...
Use the watch command to wait for the pipeline to complete.
Because there are more tasks, the pipeline might take longer to finish.
[student@workstation pipelines-creation]$watch -n 2 tkn p list...output omitted... NAME ...output omitted... DURATION STATUS nodejs-build ...output omitted... 3m25sSucceeded
After the pipeline finishes, stop the watch command by pressing Ctrl+c.
View the logs for the pipeline to observe that the new tasks run in addition to the original two. Also notice that the application version number is used in the container image tag. Because there is now more than one run of the pipeline, press Enter to select the latest instance when prompted.
[student@workstation pipelines-creation]$tkn p logs nodejs-build...output omitted... {"level":"info" ... "Successfully cloned ... redhattraining/DO288-apps ..."} ...output omitted... 1.0.0 ...output omitted... > exchange@1.0.0 test > mocha tests ...output omitted... 5 passing (10ms) ...output omitted... > exchange@1.0.0 lint > eslint . ...output omitted... Successfully taggedregistry.ocp4.example.com:8443/developer/exchange:1.0.0...output omitted...