Let's imagine the following scenario:
You needed to implement some CI/CD for your project and since you have some knowledge about docker and its advantages you thought "You know what, I'm going to run Jenkins as a docker container"! And you did and were able to create a pipeline with build steps and everything was going wonderfully.
Then you realised that maybe you want to build a docker image from within Jenkins, so you added a shell script to do so. But Oh-oh! Docker is not recognized and your build fails.
First Scenario : You have a Jenkins Docker container that can't run docker and you want to add docker to it.
Step 1 : Install Docker
In this scenario we will solve two issues. The first one is how to run docker inside the Jenkins container (Docker out of Docker). And the second one how to do so without losing your existing Jenkins configuration and pipelines.
Let's assume you are using RHEL or Centos, first you need to install docker, if you are using a different Linux distrubtion check the documentation from docker on what commands to run to install it, it is pretty easy to read and straightforward :
sudo -i
whoami # Should show "root"
- Run yum update
yum -y update
- Install docker
yum install -y yum-utils
Note: Notice that this repo is a centos repo while we are using Rhel, the reason is because docker is not supported for some versions of RHEL but centos's docker works fine on all version of Rhel
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin
- Check if docker is installed :
docker --version
Step 2 : Run Jenkins container :
To pull the jenkins image and run it as a docker container use the following command
docker run --name jenkins -p 8080:8080 -p 50000:50000 -d --restart=on-failure -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts-jdk11
Explanation :
--name jenkins : We are giving a name to this container so we can distinguish it easily. You can name it jenkins or whatever you want.
-p 8080:8080 -p 50000:50000 : We are mapping and exposing the ports here. Jenkins runs on port 8080 by default but we can map it to whatever port we want. So for example if you use -p 80:8080 you will be able to access Jenkins through port 80.
-d : To run the container in detached mode (Or in the background)
-v jenkins_home:/var/jenkins_home : Adding the jenkins_home volume.
jenkins/jenkins:lts-jdk11: The Jenkins image we want to run from dockerhub repository. Repository owner: jenkins. Repository name: Jenkins. Version or tag is : lts-jdk11.
docker logs jenkins
Output:
Copy the password and head to : http://yourdomain.com:8080
Create credentials:
Create a simple Jenkins job
Now Jenkins is Setup correctly. Click on add new Item to add our first Jenkins job to test if docker works. It is supposed to fail. For the sake of simplicity we will start a FreeStyle project :
Put the following in the shell command which will allow us to verify if docker exists or not inside our Jenkins :
docker --version
docker ps -a
Run the build/job
The build fails as expected
Let's see the logs of the build to verify that it was caused by missing docker :
Step 3 : Create a new Jenkins container from the existing container
This step is not necessary, but I will put it here in case you already customized your current Jenkins container and want to keep the customizations.
What we will actually do is create an image from the existing container and run a container from the image we created.
Basically we want to :
- Get the image of the current Jenkins container we are running
- Create a new image out of it (a copy)
- Create a New Jenkins container from this new image.
docker commit jenkins my-jenkins-image
Explanation of the command :
- jenkins : this is the name of our jenkins container. You can see its name by running "docker ps" or "docker ps -a" if it's not running.
- my-jenkins-image: The name of the output image. The image that we want to create from the existing container.
Result :
Congratulations. Now you have a "copy" of the old Jenkins image that you can use to create a new Jenkins container that keeps the customization.
Step 4 : How to keep the old configuration and files of the old Jenkins.
Before we create a new Jenkins container that will be able to run docker commands inside it we want it to have the configuration and the files of the old Jenkins container. If we don't do this we will have to reinstate Jenkins, enter the master password again, set credentials, define pipelines ... We don't want to do that even in a small project, but if it's an entreprise grade project well, we definitely don't want to do that.
In order to do that we will use volumes. What are docker volumes? Well the simplest explanation is that they allow you to connect and share the content of two directories. It's actually a bit more complex than that but this concept is sufficient for you to understand what they are and what are their usages.
As an example let's say you and your friend have 2 computers. Your compuer is named A, and their computer is named B. You both have a folder called MyFolder which is empty.
Now you want to connect the MyFolder on computer A to MyFolder on computer B. In a way that whatever files are in MyFolder in computer A will be in MyFolder in computer B.
Computer A
My Folder
- hello.txt
Computer B
My Folder
- hello.txt
Now if you create a new file in My Folder on Computer B called goodbye.txt, it will "magically" appear on computer A as well !
Computer A
My Folder
- hello.txt
- goodbye.txt
Computer B
My Folder
- hello.txt
- goodbye.txt
So the two folders are basically interlinked.
In our case what we want to do is mount the configuration files that are in the old (current) Jenkins container and mount them to the new Jenkins container we want to create.
Copy the Jenkins configuration files from the old Jenkins container to a local folder on your host machine:
docker cp jenkins:/var/jenkins_home /usr/jenkins-data
Explanation:
jenkins:/var/jenkins_home : From container named "jenkins", copy the directory "/var/jenkins_home"
/usr/jenkins-data: Where we want to copy this directory into
Check if the data was copied :
ls /var/jenkins_home # Should not be empty
Create the new Jenkins that can run docker
Congratulations. You created an image from the old jenkins container and you copied the Jenkins data, which you will use to create a new Jenkins container that is exactly the same as the old one with the added benefit of it being able to run docker.
Stop the current Jenkins container
docker stop jenkins
Create new jenkins container that can run docker
docker run -u 0 --name jenkins-docker -p 8080:8080 -p 50000:50000 -d --restart=on-failure -v /var/run/docker.sock:/var/run/docker.sock -v /usr/jenkins-data:/var/jenkins_home -v $(which docker):/usr/bin/docker my-jenkins-image
Check new container logs. It shouldn't show any password :
docker logs jenkins-docker
Go to your http://yourdomain.com:8080
Notice that we are asked to log in and not to set up Jenkins even though we are running a new Jenkins container and not the old ones. This means that the configuration from the old Jenkins container is copied successfully.
We also have the old job that we created.
Now build the job
You see that the build is successful now
See the logs :
Important: Notice how when we run docker ps -a
we are getting a list of the containers of the docker that is installed on the Host machine! This is very important. Jenkins is now able to run not an installation of docker inside the jenkins container, but the installation of docker on the host itself.
Basically running "docker ps -a" from a Jenkins shell script is the exact same as running it from your server's SSH with a root account.
I hope this tutorial was helpful, it's my first time writing tutorials so there are some things to improve. Do not hesitate to leave any feedback.
Top comments (0)