Overview #
In this tutorial I’m using a Kubernetes K8s cluster with MetalLB deployed with Kubespray on Debian 12 servers:
192.168.30.21 node1 # Controller / Master Node
192.168.30.22 node2 # Controller / Master Node
192.168.30.23 node3 # Worker Node
192.168.30.24 node4 # Worker Node
192.168.30.60 NFS Server, Bitbucket Data Center Container, Deployment Host
NFS Prerequisites #
NFS Server Setup #
Jenkins Folder Structure #
Create the folder structure for the PersistentVolume:
# Create folder structure
sudo mkdir -p /srv/nfs/k8s_share/jenkins-volume
# Change permissions
sudo chown -R 1000:1000 /srv/nfs/k8s_share/jenkins-volume
NFS Exports #
I’m using the following NFS server configuration:
# Install NFS package
sudo apt install nfs-kernel-server
# Open NFS configuration
sudo vi /etc/exports
# NFS configuration: Define the kubernetes nodes
/srv/nfs/k8s_share 192.168.30.71(rw,sync,no_root_squash)
/srv/nfs/k8s_share 192.168.30.72(rw,sync,no_root_squash)
/srv/nfs/k8s_share 192.168.30.73(rw,sync,no_root_squash)
/srv/nfs/k8s_share 192.168.30.74(rw,sync,no_root_squash)
# Restart NFS server
sudo systemctl restart nfs-server
Install NFS on Kubernetes Nodes #
Install the NFS utilities package on the Kubernetes nodes:
# Install NFS utilities package
sudo apt install nfs-common -y
Verify the NFS connectivity:
# Verify that the NFS server is correctly configured
/usr/sbin/showmount -e 192.168.30.60
Kubernetes Prerequisites #
Create Namespace #
# Create a namespace for the Jenkins deployment
kubectl create namespace jenkins
StorageClass, PV, PVC #
# Create a manifest for the Jenkins volume
vi jenkins-volume.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: jenkins-pv
namespace: jenkins
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: Immediate
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins-pv
namespace: jenkins
spec:
storageClassName: jenkins-pv
accessModes:
- ReadWriteOnce
capacity:
storage: 20Gi
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Retain
nfs:
path: /srv/nfs/k8s_share/jenkins-volume
server: 192.168.30.60
# Deploy the manifest
kubectl create -f jenkins-volume.yaml
Verify Storage Resources #
# List PV & PVC
kubectl get pv,pvc -n jenkins
# Shell output:
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/jenkins-pv 20Gi RWO Retain Available jenkins-pv <unset> 38s
Helm #
Add Jenkins Repository #
# Add the Jenkins repository
helm repo add jenkinsci https://charts.jenkins.io
# Update the repository index
helm repo update
# List Helm Charts
helm search repo jenkinsci
# Shell output:
NAME CHART VERSION APP VERSION DESCRIPTION
jenkinsci/jenkins 5.2.2 2.452.2 Jenkins - Build great things at any scale! As t...
Save Helm Chart Values #
# Save the values for the Jenkins chart
helm show values jenkinsci/jenkins > jenkins-values.yaml
Adopt Helm Chart Values #
# Open the Helm Chart manifest
vi jenkins-values.yaml
# Define the service type: (Default is ClusterIP)
serviceType: NodePort
# Storage
storageClass: jenkins-pv # Define the PV
# -- Annotations for the PVC
annotations: {}
# -- Labels for the PVC
labels: {}
# -- The PVC access mode
accessMode: "ReadWriteOnce"
# -- The size of the PVC
size: "8Gi"
# Deactivate the serviceAccount
serviceAccount:
# -- Configures if a ServiceAccount with this name should be created
create: false
# Install default plugins
installPlugins:
- kubernetes:4246.v5a_12b_1fe120e
- workflow-aggregator:596.v8c21c963d92d
- git:5.2.2
- configuration-as-code:1810.v9b_c30a_249a_4c
Install Jenkins #
# Deploy Jenkins
helm install jenkins jenkinsci/jenkins -f jenkins-values.yaml -n jenkins
# Delete Jenkins
helm delete jenkins -n jenkins
Verify Service Resources #
# List resources in the "jenkins" namespace
kubectl get all -n jenkins
# Shell output:
NAME READY STATUS RESTARTS AGE
pod/jenkins-0 2/2 Running 0 64s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/jenkins NodePort 10.233.38.174 <none> 8080:32266/TCP 64s
service/jenkins-agent ClusterIP 10.233.35.121 <none> 50000/TCP 64s
Kubernetes TLS Certificate Secret #
In this setup I’m using a Let’s Encrypt wildcard certificate.
# Create a Kubernetes secret for the TLS certificate
kubectl create secret tls jenkins-tls --cert=./fullchain.pem --key=./privkey.pem -n jenkins
Nginx Ingress for NodePort Service #
# Create ingress manifest
vi jenkins-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jenkins
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
spec:
tls:
- hosts:
- jenkins.jklug.work
secretName: jenkins-tls
rules:
- host: jenkins.jklug.work
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: jenkins # Define AWX ingress service name
port:
number: 8080
# Deploy ingress resource
kubectl create -f jenkins-ingress.yml -n jenkins
# Delete ingress resource
kubectl delete -f jenkins-ingress.yml -n jenkins
DNS Entry #
The DNS entry for the Nginx ingress must point to one of the worker nodes:
192.168.30.23 jenkins.jklug.work
# Or
192.168.30.24 jenkins.jklug.work
Jenkins Webinterface #
Webinterface #
# Access the webinterface: Via NodePort
192.168.30.21:32266
192.168.30.22:32266
192.168.30.23:32266
192.168.30.24:32266
# Access the webinterface: Via Ingress
https://jenkins.jklug.work
Login & Retrieve Password #
# Default user
admin
# Retrieve admin password
kubectl exec --namespace jenkins -it svc/jenkins -c jenkins -- /bin/cat /run/secrets/additional/chart-admin-password && echo
# Shell output:
YbGzyCogoJb8phkLqdo2kF
Restart Jenkins #
Jenkins can be restarted from within the webinterface as follows:
-
Navigate to:
https://jenkins.jklug.work/safeRestart
-
Click “Restart”
Links #
# Official Documentation
https://www.jenkins.io/doc/book/installing/