[Guide] Set up Jenkins as a build server for Spring Boot on a Raspberry Pi

In our cluster setup we wanted to be able to deploy Spring Boot microservices in Kubernetes cluster. This guide will show how we installed and configured Jenkins on a Raspberry Pi running Raspbian, but it wouldn't be much different running it on HypriotOS. To see how to use Spring Boot see our Spring Boot post, and check out the post about Spring Boot on Kubernetes as well.

As mentioned in the post with Kubernetes, Raspberry Pis run on ARM architecture, which means that you need to either compile Docker images on the Raspberry Pi or cross-compile from your computer. Since Spring Boot runs on the JVM, you could copy the JAR-file to the Raspberry Pi, build a docker image, and update your Kubernetes cluster... But it wouldn't be as funny as automating everything. In this post, we will show how to make Jenkins look for updates in your Git repository, run your tests, build a docker image, push the image to DockerHub, and finally do a rolling-update in your Kubernetes cluster.

Installing Jenkins

We had some issues with the versions of Java, but choosing Java 8 seemed to solve it. Type in the following command and select jdk-8 as your java choice.

$ sudo update-alternatives --config java
There are 2 choices for the alternative java (providing /usr/bin/java).

  Selection    Path                                                   Priority   Status
------------------------------------------------------------
  0            /usr/lib/jvm/java-7-openjdk-armhf/jre/bin/java          1063      auto mode
  1            /usr/lib/jvm/java-7-openjdk-armhf/jre/bin/java          1063      manual mode
* 2            /usr/lib/jvm/jdk-8-oracle-arm32-vfp-hflt/jre/bin/java   318       manual mode

Press enter to keep the current choice[*], or type selection number: 2  

Jenkins can be installed using apt-get, which also can be seen in the official guide.

$ mkdir jenkins && cd jenkins
$ wget -q -O - https://jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
$ sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
$ sudo apt-get update
$ sudo apt-get install jenkins

This is actually it. If it doesn't start you can run sudo /etc/init.d/jenkins start.
Now you can access the Jenkins user interface at port 8080.
If that port is already used, you can change the port (e.g. to 8000) by making the following two optional changes.

$ sudo nano /etc/init.d/jenkins

At the line below 'DAEMON_ARGS...' specify your port in the following way:

HTTP_PORT=8000  
JENKINS_ARGS="--httpPort=$HTTP_PORT"  

Afterwards, go to line 108 (roughly) and change 8080 to your new port. It should look like this:

check_tcp_port "http" "$HTTP_PORT" "8000" || return 2  

To make sure the change happens, you can restart Jenkins.

sudo /etc/init.d/jenkins restart  

After the restart Jenkins starts automatically.

Installing Docker

Later in the guide Docker is needed to deploy our Spring Boot applications, so if you don't already have Docker installed, the Hypriot crew can help you out.

$ curl -sSL http://downloads.hypriot.com/docker-hypriot_1.8.1-1_armhf.deb >/tmp/docker-hypriot_1.8.1-1_armhf.deb
$ sudo dpkg -i /tmp/docker-hypriot_1.8.1-1_armhf.deb
$ rm -f /tmp/docker-hypriot_1.8.1-1_armhf.deb

Afterwards, set up permissions for yourself and Jenkins to run docker.

$ sudo sh -c 'usermod -aG docker pi'
$ sudo sh -c 'usermod -aG docker jenkins'
$ sudo systemctl enable docker.service

Reboot and run docker info to see that it is working.

$ sudo reboot
$ docker info

Setting up a Spring Boot project

Our setup is using Maven, so this is installed as well. This, unfortunately, takes around 90MB of disk space, but it works well with Spring Boot.

$ sudo apt-get install maven

Now Maven should be available by running mvn -version, and you should see something like the following:

$ mvn -version
Apache Maven 3.0.5  
Maven home: /usr/share/maven  
Java version: 1.8.0_65, vendor: Oracle Corporation  
Java home: /usr/lib/jvm/jdk-8-oracle-arm32-vfp-hflt/jre  
Default locale: en_US, platform encoding: ANSI_X3.4-1968  
OS name: "linux", version: "4.1.17-v7+", arch: "arm", family: "unix"  

To use the newer versions of Spring Boot, Java 8 must be installed and configured. If you haven't configured it already scroll up and see the instructions.

Now you need to go to your browser and visit your Raspberry Pi's IP on the port you use e.g. 192.168.1.100:8000.

If you see a similar page click "Create new jobs", write an item name and select "Maven project". When you are done click "OK".

You will be directed to the configure page and asked where Maven is installed.

This was installed in /usr/share/maven. Afterwards it's time to find your Java JDK. In our case it was located in /usr/lib/jvm/jdk-8-oracle-arm32-vfp-hflt.

With Maven and Java located, click "Save".

Now it's time to install the GitHub plugin. Click on the Jenkins logo in the top left corner, and then click:

  • Manage Jenkins
  • Manage Plugins
  • Available
  • Click the filter input field, and type GitHub

Now you should be able to find "GitHub plugin" as seen below
Click "Install without restart", and click the check-box on the next page to restart afterwards.

When it is done, click Jenkins in the top corner again and click on your project to start checking your Git repository. Click "Configure", and now you should see a Git field under "Source Code Management". Insert your Git URL and click "Save".

The preferred way to listen for updates would be a web-hook with push on change in the repository, but we are not in control of the firewall we are behind. ngrok might be able to fix this, but we haven't looked into it. The alternative to push is polling, so that's that we will do.
Scroll down to "Build Triggers" and select "Poll SCM". Under Schedule you can specify how often to pull. This is done with a CRON expression, and to check each fifth minute write the following:
H/5 * * * *

Click "Save" and check that your setup can build. You can click "Build Now" and watch the output by clicking the build under "Build history", and then "Console Output". It will take some time to download the Maven Packages (especially Spring), but it will get there.

No you are able to build your JARs on you Raspberry Pi, but the fun part is only to begin.

Automating everything

To control the Kubernetes cluster kubectl must be set up. Download and install it is shown below.

$ cd ~
$ mkdir kubernetes && cd kubernetes
$ wget https://storage.googleapis.com/kubernetes-release/release/v1.1.2/bin/linux/arm/kubectl
$ sudo cp kubectl /usr/local/bin/kubectl
$ sudo chmod 755 /usr/local/bin/kubectl

To set the right permissions and setup your Docker Hub account, you can log into your Raspberry Pi as the Jenkins user and afterwards login to Docker Hub. This is an easy way for a local setup, but there probably exists a Jenkins plugin that can handle authentication in a more fine-grained way.

First, change the password to something you can remember.

$ sudo passwd jenkins

Then log in as jenkins with your new password e.g. ssh jenkins@192.168.1.100, and run docker login. You will need a user at Docker Hub for this.

Afterwards you can add your Kubernetes master, and set it as the context (the name clusterN isn't important, just find something you can remember).

$ kubectl config set-cluster clusterN --server=http://192.168.1.N:8080
$ kubectl config set-context clusterN --cluster=clusterN
$ kubectl config use-context clusterN

Log out again an log in as pi again.

Now you should be ready to add the last step, which is the post build script. Go to your project and click "Configure", scroll down to "Post Steps" and select "Add post build step" followed by "Execute shell".

In the text area, add the script below. The script builds a new Docker image, pushes it to Docker Hub and deploys the new version to a Kubernetes cluster as a rolling update.

Replace rpicloud/spring-boot with your own repository on Docker Hub, and notice that kubectl rolling-update spring-boot ... refers to a replication-controller on clusterN called spring-boot. You can see how to set this up in our post about Kubernetes visualization.

echo "Containerizing the successful build"

docker build -t rpicloud/spring-boot:${POM_VERSION} .  
docker tag -f rpicloud/spring-boot:${POM_VERSION} rpicloud/spring-boot:latest

docker push rpicloud/spring-boot:${POM_VERSION}  
docker push rpicloud/spring-boot:latest

docker rmi rpicloud/spring-boot:${POM_VERSION}  
docker rmi rpicloud/spring-boot:latest

kubectl config use-context cluster31  
kubectl rolling-update spring-boot --image=rpicloud/spring-boot:${POM_VERSION} --update-period=5s --timeout=15m  

The ${POM_VERSION} refers to the version number from the Spring Boot project. Deploying as a part of the post build script will make the build hang until the deployment is done. It would be better to deploy independently afterwards, but that's up to the reader to do so.

Now you should be able to push something to your Git repository and see your Kubernetes cluster update. You can eventually set up a visualizer to see what is happening on your cluster.