Run Jenkins Master on Kubernetes Cluster
Such a lovely evening. It was a great sunny day near the Black Sea where I’m taking some time off with my family. Now it’s late enough to have a beer and… build a Jenkins master? Why not.
Today I’ll play with Kubernetes - one of my favorite toys lately. I don’t want to waste too much time building my cluster so I’ll use the pre-built package for MacOS - docker + kubernetes. It works great and it’s very simple to install.
Now since I promise you an easy time this evening let’s build our Jenkins master using Helm. That’s kind of a package manager for Kubernetes that you can download from github. It’s basically one binary that uses the context from kubectl which is used to control the cluster.
You’ll need to initialize a tiller (kubernetes agent running on pod over the cluster) and the helm (client) itself. Do that by simply running “helm init”. This process might be a bit more complicated if you’re running it over remote cluster since you’ll need to authenticate first but if you know how to setup remote cluster then that would be a piece of cake for you. So, let’s install Jenkins:
helm install --name jenkins --namespace jenkins stable/jenkins
Here’s how output from this command looks like:
$ helm install --name jenkins --namespace jenkins stable/jenkins
NAME: jenkins
LAST DEPLOYED: Sun Jun 24 21:58:07 2018
NAMESPACE: jenkins
STATUS: DEPLOYED
RESOURCES:
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
jenkins-agent ClusterIP 10.99.56.118 <none> 50000/TCP 1s
jenkins LoadBalancer 10.108.218.206 localhost 8080:31573/TCP 1s
==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
jenkins 1 1 1 0 1s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
jenkins-789554878-5jfkx 0/1 Pending 0 0s
==> v1/Secret
NAME TYPE DATA AGE
jenkins Opaque 2 1s
==> v1/ConfigMap
NAME DATA AGE
jenkins 4 1s
jenkins-tests 1 1s
==> v1/PersistentVolumeClaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
jenkins Pending hostpath 1s
NOTES:
1. Get your 'admin' user password by running:
printf $(kubectl get secret --namespace jenkins jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
2. Get the Jenkins URL to visit by running these commands in the same shell:
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc --namespace jenkins -w jenkins'
export SERVICE_IP=$(kubectl get svc --namespace jenkins jenkins --template " { { range (index .status.loadBalancer.ingress 0) }} { { . }} { { end }}")
echo http://$SERVICE_IP:8080/login
3. Login with the password from step 1 and the username: admin
For more information on running Jenkins on Kubernetes, visit:
https://cloud.google.com/solutions/jenkins-on-container-engine
$
Easy. Once the pod is deployed you can go to http://localhost:8080 and login with username admin and the password you see via this command:
printf $(kubectl get secret --namespace jenkins jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
Next I decided to build a very simple pipeline on the Jenkins:
node {
echo 'Hello World'
}
And here’s its console output:
Started by user admin
[Pipeline] node
Still waiting to schedule task
Waiting for next available executor
Running on default-20hkl in /home/jenkins/workspace/test
[Pipeline] {
[Pipeline] echo
Hello World
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
All it does is print “Hello World” without even setting up a stage. Now if you run the job you’ll notice the beauty of this setup right away. There is no executor at first but when you run the job a pod will be created automatically for you. When the executor running on this pod is created the job will run.
Now let’s look at some details of what’s going on. We created the Jenkins on a namespace called “jenkins”. Let’s see this namespace and all resources there.
$ kubectl get all --namespace jenkins
NAME READY STATUS RESTARTS AGE
pod/jenkins-789554878-5jfkx 1/1 Running 0 1h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/jenkins LoadBalancer 10.108.218.206 localhost 8080:31573/TCP 1h
service/jenkins-agent ClusterIP 10.99.56.118 <none> 50000/TCP 1h
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment.apps/jenkins 1 1 1 1 1h
NAME DESIRED CURRENT READY AGE
replicaset.apps/jenkins-789554878 1 1 1 1h
$
Notice that there is no executor pod at the moment. That’s because the executors don’t persist. They become available when there is demand from the Jenkins master. That’s one pretty good example of scaling your resources and killing them once there is no need for them to stick around.
Now to recap:
- Docker + Kubernetes on MacOS was used as initial cluster setup which is very easy to install. Same thing you can get on Windows as well. On Linux you’ll need to do some configuration but it’s pretty straightforward and lots of documentation is available to support you.
- We install Jenkins using Helm. It’s easy to start but it will take some time getting used to when you want to configure all the plugins and settings. Helm is using charts which is a way to describe the packages. Tiller is Helm’s agent in the cluster that we initialized.
- Then we ran a simple pipeline that says “Hello World” and Jenkins started another pod to run an executor so that our pipeline gets executed.
What happens next is up do you. You can build a Jenkinsfile to support your app and very easily deploy all its setup on the cluster. You can even build a whole CI/CD pipeline to promote your app through the different stages of its lifecycle.
That’s however a matter for another day…