Lasse Flygenring-Harrsen

Lasse Flygenring-Harrsen

Student, HTX Roskilde

Creator of SkiftTilLinux.dk

© 2020

Using docker build agents in Jenkins

I am building various of my private and school projects using Jenkins, which so far has been a really good experience. I have been using a jenkins ssh agent on a Debian 10 LXC container as my build environment, but with my friends utilizing my jenkins instance, and more and more diverse projects being built, me having to manually install build dependencies and various languages is quickly becoming unsustainable.

I had some experience with Docker before starting this project, primarily using it to run various services when i used Unraid on my home server (I have since moved to Debian for my server and I am using LXC containers for most of my services, including jenkins), and I was aware that Jenkins had some kind of integration with it. So I wanted to figure out how I could use it to build various projects, without having to install any extra software on my host system.

The project

The project I am gonna be using as an example is a simple QT-based CAS tool that me and some friends are doing as our final project for Digital design and development (Final year A level course for HTX in Denmark). Which uses flex and bison in its build process. You can see the basic structure of the project below:

Diophant
├──[D] Diophant
│   ├──[D] Build
│   ├──[D] Forms
│   ├──[D] Headers
│   ├──[D] Objects
│   ├──[D] Other
│   ├──[D] Parser
│   ├──[D] Sources
│   ├── Diophant.pro
│   └── Makefile
├── DiophantSolution.pro
├── Jenkinsfile
└── Readme.md

And below you can see the existing jenkinsfile for the project, as you can see it’s not that complicated since we’re just running qmake, and then running GNU make on the makefile that qmake generates.

Jenkinsfile, pre-docker:

pipeline
{
	agent { node { label 'linux' } }

	stages
	{
		stage('Build')
		{
			steps
			{
				sh 'qmake'
				sh 'make'
			}
		}

	}

	post
	{
		success
		{
			archiveArtifacts artifacts: 'Diophant/Build/**/*', fingerprint: true
		}

		cleanup
		{
			cleanWs()
		}
	}
}

Jenkins setup

Since I already had a working Jenkins setup I just had to install Docker and configure the Jenkins docker integration. I installed docker by following the instructions here, and installed the docker plugin for jenkins.

Running Docker in LXC

To get Docker to run in LXC i had to add the following to the LXC containers configuration file. I’ll have to do some hardening of the host system in the future, and that will involve me reevaluating the security of this particular container.

lxc.apparmor.profile = unconfined
lxc.apparmor.allow_nesting = 1
lxc.cgroup.devices.allow = a

Connecting jenkins to docker

To ensure that jenkins pipelines utilizing docker are only run on hosts that actually have docker installed we can configure jenkins to assign a label implicitly to any job using any kind of docker functionality. In this case “docker”, note: You’ll have to assign the label to the host machine(s) manually. alt text Since I am running jenkins and docker on the same machine I am connecting through a UNIX socket and not tcp. So the Docker host URI will be: unix:///var/run/docker.sock, since we’re using a UNIX socket to connect to docker, we wont have to do any authentication besides adding the jenkins user to the docker group. alt text

Dockerfile

So the Debian packages necessary for building the packages are as follows:

  • build-essential, for the GNU toolchain make, gcc etc.
  • flex, lexical analyser.
  • bison, parser generator.
  • qt5-default, QT development files and qmake.

To get Docker to construct a Debian-based container with those packages installed, we can write the following Dockerfile, which basically just fetches a basic debian system and install those four packages inside it.

FROM debian:buster

RUN apt-get update
RUN apt-get install -y build-essential
RUN apt-get install -y flex
RUN apt-get install -y bison
RUN apt-get install -y qt5-default

Thus the only two changes to the projects VCS repository (in this case, a git repo on my own gitea instane) is adding the above Dockerfile to the root of the repository.

Diophant
├──[D] Diophant
│   ├──[D] Build
│   ├──[D] Forms
│   ├──[D] Headers
│   ├──[D] Objects
│   ├──[D] Other
│   ├──[D] Parser
│   ├──[D] Sources
│   ├── Diophant.pro
│   └── Makefile
├── DiophantSolution.pro
├── Dockerfile
├── Jenkinsfile
└── Readme.md

And changing the agent line of the Jenkinsfile to the following:

agent { dockerfile true }

Which will make jenkins look for a dockerfile in the repository. After which, it takes care of the rest.

Testing the pipeline

Now the jenkins pipeline is ready to go, and in my case it looks like it works:

alt text

Note, the first time the project is building after adding/changing the dockerfile the process will take a little longer due to docker not having the image cached. After that it’ll be basically the same as building without docker.

Conclusion

I believe Docker will simplify my use of jenkins over time, as i migrate all of the existing pipelines to using it. For further reading you can check the jenkins site.