In earlier posts, we explored how to run Jenkins inside a Docker container and how to use the Docker Plugin to execute pipeline jobs inside Docker containers. Today, we will take this one step further with a practical guide on connecting your Docker host to the Jenkins container, enabling Jenkins to control Docker containers directly using the Docker CLI and Docker socket.
This setup is especially useful if you are running Jenkins on your local machine or a virtual server with Docker and want Jenkins to manage containers on the same Docker host.
1. Prerequisites
Before we dive into the details, make sure you have the following:
- A virtual machine or server with Docker installed (for this example, we use Ubuntu Server).
- Jenkins running inside a Docker container.
- If you haven't set this up yet, check my previous guide on running Jenkins in Docker.
- Administrative access to the host machine where Docker is installed.
- Basic understanding of Docker concepts like sockets and bind mounts.
2. Understanding the Problem
When Jenkins runs inside a Docker container, it is isolated from the Docker host. This means that Jenkins cannot interact with the Docker daemon directly unless we explicitly expose the Docker socket and provide the Docker CLI.
Here’s what we want to achieve:
- Connect the Docker socket of the host to the Jenkins container so Jenkins can communicate with the Docker daemon.
- Install the Docker CLI inside the Jenkins container to allow it to execute Docker commands.
By achieving this setup, Jenkins (running inside the container) will have access to the host Docker daemon, enabling it to run Docker commands and manage containers seamlessly.
3. Why Use the Docker Socket?
The Docker socket (/var/run/docker.sock) is a Unix socket used by Docker to listen for API requests. By mounting this socket into your Jenkins container:
- Jenkins can communicate with the Docker daemon running on the host machine.
- Jenkins jobs can start, stop, and manage other containers as needed.
However, there are security concerns with exposing the Docker socket, which we’ll address later in this post.
4. Steps to Connect Docker Socket and CLI to Jenkins
Step 4.1: Stop Your Existing Jenkins Container
If you already have Jenkins running inside a Docker container, stop it before making changes.
docker stop jenkins
docker rm jenkins
Step 4.2: Run Jenkins Container with Docker Socket
To connect the Docker socket, you need to bind-mount the socket file (/var/run/docker.sock) from the host into the Jenkins container.
Here’s the updated docker run command:
docker run -d --name jenkins \
-p 8080:8080 -p 50000:50000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v jenkins_home:/var/jenkins_home \
jenkins/jenkins:lts
Explanation of the Command:
-v /var/run/docker.sock:/var/run/docker.sock– This mounts the Docker socket from the host into the container. Jenkins can now communicate with the Docker daemon.-v jenkins_home:/var/jenkins_home– Persistent storage for Jenkins configurations and jobs.-p 8080:8080– Exposes Jenkins on port 8080.jenkins/jenkins:lts– The official Jenkins LTS Docker image.
Certainly! Here's an updated Step 4.3 that includes an alternative option to mount the Docker binary directly into the container.
Step 4.3: Install Docker CLI Inside Jenkins
At this stage, you have two options for enabling the Docker CLI inside the Jenkins container:
Option 1: Install the Docker CLI Inside the Container
This approach installs the Docker CLI directly into the Jenkins container. Use this if you want Jenkins to have the full Docker client locally.
Test access to the Docker daemon:
docker ps
If the Docker socket is correctly mounted, you should see a list of running containers on the host machine.
Verify the Docker CLI installation:
docker --version
Install the Docker CLI (for Ubuntu-based images):
apt-get update
apt-get install -y docker.io
Access the Jenkins container:
docker exec -it jenkins bash
Option 2: Mount the Docker Binary From the Host
Instead of installing the Docker CLI in the container, you can directly mount the Docker binary from the host machine into the container. This ensures that Jenkins uses the same version of the Docker CLI as the host.
Here’s how to update your docker run command:
docker run -d --name jenkins \
-p 8080:8080 -p 50000:50000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/local/bin/docker:/usr/local/bin/docker \
-v jenkins_home:/var/jenkins_home \
jenkins/jenkins:lts
Explanation:
-v /usr/local/bin/docker:/usr/local/bin/docker– This mounts the Docker CLI binary (/usr/local/bin/docker) from the host into the container.- By doing this, the Jenkins container can run Docker commands without installing the CLI locally.
Which Option to Choose?
- Option 1 (Install CLI): Suitable for containers that need their own isolated Docker CLI installation. This is better for flexibility but adds a dependency.
- Option 2 (Mount Binary): Faster and ensures the Jenkins container always uses the same Docker version as the host. However, it introduces a dependency on the host’s Docker binary.
Both approaches work seamlessly when combined with the Docker socket mount.
Once you’ve set up either option, proceed to verify Docker access:
docker exec -it jenkins docker ps
This should list the running containers on the host.
5. Configuring Jenkins to Use Docker
Now that Jenkins can access Docker, you can configure Jenkins jobs to use Docker commands.
Step 5.1: Verify Docker Access
In the Jenkins UI, navigate to Manage Jenkins > Script Console. Run the following script to verify Docker access:
println "Testing Docker connection..."
def result = "docker ps".execute().text
println result
You should see the list of running containers on the host.
Step 5.2: Install Docker Pipeline Plugin
To integrate Docker into your pipelines:
- Go to Manage Jenkins > Manage Plugins.
- Search for Docker Pipeline Plugin and install it.
- Restart Jenkins to apply the changes.
6. Writing a Jenkins Pipeline Using Docker
Here’s an example of a Declarative Jenkins Pipeline that uses the Docker CLI to build and run a container:
pipeline {
agent any
stages {
stage('Build Docker Image') {
steps {
script {
sh 'docker build -t myapp-image .'
}
}
}
stage('Run Docker Container') {
steps {
script {
sh 'docker run -d --name myapp-container -p 8081:80 myapp-image'
}
}
}
stage('Cleanup') {
steps {
script {
sh 'docker stop myapp-container && docker rm myapp-container'
}
}
}
}
}
Explanation:
- Stage 1 builds a Docker image from a Dockerfile in your project directory.
- Stage 2 runs the built image as a container.
- Stage 3 stops and removes the container after the job is complete.
7. Security Considerations
Mounting the Docker socket into a container comes with significant security risks. If Jenkins is compromised, attackers can use the Docker socket to control your entire Docker host.
Recommendations:
- Restrict Jenkins container access to trusted users.
- Use role-based access control (RBAC) in Jenkins.
- Consider running Jenkins agents with isolated permissions instead of exposing the socket directly.
For production setups, tools like Docker-in-Docker (DinD) or running Jenkins agents on separate nodes are safer alternatives.
Conclusion
In this guide, we covered how to connect the Docker socket and Docker CLI to a Jenkins server running inside a Docker container. This allows Jenkins to manage containers on the host Docker daemon seamlessly. While this approach is convenient, always consider the security implications and apply best practices to secure your environment.
Now that you’ve set up Jenkins with Docker integration, you can build more powerful and flexible pipelines for your CI/CD workflows!