Automated docker container building in Jenkins.

Alex's Guardian > Blog > Homelab Things > Automated docker container building in Jenkins.
A picture of the Jenkins dashboard that shows a list of projects

The Problem:

I use Caddy Server as my reverse proxy behind Cloudflare to host all of my services (including this website). Caddy makes it super easy to setup a webserver and obtain SSL certs as it includes Let’s Encrypt cert generation by default.. Un-like Nginx.. Though Caddy does not, by default, ship with DNS provider plugins. Thankfully abiosoft/caddy-docker image exists and provides a builder to build custom images with different plugins!

The Solution:

Automate the process of building my custom Caddy Server image with my needed plugins via Jenkins as the docker hub automated building sucks…

Thus our journey begins…

I was originally using docker hub to do this along with a cloned abiosoft/caddy-docker repo and build hooks. Though this was super annoying to keep up-to-date so I went looking for a different solution. Which ended up being Jenkins. #automation

First I had to get Jenkins up and running. For this I decided to deploy Jenkins as a docker container on my main docker system.

Using docker containers allows you to separate application dependency bloat from your host OS as each container contains all of the necessary services for your desired application. It also gives you finer control over application security.

docker run --name=jenkins -p 8080:8080 -p 50000:50000 -v /var/run/docker.sock:/var/run/docker.sock -v jenkins:/var/jenkins_home/ -e TZ=America/New_York jenkins/jenkins

After the image downloads and the container is running we can access the Jenkins UI via http://hostname:8080 and run through the setup process wizard. Once Jenkins is setup and configured, we need to install Docker into the Jenkins container so we can build images. To do this we need to enter the Jenkins container CLI.

docker exec -it jenkins bin/bash

Then install Docker

apt-get update && \
    apt-get -y install apt-transport-https \
    ca-certificates \
    curl \
    gnupg2 \
    software-properties-common && \
    curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
    add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
    $(lsb_release -cs) \
    stable" && \
    apt-get update && \
    apt-get -y install docker-ce

After Docker is installed you can exit the CLI and open the Jenkins web UI. Once the dashboard loads navigate to the plugin install page (Manage Jenkins > Manage Plugins) and click on the available tab. In the filter bar search/install the following plugins:

  • Docker Plugin
  • CloudBees Docker Build and Publish plugin
  • Git

After you install these plugins you will need to restart Jenkins if you did not have it auto restart.

Setting up the build job.

First we need to add our Git account as a Jenkins credential. This can be done via http://jenkins:8080/credentials/store/system/domain/_/newCredentials. For my purposes I just left the git credential as a globally accessed one since I am the only one using this instance of Jenkins. If you have 2FA enabled on your account you will need to use a PAT. If you plan to upload this image to docker hub you will need to create another credential for it as well.

Now that we have our credential(s) setup we can get the build job configured for our image. For this post I will be using my Caddy Server image as an example. On the main dashboard for Jenkins click New Item > Freestyle project. Give it a name and click OK. Now enter the following info for your build job:

In Source Code management section:
Repository URL: https://github.com/abiosoft/caddy-docker.git
Credentials: Your Git credentials
Branch: */master
Repository Browser: Auto

In Build Triggers:
Poll SCM: Checked
Schedule: H */5 * * *

In Build Environment:
Add timestamps to Console Output: Checked

In Build:
Enable Docker Build and Publish
Repository Name: alexandzors/caddy (Change this to match your image)
Tag: latest (Image tag)
Docker Host URI: unix://var/run/docker.sock
Registry Credentials: Docker hub credentials
Advanced:
– No Cache: Checked
– Force Pull: Checked
– Create fingerprints: Checked
– Skip tag as latest: Checked (If you did not specify a tag above)
– Build Context: Folder the container should be built from. (Usually same as the Dockerfile location.)
– Additional Build Arguments: include any build args for the image (e.g. --build-arg plugins="plugin,plugin")
– Dockerfile Path: ./Dockerfile (location of the Dockerfile in the git repo)

Click Apply and you now have a build job configured.

Now that your build job is configured you can kick off a build by returning to the main dashboard of Jenkins, clicking on your new job, and clicking build now. You will see activity in the build history column which indicates your build is in progress, completed, or failed. You can monitor the build in real-time by clicking on the build in the history column and then clicking on Console Output.

Optional Build steps.

I actually have my docker images building on a different machine than Jenkins. I do this by exposing the docker API via TCP on the build machine, and then tell Jenkins to use the TCP socket as the Docker Host URI.

After the build is complete, I use the Execute shell script on remote host using ssh plugin to execute the command yes | docker image prune. This removes all dangling docker images which frees up space on the host (aka garbage collection).

If you want to build Jenkins with Docker already installed, I have a Dockerfile hosted in my git repo here.