Building Docker Images in a Jenkins Pipeline with a DIND Jenkins Agent

This post describes on how to reliable build Docker images on a central build infrastructure using Jenkins. It will describe how to configure the Jenkins, how to build a Jenkins Agents with a Docker-in-Docker-Approach, run the Agent of a separate hardware and use a descriptive pipeline for the Job.

For a better overview:

Configure „Cloud“ Machine

The Cloud machine is our separate VM where we would like to run the Docker agents. This will shift the load from the Jenkins master to the separate box. 

In our case this second VM is running on a HP Z240 Workstation PC and its the only VM running running on this ESXi host.

Preconditions

The Pre-Conditions are an installed Linux of your choice (we go with CentOS 7.5 (Systemd)) and Docker.

You should be a little bit experienced using Docker and Jenkins. 

Configure Docker

Normally the Docker Daemon only accepts conenctions from the localhost. We need to configure the Daemon to also allow tcp connections from any host. The official documentation with all the security relevant topics (https and firewall) can be found here:

Be aware of the differences between unix systems that are using Upstart or Systemd. The configuration is different between those!

To configure the daemon create a new configuration file

This configuration basically allows TCP connections on port 2375 and also local connections. The port 2375 is the default port for insecure connections to the Docker daemon.

Reload the environment and restart the service

After this you should be able to see a listening connection using netstat -an

To verify that the connection is working you can simply to a telnet on that port. The Docker daemon reponse with some HTTP status code.

Create the Docker-In-Docker-Agent

We would like to build our Docker Images inside a Docker Container. This is the so called Docker-In-Docker-Approach. 

Example of a DIND Dockerfile

Example of a DIND Dockerfile with Proxy, SSH Key, Repository-Login and authorized_keys file

  1. We need a proxy 
  2. We are adding an own private/public key pair. 
  3. We add the config.json file that contains the login credentials to our Nexus3 repository
  4. We add the public key from our Jenkins box that should be able to connect to this Docker Agent

config.json

Build and Upload the Image

This is the last time you have to build the image and upload it manually. After you are done with this tutorial you can use your CI/CD pipeline to build the image.

Configure Jenkins

Install Plugin

This „Cloud-Provider“ plugin must be installed

Configure the Cloud

Go to „Manage Jenkins“ → „Configure System“ → Scroll down till the end

Configure your „Docker Host URI“ to point to your machine that is running the Docker daemon where the container should be started. We are using IP instead of hostname here to prevent some proxy configuration. Authentication is not required. Click „Test Connection“ to verify that the connection is working.

Configure the Docker Agent Template

  1. Tick the box „Expose DOCKER_HOST“ so that Jenkins knows that new Docker containers should be started against that daemon.
  2. Assign a label „agent-dind“ to the Template. This label is later used in the Job configuration.
  3. Choose the image that the Docker Agent should use. Thats the image we’ve build and uploaded before
  4. Connect Method must be „Connect with SSH“ and use „Inject Key“. UserId must be „Jenkins“
  5. Done

Pipeline

The pipeline that we are using makes some assumptions and some things i would like to explain.

  1. The label of the agent inside the pipeline is reffering to the label that you have configured in the „Docker Agent Templates“.
  2. The Pipeline assumes that the Docker Image should have the same name like the GIT repository. This is a convention we are using. You can override the Image name with a parameter
  3. The tagging is done with the name of the feature branch, and the prefix „feature/“ is removed. Example: My project has the repository Name „myproject“ and i am currently on branch „feature/1.2.3“. The image that is generated will have the name „myproject:1.2.3“. If i would work on the master-branch instead, it will get the the tag „myproject:latest“ and also „myproject:<BUILD_NUMBER“. 

Check in your Code

Our GIT repo looks like

Shared Library Approach

Create the Shared Lib

We are using Shared Librarys to make our Jenkins Pipelines available in the whole Jenkins instance. This has the advantage that our Jenkinsfiles are very plain.This is not something we are doing because of this DIND approach but 

Of course you dont have to use a Shared Library and can have the Pipeline Code slightly modified in your GIT repository.

Inside that „ps-jenkinsfile.git“ repository we have subfolder „var“ and inside that folder there is file called „buildDockerImage.groovy“. The file looks like:

Jenkinsfile

The Jenkinsfile is plain and only contains. This referes to the Global Shared Library that ins configured in Jenkins.

Pipeline directly in the Jenkinsfile

If you don’t want to use a Shared Library you can easily copy the complete Script in here, but slightly modified:

Create a Pipeline Job

Now we need to create a new Job. The Docker image we want to build can be any image you like. Use the Jenkins UI to create a new Freestyle Job or even a „Multibranch Pipeline Job“. The Job for us simply now looks like this.

We only give it the URL to our GIT repo and thats it.

Build The Job

If you trigger the Job now, you can see that there is a Docker Agent started on demand:

Build Output

Schreibe einen Kommentar