How To Deploy Jenkins as StatefulSet on Kubernetes

Jenkins on Kubernetes

In this blog, I have given the step by step tutorial to deploy Jenkins as StatefulSet on the Kubernetes Cluster.

Prerequisites

Given below are the Prerequisites for this setup

  1. Kubernetes Cluster
  2. kubectl installed in your system

Deploy Jenkins as StatefulSet

The following image shows the setup architecture and workflow.

Jenkins deployment as statefulset on kubernetes

Follow the below steps to deploy Jenkins as a StatefulSet.

Step 1: Create a Namespace

The first step is to create a separate namespace to deploy Jenkins.

Run the following command to create a namespace

kubectl create ns jenkins

Step 2: Create a ClusterRole and ServiceAccount

After creating the namespace, the next step is to create a clusterrole, serviceaccount and bind the clusterrole to a service account.

This service account grants Jenkins the necessary permissions to interact with the Kubernetes API.

This is primarily required for CI/CD tasks where Jenkins need to create, update, and manage resources within the cluster (e.g., pods, services, and deployments).

Create a YAML file sa.yaml and copy the below contents

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: jenkins-admin
rules:
  - apiGroups: [""]
    resources: ["*"]
    verbs: ["*"]

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins-admin
  namespace: jenkins

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: jenkins-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: jenkins-admin
subjects:
- kind: ServiceAccount
  name: jenkins-admin
  namespace: jenkins

This file creates a ServiceAccount and a ClusterRole with admin permission, you can limit the permissions as you want.

Then it binds the ClusterRole with the ServiceAccount. Run the following command to create the service account

kubectl apply -f sa.yaml

Step 3: Create a Secret

The next step is to create a Secret which creates a token for the serviceaccount.

Create a YAML file secret.yaml and copy the below contents in to it

apiVersion: v1
kind: Secret
metadata:
  name: sa-token-secret
  namespace: jenkins
  annotations:
    kubernetes.io/service-account.name: jenkins-admin
type: kubernetes.io/service-account-token

This creates a secret and links it with the service account using the annotation.

The token in the secret is particularly useful when you use Kubernetes pods and dynamic jenkins agents.

Step 4: Create a PersistentVolumeClaim (PVC)

Now, create a PersistentVolumeClaim for Jenkins.

Create a YAML file pvc.yaml and copy the below contents

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jenkins-pv-claim
  namespace: jenkins
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

This file creates a PVC which requests 10GB of storage with readwriteonce access mode.

Run the following command to create a PVC

kubectl apply -f pvc.yaml

Step 5: Deploy Jenkins as SatefulSet

Once the required resources for Jenkins are created, deploy Jenkins as a Statefulset on the cluster.

Create a YAML file statefulset.yaml and copy the below contents into it

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: jenkins
  namespace: jenkins
spec:
  serviceName: "jenkins"
  replicas: 1
  selector:
    matchLabels:
      app: jenkins-server
  template:
    metadata:
      labels:
        app: jenkins-server
    spec:
      securityContext:
        fsGroup: 1000 
        runAsUser: 1000
      serviceAccountName: jenkins-admin
      containers:
        - name: jenkins
          image: jenkins/jenkins:lts
          resources:
            limits:
              memory: "2Gi"
              cpu: "1000m"
            requests:
              memory: "500Mi"
              cpu: "500m"
          ports:
            - name: httpport
              containerPort: 8080
            - name: jnlpport
              containerPort: 50000
          livenessProbe:
            httpGet:
              path: "/login"
              port: 8080
            initialDelaySeconds: 90
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 5
          readinessProbe:
            httpGet:
              path: "/login"
              port: 8080
            initialDelaySeconds: 60
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          volumeMounts:
            - name: jenkins-data
              mountPath: /var/jenkins_home
      volumes:
        - name: jenkins-data
          persistentVolumeClaim:
              claimName: jenkins-pv-claim

This file deploys Jenkins on the Jenkins namespace with the following:

  1. As statefulset with one replica. We cannot run more than one replica becuase of the way Jenkins manages it data. Read the Jenkins architecture blog to know more.
  2. Uses a security context to run Jenkins as non-root user ID 1000. By specifying a non-root UID, you limit the potential security risk that can be caused by a compromised Jenkins container. Running as a non-root user reduces the risk of privilege escalation attacks.
  3. Jenkins uses the serviceaccount jenkins-admin we created in step 2.
  4. Uses the latest container image jenkins/jenkins:lts
  5. Added resource limits and requests, as well as liveness and readiness probes.
  6. Then it exposes port 8080 for UI access and jnlp port 50000 to communicate with the agent.
  7. Jenkins stores its data on the mounted volume under the path /var/jenkins_home and it claims the volume using the PVC created in Step 3.

Run the following command to deploy Jenkins as statefulset

kubectl apply -f statefulset.yaml

Step 6: Expose Jenkins

The final step is to expose Jenkins as NodePort using a service so that we can access the Jenkins UI on the web.

Create a YAML file sva.yaml and copy the below content into it

apiVersion: v1
kind: Service
metadata:
  name: jenkins-service
  namespace: jenkins
spec:
  selector: 
    app: jenkins-server
  type: NodePort
  ports:
    - name: http-port
      port: 8080
      targetPort: 8080
      nodePort: 30000
    - name: jnlp-port
      port: 50000
      targetPort: 50000

The jenkins-service service exposes the Jenkins port 8080 on NodePort 30000 to access the Jenkins UI on the web.

Run the following command to create the service

kubectl apply -f svc.yaml

Note: Exposing Jenkins as NodePort is not suitable for the production environment. In actual projects need to setup Ingress to access Jenkins.

Now access your Jenkins on the web using the following URL

http://<node-ip>:30000

After running this command Jenkins will as you to enter the initialAdminPassword as shown below

Jenkins initial Admin apssword

To get the admin password run the following command. Replace jenkins-0 with your pod name.

kubectl exec -it jenkins-0 cat  /var/jenkins_home/secrets/initialAdminPassword -n jenkins

After entering the admin password, it will ask you to install the plugins, and create an admin user and you can see the Jenkins workspace after completing the steps.

Jenkins Dashboard

If you want to add additional plugins to your Jenkins, go to

Manage Jenkins->Plugins->Available plugins and search for the plugin you need.

For example, in the below image, I am installing the Blue Ocean plugin on Jenkins, select the plugin and click the Install button to install it

Add and additional plugin on Jenkins

After installing the plugin you can see it on the left side of your Jenkins dashboard as shown below

Installed plugins on Jenkins

Click the Open Blue Ocean button to open the Blue Ocean dashboard

Blue Ocean dashboard

Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like