Deploying Jenkins Docker Agents

Jenkins is ok. It's got the support of a huge swath of plugins, but leaves it up to you to figure out anything but Golden Path deployments. What I wanted was a Jenkins Agent in a Container that was able to handle the deployment of other containers on the Docker host.

This article elaborates on a lot of the issues that plague the Jenkins Agent docker image, but misses some of the steps to finally get you to your destination.


Prerequisites

Install Jenkins Plugins

Log in to your Jenkins console. Install the Docker Commons and Docker Pipeline plugins.

Prestage the Docker Agent

From your Jenkins console, go to Manage Jenkins -> Nodes. Add a new Node, making it a "Permanent Agent" type. After creation, specify the following to mandatory parameters:

  1. Remote root directory -> /home/jenkins/agent
  2. Launch Method ->Launch agent by connecting it to the controller

Select the newly created agent, and copy one of the command line configurations. We'll need the the following later:

  1. The Jar URL
  2. Docker Agent Name
  3. The Secret
  4. The Infom Url

For example, the following Unix installation steps:

curl -sO {JAR_URL_TO_SAVE}
java -jar agent.jar -url {INFORM_URL_TO_SAVE} -secret {SECRET_TO_SAVE} -name "{NAME_TO_SAVE}" -workDir "/home/jenkins/agent"

Docker Setup

Obtain GID

Connect to the Docker host you will be installing the Jenkins Agent on. Obtain the GroupID (GID) of the Docker group by running getent group docker .

Create Dockerfile

Create a file called Dockerfile on the Docker host. Use the following content for the file. Replace the URL on line 4 with the JAR_URL, and replace the number on line 6 with the GID obtained previously

FROM jenkins/inbound-agent
USER root
RUN apt update && curl -fsSL https://get.docker.com | sh
RUN curl -sO {JAR_URL}
RUN usermod -aG docker jenkins
RUN groupmod -g 993 docker
USER jenkins

Create the Image

Run the following command to build the image from the docker file.

docker image build -f jenkins-agent.Dockerfile -t jenkins-agent:custom .

Run the Image

Run the following command to deploy the agent. Replace the INFORM_URL with the inform URL saves earlier, replace the SECRET with the secret saved earlier, and replace the NAME with the name saved earlier.

docker run -d -v /var/run/docker.sock:/var/run/docker.sock --init jenkins-agent:custom -url {INFORM_URL} -workDir=/home/jenkins/agent -secret {SECRET} -name "{NAME}"

Consult the docker container logs if it is not running: docker logs {container_id}

Validate

Refresh the status of the newly created Jenkins Agent, it should be connected.


Pipeline Configuration

Ensure any pipelines specify the docker tools, so that the Jenkins agent has the commands available to interact directly with the Docker host.

Configure Docker Tools

In Jenkins, to go Manage Jenkins -> Tools. Navigate "Add Docker". Provide a Name, which will be used later, select "Install Automatically". Select "Add Installer", then select "Download from Docker.com". Save the page.

Specify Docker Tools in Pipelines

When using the agent, specify the tools installation, and provide the name of the DockerTools created earlier. See Example:

pipeline {
    agent { label 'docker-prod'} 
    tools { 
        dockerTool '{NAME}'
    }
    stages {
        stage('Build') {
            steps {
                sh 'unset DOCKER_HOST'
                sh 'docker ps'
            }
        }
    }
}

The example above runs unset DOCKER_HOST . This is precautionary to ensure the Docker agent utilizes the Docker socket instead of hostname and port.

Deploying code to multiple hosts

There are likely better ways to do this that scale, but I'm ok maintaining 1:1 relationship between agents and hosts.

pipeline {
    agent none
    stages {
        stage('Build to Hosts') {
            parallel { 
                stage('Docker-01'){ 
                    agent { label 'docker-prod-01'} 
                        tools { 
                            dockerTool 'docker'
                        }
                    stages{ 
                        stage('Build'){
                            steps {
                                sh 'unset DOCKER_HOST'
                                sh 'docker build -t ghost_prod:latest .'
                            }
                        }
                        }
                    }
                stage('Docker-02') {
                    agent { label 'docker-prod-02'} 
                        tools { 
                            dockerTool 'docker'
                        }
                    stages {
                        stage('Build'){ 
                            steps {
                                sh 'unset DOCKER_HOST'
                                sh 'docker build -t ghost_prod:latest .'
                            }
                        }
                    }
                }
            }

        }
    }
}