Cloud Native 11 min read

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.

Full-Stack DevOps & Kubernetes
Full-Stack DevOps & Kubernetes
Full-Stack DevOps & Kubernetes
Step-by-Step Guide: Deploy Jenkins on Kubernetes with NFS Storage and CI/CD Pipelines

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 nfs

Create 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/v1

Apply 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:
    - ReadWriteMany

Verify 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-sa

Deploy 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-pvc

Adjust 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-k8s

Expose 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-k8s

Part 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/initialAdminPassword

Complete 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Dockerci/cdKubernetesPipelineJenkinsNFS
Full-Stack DevOps & Kubernetes
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.