How to run docker from a Jenkins docker container that is already running without docker
Tags: jenkins, docker, containerapps
Published: 2022-10-26 11:52:16

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 ](https://docs.docker.com/engine/install/centos/)on what commands to run to install it, it is pretty easy to read and straightforward : 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z8ibeq414ur5wfndnph8.png)

```
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: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wlkbb3ip2zpranc7tqro.png)

### Copy the password and head to : http://yourdomain.com:8080 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7wl83jwtjis9obm5aefl.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tttdxkrqnmay8c579lo6.png)

### Create credentials: 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/66pu5qz34zoxllin6w3p.png)

### 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 : 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kcjscaro8jdq0lj0twmo.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jgz4bks4wmm43l4s7dr8.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2nsyff7wwjwi4yqkali6.png)

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
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dwn6jk3k6muy6no1q1b5.png)

### Run the build/job


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g6et8dfo6roslb1aij1q.png)



The build fails as expected 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z13je7c9kdcypoqri3ax.png)

Let's see the logs of the build to verify that it was caused by missing docker : 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r14h1i32xxt9upkbo4n2.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wq3ip11cbi3bt391c8vj.png)

## 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 : 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qhvd0kjn8wl6mare0a66.png)

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 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qdunalel7v0zhb1kke1u.png)

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. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nx89v883k48d4i1ju5hw.png)

### Now build the job 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j78dh9b6i4tg8ib0e3lq.png)

You see that the build is successful now 
See the logs : 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y356zrpt1uw44bfei1ms.png)

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.