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…

Categories:

Updated: