Transform Jenkins Pipelines into Tekton with Helm Charts for Multi‑Env Deployments
This article walks through converting a Jenkinsfile‑based pipeline to a Tekton Pipeline, adding Helm‑based deployment tasks, integrating SonarQube scanning, handling multi‑branch releases across different namespaces, and demonstrates the full PipelineRun execution with code snippets and images.
In the previous "Tekton Series – Practice Part – My First Pipeline" article we built a simple pipeline; this guide shows how to customize a Tekton Pipeline based on an existing Jenkinsfile.
Jenkinsfile Overview
The original Jenkinsfile defines agents, environment variables, parameters, options, and stages for checkout, build & push, Helm repo addition, and deployments to dev, test, uat, pre, and prod environments, as well as a SonarQube scan.
<code>def dingmes = new org.devops.sendDingTalk()
def BUILD_USER
def IS_IMAGE_PUSH
pipeline {
agent {
kubernetes {
label "jenkins-slave-${UUID.randomUUID().toString()}"
yaml """
apiVersion: v1
kind: Pod
spec:
nodeSelector:
kubernetes.io/hostname: node-2
containers:
- name: gradle
image: registry.cn-hangzhou.aliyuncs.com/coolops/builder-gradle:v2
command: ['cat']
tty: true
volumeMounts:
- name: caches
mountPath: /root/.gradle/caches/
- name: indocker
mountPath: /var/run/docker.sock
- name: helm
image: registry.cn-hangzhou.aliyuncs.com/coolops/helm3:3.2.4
command: ['cat']
tty: true
- name: sonar
image: registry.cn-hangzhou.aliyuncs.com/coolops/gradle:5.6.4-jdk11
command: ['cat']
tty: true
volumeMounts:
- name: sonarcache
mountPath: /root/.gradle/caches/
volumes:
- name: caches
hostPath:
path: "/data/jenkins-job/${JOB_NAME}/gradle/"
- name: indocker
hostPath:
path: "/var/run/docker.sock"
- name: sonarcache
hostPath:
path: "/data/jenkins-job/${JOB_NAME}/sonar/"
"""
}
}
environment {
APP_NAME = "${params.APP_NAME}"
DOCKER_CREDENTIAL_ID = 'dockerhub-token'
GIT_CREDENTIAL_ID = 'git-token'
SONAR_CREDENTIAL_ID = 'sonar-token'
KUBECONFIG_CREDENTIAL_ID = 'kubeconfig-token'
REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
DOCKERHUB_NAMESPACE = 'coolops'
CHART = 'coolops/rd'
CHART_USERNAME = xxx
CHART_PASSWORD = xxx
IMG_REPO = "$REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME"
IMG_TAG = "$GIT_COMMIT"
COMMON_ARGS = "--set image.repository=$IMG_REPO \
--set image.tag=$IMG_TAG \
--set ingress.hosts[0].paths[0]=/ "
}
parameters {
choice(description: '通过 Gradle --refresh-dependencies 参数进行 Jar 包强制刷新', name: 'refresh', choices: ['false', 'true'])
}
options { timeout(time: 30, unit: 'MINUTES') }
stages {
stage('Checkout SCM') { steps { checkout(scm) } }
stage('Build & Push') {
steps {
container('gradle') {
withCredentials([usernamePassword(credentialsId: "$DOCKER_CREDENTIAL_ID", passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME')]) {
sh 'echo "$DOCKER_PASSWORD" | docker login $REGISTRY -u "$DOCKER_USERNAME" --password-stdin'
sh '''
export EXIST_IMG=$(docker pull $IMG_REPO:$IMG_TAG >/dev/null && echo true || echo false)
if [ $refresh == "true" -o $EXIST_IMG == "false" ]; then
echo "开始编译并推送镜像";
$refresh && gradle clean bootJar --configure-on-demand --build-cache --refresh-dependencies || gradle clean bootJar --configure-on-demand --build-cache
docker build -f Dockerfile -t $IMG_REPO:$IMG_TAG .
docker push $IMG_REPO:$IMG_TAG
else
echo "镜像已存在,跳过编译"
fi
'''
}
}
}
}
// Additional stages for helm repo, deployments, and SonarQube omitted for brevity
}
}
</code>Key Differences from the First Pipeline
Multi‑branch pipeline releases
Deployment switched from kubectl to Helm charts
Added code scanning with SonarQube
Helm charts are stored in an Alibaba Cloud private repository; users can apply for access via Alibaba Cloud → Cloud DevOps → R&D → Private Repository.
Helm Chart Deployment Task
The following Task definition encapsulates Helm deployment parameters such as namespace, app name, chart name, and additional arguments.
<code>apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
name: helm-to-k8s
spec:
workspaces:
- name: source
- name: kubernetesconfig
mountPath: /root/.kube
params:
- name: IMAGE
- name: TAG
- name: NAMESPACE
- name: BRANCH_NAME
- name: CHART_NAME
- name: CHART_USERNAME
- name: CHART_PASSWORD
- name: APP_NAME
steps:
- name: run-helm
image: registry.cn-hangzhou.aliyuncs.com/coolops/helm3:3.2.4
workingDir: $(workspaces.source.path)
script: |
helm repo add coolops https://repomanage.rdc.aliyun.com/helm_repositories/66465-coolops \
--username=$(params.CHART_USERNAME) --password=$(params.CHART_PASSWORD)
common_args="--set image.repository=$(params.IMAGE) --set image.tag=$(params.TAG) --set ingress.hosts[0].paths[0]=/"
helm -n $(params.NAMESPACE) upgrade $(params.APP_NAME) $(params.CHART_NAME) ${common_args} || \
helm -n $(params.NAMESPACE) install $(params.APP_NAME) $(params.CHART_NAME) ${common_args}
</code>SonarQube Scanner Task
This Task runs SonarQube analysis on the source code.
<code>apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
name: sonar-scanner
spec:
workspaces:
- name: source
params:
- name: SONAR_USERNAME
- name: SONAR_PASSWORD
- name: SONAR_URL
- name: APP_NAME
steps:
- name: sonar-scanner
image: registry.cn-hangzhou.aliyuncs.com/coolops/sonar-scanner:2.2.0
workingDir: $(workspaces.source.path)
script: |
scanTime=`date +%F-%H-%M-%S`
sonar-scanner -Dsonar.host.url=$(params.SONAR_URL) \
-Dsonar.projectKey=$(params.APP_NAME) \
-Dsonar.projectName=$(params.APP_NAME) \
-Dsonar.projectVersion=${scanTime} \
-Dsonar.login=$(params.SONAR_USERNAME) \
-Dsonar.password=$(params.SONAR_PASSWORD) \
-Dsonar.projectDescription="$(workspaces.source.path)"
</code>Assembling the Full Pipeline
The pipeline stitches together cloning, unit testing, image building & pushing, Helm deployments to dev/test/pre/uat/prod based on branch names, and the SonarQube scan.
<code>apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: rd-pipeline
spec:
workspaces:
- name: rd-repo-pvc
- name: docker-config
- name: kubernetes-config
params:
- name: git_url
- name: revision
type: string
default: "master"
- name: gitInitImage
type: string
default: "registry.cn-hangzhou.aliyuncs.com/coolops/tekton-git-init:v0.29"
- name: pathToDockerfile
description: "The path to the build context, used by Kaniko"
default: "."
- name: imageUrl
description: "Url of image repository"
- name: imageTag
description: "Tag to apply to the built image"
default: "latest"
- name: chart_name
type: string
default: "coolops/coolops-rd"
- name: chart_username
type: string
- name: chart_password
type: string
- name: app_name
type: string
- name: namespace
type: string
default: "default"
- name: sonar_username
type: string
default: "admin"
- name: sonar_password
type: string
default: "admin"
- name: sonar_url
type: string
tasks:
- name: clone
taskRef:
name: git-clone
workspaces:
- name: output
workspace: rd-repo-pvc
params:
- name: url
value: $(params.git_url)
- name: revision
value: $(params.revision)
- name: gitInitImage
value: $(params.gitInitImage)
- name: unit-test
runAfter: [clone]
taskRef:
name: unit-test
workspaces:
- name: source
workspace: rd-repo-pvc
- name: build-push-image
runAfter: [unit-test]
taskRef:
name: build-push-image
params:
- name: pathToDockerfile
value: $(params.pathToDockerfile)
- name: imageUrl
value: $(params.imageUrl)
- name: imageTag
value: $(tasks.clone.results.commit)
workspaces:
- name: source
workspace: rd-repo-pvc
- name: dockerconfig
workspace: docker-config
- name: deploy-to-dev
when:
- input: $(params.revision)
operator: in
values: [dev]
taskRef:
name: helm-to-k8s
params:
- name: IMAGE
value: $(params.imageUrl)
- name: TAG
value: $(tasks.clone.results.commit)
- name: BRANCH_NAME
value: $(params.revision)
- name: CHART_NAME
value: $(params.chart_name)
- name: CHART_USERNAME
value: $(params.chart_username)
- name: CHART_PASSWORD
value: $(params.chart_password)
- name: APP_NAME
value: $(params.app_name)
- name: NAMESPACE
value: coolops-dev
workspaces:
- name: source
workspace: rd-repo-pvc
- name: kubernetesconfig
workspace: kubernetes-config
runAfter: [build-push-image]
// Similar deploy-to-test, deploy-to-pre, deploy-to-uat, deploy-to-prod tasks omitted for brevity
- name: sonar-scanner
when:
- input: $(params.revision)
operator: in
values: [test]
taskRef:
name: sonar-scanner
runAfter: [clone]
params:
- name: SONAR_USERNAME
value: $(params.sonar_username)
- name: SONAR_PASSWORD
value: $(params.sonar_password)
- name: SONAR_URL
value: $(params.sonar_url)
- name: APP_NAME
value: $(params.app_name)
workspaces:
- name: source
workspace: rd-repo-pvc
</code>Running a PipelineRun
A sample PipelineRun supplies concrete parameter values and workspace definitions.
<code>apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: test-hello-world-pipeline-run
spec:
pipelineRef:
name: rd-pipeline
params:
- name: revision
value: test
- name: git_url
value: https://gitee.com/coolops/devops-hello-world.git
- name: imageUrl
value: registry.cn-hangzhou.aliyuncs.com/coolops/devops-hello-world
- name: imageTag
value: latest
- name: pathToDockerfile
value: Dockerfile
- name: chart_username
value: username
- name: chart_password
value: password
- name: app_name
value: hello-world
- name: sonar_username
value: username
- name: sonar_password
value: password
- name: sonar_url
value: http://sonarqube.coolops.cn
workspaces:
- name: rd-repo-pvc
volumeClaimTemplate:
spec:
accessModes: [ReadWriteOnce]
storageClassName: openebs-hostpath
resources:
requests:
storage: 1Gi
- name: docker-config
secret:
secretName: docker-config
- name: kubernetes-config
secret:
secretName: kubernetes-config
serviceAccountName: tekton-build-sa
</code>The pipeline successfully builds the Docker image, pushes it, deploys the application to the selected namespace via Helm, and reports SonarQube analysis results.
SonarQube scan results are displayed as follows:
Conclusion
Migrating from Jenkins to Tekton mainly involves rewriting the pipeline syntax; the underlying steps—code checkout, build, image push, multi‑environment deployment, and code scanning—remain the same, making the transition straightforward.
Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.