Docker – Building Docker images inside of a Dockerized Jenkins server

Install PHP on CentOS 8

Alright. Now you know how to deploy Jenkins as a Docker container, but we really want to be able to use Jenkins to build Docker images, as we did in the standalone deployment of Jenkins. To do that, we could deploy the same Jenkins image, and exec into it and install Docker and could probably get it to work, but we don’t need to go to that much trouble. We’re not the first pioneers to go down this road. There are several Docker images that have been created to do just what we are looking to do. One such image is h1kkan/jenkins-docker:lts. You can read about it by following the link in the following References section, but for now just know that it is an image that has been set up as a Jenkins server, and has Docker already installed in it. In fact, it also has Ansible and the AWSCLI pre-installed so you can do more than just build Docker images using it.

To begin, we will create a location on the Docker host to mount a Docker volume to store and preserve the Jenkins configuration. If you are using the same Docker host as you used in the previous section, you should already have created your folder and assigned ownership of it to ID 1000. If not, the following are the commands you use to do so:

# Setup volume location to store Jenkins configuration
mkdir $HOME/jenkins_home
chown 1000 $HOME/jenkins_home

Also, if you haven’t done so already, you can use the docker container stop jenkins command to stop (and remove) the Jenkins container that we created in the previous section to clear the way for our new and improved Jenkins server. When you are ready to create the new container, you can use these commands:

# Deploy a Jenkins server that is configured to build Docker images
docker container run -d -p 8080:8080 -p 50000:50000 \
-v $HOME/jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
--name jenkins --rm h1kkan/jenkins-docker:lts

# Start the Docker service in the Jenkins docker container
docker container exec -it -u root jenkins service docker start

You will have noticed a couple of differences in this code block. The first is the use of a second volume. This is a well-known trick, of sorts, that allows a container to issue Docker commands to its host. This essentially allows what is known as Docker-in-Docker. The next difference is an extra Docker command that will start the Docker service inside the running container. Because each container starts up with a single process, having both a Jenkins server process and a Docker daemon running requires this extra step.

Once the Docker service has started within the Jenkins container, you are all set to create new Jenkins jobs that use and build Docker images. You can test it out yourself by recreating the second example above, hello-docker-test, in your new Jenkins server. And since we are using the Docker volume mounted on the host at $HOME/jenkins_home to store our Jenkins data, this should be the last time you need to create this job.

This is all working wonderfully, but you may recall from Chapter 7, Docker Stacks, that we have a better way to deploy apps than by using the docker container run command, namely using Docker stacks. So would you like to see our example re-imagined as a Docker stack? Me too! OK then, let’s do it.

First off, use the container stop command to stop your current Jenkins container. It will leave behind the jenkins_home folder with our Jenkins server’s data, but if for some reason you skipped ahead to this part of the chapter and haven’t created that yet, the following are the commands to use:

# Setup volume location to store Jenkins configuration
mkdir $HOME/jenkins_home
chown 1000 $HOME/jenkins_home

Again, if you did those two commands for one of the previous examples, and you are using the same Docker host, you don’t have to do that again because the folder already exists and has the right ownership.

Next, you need to create a compose file for our Jenkins stack. I called mine jenkins-stack.yml and entered the following YML code into it:

# jenkins-stack.yml
version: "3"
    image: h1kkan/jenkins-docker:lts
       - 8080:8080
       - 50000:50000
       - $HOME/jenkins_home:/var/jenkins_home
       - /var/run/docker.sock:/var/run/docker.sock
       replicas: 1
         condition: on-failure
      constraints: [node.role == manager]

    image: registry
       - 5000:5000
    replicas: 1
      condition: on-failure

You will notice that we are creating two services; one is our Jenkins server, and the other is a Docker registry. We will use the registry service in an upcoming example, so keep that in your back pocket for now. Looking at the Jenkins service description, there is nothing we did not see already in Chapter 7, Docker Stacks, when we learned about Docker stacks. You will notice our two port mappings and the two volumes that were used in the last example. We are confining the single Jenkins replica to our manager node.

Remember that to use Docker stacks we have to be running in swarm mode, so if you have not done so already, create your swarm with the docker swarm init command that we learned in Chapter 5,  Docker Swarm

Understand that if your swarm has more than one manager node, you will need to further confine the Jenkins replica to just the single manager that has your jenkins_home volume mount point. This can be accomplished with a combination of roles and labels. Alternatively, you can use a storage driver and mount a volume that can be shared among swarm managers. For simplicity, we are assuming a single manager node for our example.

Now use the stack deploy command to set up the Jenkins application. The following is an example of the command to use:

# Deploy our Jenkins application via a Docker stack
docker stack deploy -c jenkins-stack.yml jenkins

Once the stack is deployed and the services up and running, you can browse to any node in your swarm, on port 8080, and get to your Jenkins server. What’s more, if you are reusing the jenkins_home folder from our previous example, you will not have to supply the admin password, create a new user, and select your plugins because all of the data related to those tasks was stored in the jenkins_home folder and is reused now by your stack-based Jenkins service. One more interesting note is that you do not have to start the Docker service when you use this image in a stack application. Bonus!

OK, we now have a sweet stack-based Jenkins service that is capable of using and building Docker images. Everything seems right with the World. But there is one thing that could make this better. And by better, I mean more Docker-y: instead of using the normal Jenkins agents for our build jobs, what if we wanted to spin up a new, pristine Docker container to use for each execution of our Jenkins jobs? This would ensure that every build was built from scratch in a clean, consistent environment. Plus, it really takes the Docker inception level up a notch, so I like it a lot. If you want to see how it’s done, keep reading.

Comments are closed.