Building a Custom Jenkins Docker Image - Part 1

by John Turner

Posted on February 14, 2017

Kubernetes, Docker, Jenkins

Last week I spent some time learning how to utilize Kubernetes and Jenkins to form the foundation of a build infrastructure. I documented some of those learnings in the posts below:

This is a pretty good start but having built out VM based build infrastructure a number of times in the past I’m well aware that there is a long way to go before I have something I can use, manage and maintain. When working with VM’s I’ve perviously chosen to use Chef to manage and maintain the Jenkins master and slave hosts. Typically, Chef facilitated automation of the installation and configuration of:

Java

Jenkins

Jenkins plugins

Jenkins jobs (using JobDSL)

Git

Apache Maven

To achieve the same level of automation with Docker and Kubernetes, I will need to be able to perform all of the above and distribute as a set of Kubernetes resource definition files and Docker image(s). Before we start doing anything meaningful, I want to disable the setup wizard because, after all, we will be automating the Jenkins setup. To do this I modify the Kubernetes deployment resource file to specify a JAVA_OPTS environment variable.

jenkins-deployment.yaml apiVersion : extensions/v1beta1 kind : Deployment metadata : name : jenkins spec : replicas : 1 template : metadata : labels : app : jenkins spec : containers : - name : jenkins image : jenkins:2.32.2 env : - name : JAVA_OPTS value : -Djenkins.install.runSetupWizard=false ports : - name : http-port containerPort : 8080 - name : jnlp-port containerPort : 50000 volumeMounts : - name : jenkins-home mountPath : /var/jenkins_home volumes : - name : jenkins-home emptyDir : {}

You can test this by creating the Kubernetes deployment and service as described in previous posts.

The next task is to replicate the installation of the recommended Jenkins plugins. To do this we must build our own docker image but first we will need to setup our local development environment.

Install the docker command line tool:

brew install docker

Configure your environment to use the Docker Host provided by minikube:

eval $( minikube docker-env )

I will create a working directory in which I’ll store source files. The name is not important but I will call it docker-library/jenkins. This is where I will maintain all my docker images (there’s ambition for you!).

In this directory I’ll create a Dockerfile. The first statement I’ll add is the FROM statement which tells Docker which image my image will be based on. Naturally I will base my image on the official Jenkins image.

from jenkins:2.32.2

The documentation for the official docker image details how to install additional plugins. I found this not to work with the latest version of the image but found more recent instructions on the associated github readme which I verified did behave as expected. I was able to install additional plugins by adding the statement below to the Dockerfile.

RUN /usr/local/bin/install-plugins.sh docker-slaves plugin-name:plugin-version

In order to mimic exactly the behavior of the install wizard I wanted to install the recommended plugins, versions and their dependencies. The wizard retrieves the recommended plugins from a json file available from the Jenkins GitHub repository.

Dockerfile from jenkins:2.32.2 # install plugins specified in https://github.com/kohsuke/jenkins/blob/master/core/src/main/resources/jenkins/install/platform-plugins.json # install Organisation and Administration plugins RUN /usr/local/bin/install-plugins.sh cloudbees-folder RUN /usr/local/bin/install-plugins.sh antisamy-markup-formatter # install Build Features plugins RUN /usr/local/bin/install-plugins.sh build-timeout RUN /usr/local/bin/install-plugins.sh credentials-binding RUN /usr/local/bin/install-plugins.sh timestamper RUN /usr/local/bin/install-plugins.sh ws-cleanup # install Build Tools plugins RUN /usr/local/bin/install-plugins.sh ant RUN /usr/local/bin/install-plugins.sh gradle # install Pipelines and Continuous Delivery plugins RUN /usr/local/bin/install-plugins.sh workflow-aggregator:2.0 RUN /usr/local/bin/install-plugins.sh github-organization-folder:1.6 RUN /usr/local/bin/install-plugins.sh pipeline-stage-view:2.0 # install Source Code Management plugins RUN /usr/local/bin/install-plugins.sh git RUN /usr/local/bin/install-plugins.sh subversion # install Distributed Builds plugins RUN /usr/local/bin/install-plugins.sh ssh-slaves # install User Management and Security plugins RUN /usr/local/bin/install-plugins.sh matrix-auth RUN /usr/local/bin/install-plugins.sh pam-auth RUN /usr/local/bin/install-plugins.sh ldap # install Notifications and Publishing plugins RUN /usr/local/bin/install-plugins.sh email-ext RUN /usr/local/bin/install-plugins.sh mailer

Lets build the docker image specifying the image name and version tag.

$ docker build -t monkeylittle/jenkins:1.0.0 .

Learn about what happens during the build process by reading the Docker getting started guide.

Verify the image was created and uploaded to the minikube local registry as follows:

$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE monkeylittle/jenkins 1.0.0 443d3e0a31f8 9 minutes ago 712 MB ...

It’s important that when building the image that a tag is specified. If not specified Docker will attempt and fail to find the image on DockerHub.

Now that we have our docker image available in the minikube local docker registry we can update our kubernetes deployment resource file to specify our newly created image.

jenkins-deployment.yaml apiVersion : extensions/v1beta1 kind : Deployment metadata : name : jenkins spec : replicas : 1 template : metadata : labels : app : jenkins spec : containers : - name : jenkins image : monkeylittle/jenkins:1.0.0 env : - name : JAVA_OPTS value : -Djenkins.install.runSetupWizard=false ports : - name : http-port containerPort : 8080 - name : jnlp-port containerPort : 50000 volumeMounts : - name : jenkins-home mountPath : /var/jenkins_home volumes : - name : jenkins-home emptyDir : {}

jenkins-service.yaml apiVersion : v1 kind : Service metadata : name : jenkins spec : type : NodePort ports : - port : 8080 targetPort : 8080 selector : app : jenkins

You can now create the kubernetes deployment (and service) and view the Jenkins UI in a browser to verify the default plugins have been installed. Next step is to automate the installation of the various required tools.

Please enable JavaScript to view the comments powered by Disqus.