Docker – Using Docker containers for your Jenkins build nodes

How to configure nginx for Magento 2

To use Docker containers for the Jenkins build agents, you need to do a few things to your Jenkins configuration:

  • Build a new Docker image that can act as a Jenkins build agent, and is capable of building Docker images (of course)
  • Push the new image to a Docker registry
  • Turn off the default Jenkins build agents
  • Install the Docker plugin for Jenkins
  • Configure a new cloud to enable Dockerized build agents

Building the Docker image

Let’s get started. The first thing we want to do is build our specialized Docker image that can be used for our Jenkins agents. To do this, we are going to use the skills we learned in Chapter 3, Creating Docker Images, to create Docker images. Start by creating a new folder on your development system, and then change your working directory to that folder. I named mine jenkins-agent:

# Make a new folder to use for the build context of your new Docker image, and cd into it
mkdir jenkins-agent
cd jenkins-agent

Now create a new file, named Dockerfile, using your favorite editor, enter the following code into it, and then save it:

# jenkins-agent Dockerfile
FROM h1kkan/jenkins-docker:lts-alpine
USER root
ARG user=jenkins

ENV HOME /home/${user}
ARG VERSION=3.26
ARG AGENT_WORKDIR=/home/${user}/agent

RUN apk add --update --no-cache curl bash git openssh-client openssl procps \
 && curl --create-dirs -sSLo /usr/share/jenkins/slave.jar https://repo.jenkins-ci.org/public/org/jenkins-ci/main/remoting/${VERSION}/remoting-${VERSION}.jar \
 && chmod 755 /usr/share/jenkins \
 && chmod 644 /usr/share/jenkins/slave.jar \
 && apk del curl

ENV AGENT_WORKDIR=${AGENT_WORKDIR}
RUN mkdir -p /home/${user}/.jenkins && mkdir -p ${AGENT_WORKDIR}
USER ${user}

VOLUME /home/${user}/.jenkins
VOLUME ${AGENT_WORKDIR}
WORKDIR /home/${user}

Here is what our new Dockerfile is doing: in our FROM instruction, we are using the same Docker image that we used in our Docker-in-Docker example above so that we have a base image that will allow us to build Docker images. Next, we use the USER command to set the current user to root. Next, we create an ARG named user and set it to a value of jenkins. After that, we set an environment variable named HOME that has a value for the Jenkins user’s home folder. Then, we set two more ARGs, one for the version and one for the Jenkins agent’s working directory. The next one is where the magic happens. We are using a RUN command to set up and curl the Jenkins slave.jar file. This is the bit that is required to run as a Jenkins agent. We also set some permissions on the folder and file, and then clean up a bit by deleting curl. After that, we set another environment variable, this one for AGENT_WORKDIR. Next up, we create a couple of folders in the container. Then, we use the USER instruction again, this time setting the current user to our Jenkins user. We round out the Dockerfile by creating a couple of VOLUME instances and, finally, we set the current working directory to the home directory for our Jenkins user. Phew! That seems like a lot, but really it’s not so bad, and all you have to do copy and paste the preceding code into your Dockerfile and save it. 

Now that we have our Dockerfile ready to use, it might be a good time to create a git repo and save your code to it. Once you are satisfied that your project has been properly set up with git, we can build our new Docker image. The following is the command you will use for that:

# Build our new Jenkins agent image
docker image build -t jenkins-agent:latest .

It should build successfully and create a locally-cached image tagged as jenkins-agent:latest

Pushing the new image to a Docker registry

Next, we need to push our new image to a Docker registry. Of course, we could push it to our repo within hub.docker.com, but since we have an application stack that just so happens to have deployed a Docker registry, why don’t we utilize it for our Jenkins agent image? First, we need to tag our new image with the registry. Your tag command will differ from mine based on the domain name of your Docker swarm, but for my example, the following is what my tag command looks like:

# Tag the image with our swarm service registry
docker image tag jenkins-agent:latest ubuntu-node01:5000/jenkins-agent:latest

Now that the image is tagged locally, we can push it to the registry with the following command; again, your command will be different based on the domain name of your swarm:

# Push the Jenkins agent image to the registry
docker image push ubuntu-node01:5000/jenkins-agent:latest

All of these commands might utilize a better version scheme than the oversimplified use of the latest tag, but you should be able to address that on your own. With our image built, tagged, and pushed to the Docker registry, we are ready to update our Jenkins configuration to use it.

Turning off the default Jenkins build agents

Now we are ready to update our Jenkins configuration to support our Dockerized build agents. The first configuration change we are going to make is to turn off the default build agents. To do this, log into your Jenkins server, and click the Manage Jenkins menu link. This will take you to a variety of configuration groups you can manage, such as system, plugins, and CLI settings. For now, we will need to go to the Configure System management group:

Once you are in the Configure System management group, you are going to change the value for # of executors to 0. It should look something like the following:

When you have changed the # of executors value to 0, you can go ahead and save the settings by clicking the Save button in the lower-left part of the screen. At this point, with this change in place, your Jenkins server will not be able to run any jobs because there are no Jenkins agents configured to run them. So let’s move on quickly to the next step, which is to install the Docker plugin.

Installing the Docker plugin for Jenkins

Now we need to install the Docker plugin for Jenkins. You accomplish this as you would other plugin installations. Click on the Manage Jenkins menu link, and from the list of configuration groups, click the link for the Manage Plugins group:

Once you are in the Manage Plugins configuration group, select the tab for Available plugins, and then in the filter box, type docker to narrow down the list of available plugins to those related to Docker:

Even with a filtered list, there are still a lot of plugins to choose from. Find and check the box for the Docker plugin. It looks like the following:

With the Docker plugin checkbox checked, scroll down and click the Install without restart button. This will download and install the plugin for you, and then enable it as soon as Jenkins restarts. On the install screen, you have the option to execute a restart as soon as the plugin is installed. To do this, check the Restart Jenkins when installation is complete and no jobs are running checkbox:

Since we set the # of executors to 0 a few minutes ago, there will not be any jobs running now, so as soon as the plugin is installed, Jenkins will restart. As soon as Jenkins comes back online, the plugin will be installed. We need to log back in to Jenkins and set up our Cloud.

Creating a new Cloud to enable our Dockerized build agents

Now we will tell Jenkins to use our custom Docker image to run containers as Jenkins build agents. Once more, click on the Manage Jenkins menu link. From the list of configuration groups, you will again click the link for the Configure System group. You will find the Cloud configuration near the bottom of the configuration options. Click on the Add a new cloud dropdown and select Docker:

The screen will update and you will have two new configuration groups: Docker Cloud details… and Docker Agent templates…:

Let’s take care of the Docker Cloud details first. Click on that button now. You can leave the Name value as the default for docker. In the Docker Host URI field, enter unix:///var/run/docker.sock. You can find this value by clicking the question mark help icon and copying and pasting it into the input field. Next, click the Test Connection button and you should see a version line show up, similar to the one you will see in the following screenshot. Make note of the API Version number as you will need it for the Advanced… setup. Click the Advanced… button and enter the API Version number in the Docker API Version field. You need to check the Enabled checkbox to enable this feature, so be sure to do so. Finally, you may want to change the number of containers that the system can run concurrently. The default is 100. For my example, I reduced the value to 10. When you are done, your configuration should look something like the following:

Next, click the Docker Agent templates… button and then click the Add Docker template button that appears so that we can to configure the Jenkins agent settings. Here, you will want to click the agent’s Enabled checkbox to enable our new agent template. You can give a name to use as the prefix for the containers that are run by Jenkins as the build agents, or you can leave the name blank and the docker prefix will be used. Next, enter the repository and the name tag for the image you want to use for the build agent containers. We created our custom image, tagged it, and pushed it to our Jenkins stack application repo using the ubuntu-node01:5000/jenkins-agent:latest image name, so enter that value into the Docker Image field. Set the Instance Capacity value to 1, and the Remote File System Root value to /home/jenkins/agent. Make sure the Usage value is set to Use this node as much as possible, and use the Attach Docker container value for the Connect method. Set the User to root. Change the Pull strategy value to Pull once and update latest:

Finally, we need to configure some Container settings…, so click to expand that section. The value we need to enter here is the command we want to use when the container is run. The value you need in the Docker Command field is java -jar /usr/share/jenkins/slave.jar. The value you need in the Volumes field is /var/run/docker.sock:/var/run/docker.sock:

And lastly, check the checkbox for Allocate a pseudo-TTY:

Scroll down to the bottom of the configuration screen and click the Save button to save all of the Cloud settings. That was some serious configuration Kung Fu—great job! However, just in case you want a quick reference for all of the values entered, here are all of the custom (or non-default) values entered to configure the Docker Cloud in our example:

Field name Value used
Docker Host URI unix:///var/run/docker.sock
Docker API Version 1.38 (match the version shown in the connection test)
Docker Cloud Enabled Checked
Container Cap 10
Docker Agent Enabled Checked
Docker Agent Template Name agent
Docker Image ubuntu-node01:5000/jenkins-agent:latest
Instance Capacity 1
Remote File System Root /home/jenkins/agent
Usage Use this node as much as possible
Connection Method Attach Docker container
User root
Pull Strategy Pull once and update latest
Docker Command java -jar /usr/share/jenkins/slave.jar
Volumes /var/run/docker.sock:/var/run/docker.sock
Allocate a pseudo-TTY Checked

 

Now that everything is configured, let’s give our newly-defined Jenkins agents a test.

Testing our new build agents

Head back to the Jenkins dashboard and click on the S chedule a Build button for our hello-docker-test job. This will start a new build for our job, which in turn will create a new Dockerized build agent. It uses the configuration we set to execute a docker container run command to run a new container based on the image we specified. Initially, the executor will be offline as the container spins up: 

Notice that the executor name has the agent prefix that we specified. Once the container is running, the Jenkins job will be initiated within it, essentially using the docker container exec command. When the Jenkins job has started, the normal job-progress graphic will display, and the executor will no longer show as offline. The status will then look something like this:

If you click on the progress bar of the executing job, you can view the job’s console output, and after a short while the job will show the finished: SUCCESS status, like the following:

A job well done! Let’s examine one last example Jenkins job to show a pipeline script that has more stages, and represents a real-world example of a Docker job. Are you ready? Read on.

Comments are closed.