Step-by-Step Guide: Deploy Jenkins on Kubernetes with NFS Storage and CI/CD Pipelines
This tutorial walks through installing NFS, creating PersistentVolume and PersistentVolumeClaim, deploying Jenkins as a Kubernetes Deployment with a Service, configuring Jenkins credentials and plugins, and building a complete CI/CD pipeline that builds, pushes Docker images and promotes deployments across dev, qa, and prod environments.
Part 1: Install NFS and Deploy Jenkins on the Kubernetes Cluster
On a chosen node (e.g., 192.168.80.180) install the NFS utilities, start the service, and create a shared directory /data/v1. Export the directory with read‑write permissions for the cluster subnet and reload the export table.
yum install nfs-utils -y
systemctl start nfs
mkdir -p /data/v1
# Add to /etc/exports
# /data/v1 192.168.80.0/24(rw,no_root_squash)
exportfs -arv
systemctl restart nfsCreate a dedicated namespace for Jenkins: kubectl create namespace jenkins-k8s Define a PersistentVolume that uses the NFS server:
apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins-k8s-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
nfs:
server: 192.168.80.180
path: /data/v1Apply the PV: kubectl apply -f pv.yaml Create a matching PersistentVolumeClaim in the jenkins-k8s namespace:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-k8s-pvc
namespace: jenkins-k8s
spec:
resources:
requests:
storage: 10Gi
accessModes:
- ReadWriteManyVerify the binding: kubectl get pvc -n jenkins-k8s Create a service account and grant it cluster‑admin rights:
kubectl create sa jenkins-k8s-sa -n jenkins-k8s
kubectl create clusterrolebinding jenkins-k8s-sa-cluster \
--clusterrole=cluster-admin \
--serviceaccount=jenkins-k8s:jenkins-k8s-saDeploy Jenkins as a Deployment that mounts the NFS volume and uses the service account:
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
namespace: jenkins-k8s
spec:
replicas: 1
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
serviceAccount: jenkins-k8s-sa
containers:
- name: jenkins
image: jenkins/jenkins:lts
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: web
protocol: TCP
- containerPort: 50000
name: agent
protocol: TCP
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
readinessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
volumeMounts:
- name: jenkins-volume
subPath: jenkins-home
mountPath: /var/jenkins_home
volumes:
- name: jenkins-volume
persistentVolumeClaim:
claimName: jenkins-k8s-pvcAdjust ownership of the NFS directory and apply the deployment:
chown -R 1000 /data/v1
kubectl apply -f jenkins-deployment.yaml
kubectl get pods -n jenkins-k8sExpose Jenkins via a NodePort Service:
apiVersion: v1
kind: Service
metadata:
name: jenkins-service
namespace: jenkins-k8s
labels:
app: jenkins
spec:
selector:
app: jenkins
type: NodePort
ports:
- name: web
port: 8080
targetPort: web
nodePort: 30002
- name: agent
port: 50000
targetPort: agent kubectl apply -f jenkins-service.yaml
kubectl get svc -n jenkins-k8sPart 2: Configure Jenkins
Access Jenkins at http://192.168.80.199:30002/login. Retrieve the initial admin password from the NFS host:
cat /data/v1/jenkins-home/secrets/initialAdminPasswordComplete the setup wizard, install the recommended plugins, and create the first admin user (for demonstration both username and password are admin).
Part 3: Test Jenkins CI/CD Pipeline
Install the Kubernetes plugin (Manage Jenkins → Manage Plugins → Available → search “kubernetes”) and restart Jenkins.
Add a new Kubernetes cloud (Manage Jenkins → Configure System). Provide the API server URL and the service account jenkins-k8s-sa. Verify the connection test succeeds.
Configure a pod template that uses a Docker container and a host‑path volume pointing to the NFS share. Set the service account to jenkins-k8s-sa in the advanced options.
Add Docker Hub credentials (ID: dockerhub) in the global credentials store.
Create a pipeline job named jenkins-variable-test-deploy with the following script. The script clones a Git repository, builds a Docker image tagged with the short Git commit hash, pushes the image to Docker Hub, and sequentially deploys it to development, QA, and production environments after manual approval.
node('testhan') {
stage('Clone') {
echo "1.Clone Stage"
git url: "https://github.com/luckylucky421/jenkins-sample.git"
script {
build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
}
}
stage('Test') { echo "2.Test Stage" }
stage('Build') {
echo "3.Build Docker Image Stage"
sh "docker build -t xianchao/jenkins-demo:${build_tag} ."
}
stage('Push') {
echo "4.Push Docker Image Stage"
withCredentials([usernamePassword(credentialsId: 'dockerhub', passwordVariable: 'dockerHubPassword', usernameVariable: 'dockerHubUser')]) {
sh "docker login -u ${dockerHubUser} -p ${dockerHubPassword}"
sh "docker push xianchao/jenkins-demo:${build_tag}"
}
}
stage('Deploy to dev') {
echo "5.Deploy DEV"
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-dev.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-dev.yaml"
sh "kubectl apply -f k8s-dev.yaml --validate=false"
}
stage('Promote to qa') {
def userInput = input(id: 'userInput', message: 'Promote to qa?', parameters: [[ $class: 'ChoiceParameterDefinition', choices: "YES
NO", name: 'Env' ]])
echo "User selected ${userInput} for QA promotion"
if (userInput == "YES") {
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-qa.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-qa.yaml"
sh "kubectl apply -f k8s-qa.yaml --validate=false"
sh "sleep 6"
sh "kubectl get pods -n qa"
}
}
stage('Promote to prod') {
def userInput = input(id: 'userInput', message: 'Promote to prod?', parameters: [[ $class: 'ChoiceParameterDefinition', choices: "YES
NO", name: 'Env' ]])
echo "User selected ${userInput} for production promotion"
if (userInput == "YES") {
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-prod.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-prod.yaml"
sh "kubectl apply -f k8s-prod.yaml --record --validate=false"
}
}
}Save the pipeline and trigger a build. The job will clone the source, build and push a Docker image, and deploy it to the specified environments based on manual approvals.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Full-Stack DevOps & Kubernetes
Focused on sharing DevOps, Kubernetes, Linux, Docker, Istio, microservices, Spring Cloud, Python, Go, databases, Nginx, Tomcat, cloud computing, and related technologies.
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.
