How I Setup Jenkins On Docker Container Using Ansible (Part 2)
7 Min Read
The Story
This post continues from How I Setup Jenkins On Docker Container Using Ansible (Part 1)
In this post, we will detail how an auto-configured Jenkins environment was built and deployed as a Docker container.
The Walk-through
The setup is divided into 3 sections, Instance Creation, Containerization and Automation.
This post-walk-through mainly focuses on containerization.
Prerequisites
If you already have Docker and Docker-Compose installed and configured you can skip this step else you can search for your installation methods.
sudo apt install docker.io
# The Docker service needs to be set up to run at startup.
sudo systemctl start docker
sudo systemctl enable docker
python3 -m pip install docker-compose
Using a container offers convenience and ensures that deployments are deterministic. It also offers a great deal of flexibility.
Containerization
But before continuing, the directory structure shown below should resemble what we should have once we are done with the walk-through.
Directory Structure
tree -L 3
.
├── Dockerfile
├── jenkins_config.yaml
├── jenkins_plugins.txt
├── populate_jenkins_casc.py
├── requirements.txt
└── userContent
└── README.md
1 directory, 6 files
The following sections will explain some of the files and directories we will be creating.
Create Docker container
The code snippets below do the following:
- Creates a directory where we will store all our files and scripts
mkdir -p ~/tmp/jenkins-docker && cd "$_"
mkdir -p userContent
- Creates 5 empty
jenkins_config.yaml
,jenkins_plugins.txt
,populate_jenkins_casc.py
,requirements.txt
, andREADME.md
files and,- Note: The contents of these files are discussed in other posts to avoid this post getting too long and confusing.
touch jenkins_config.yaml \
jenkins_plugins.txt \
populate_jenkins_casc.py \
requirements.txt \
userContent/README.md
echo "Files in this directory will be served under your http://<server>/jenkins/userContent/" > userContent/README.md
- Creates a
Dockerfile
that includes all relevant dependencies (Note: Jenkins is pinned to a specific version to ensure determinism)
cat > Dockerfile << "EOF"
FROM jenkins/jenkins:2.321
USER root
# set your timezone
ENV TZ="/usr/share/zoneinfo/Africa/Johannesburg"
# hadolint ignore=DL3008,DL3009,DL3015
RUN apt-get update && \
apt-get -yq install \
apt-transport-https \
bzip2 \
ca-certificates \
curl \
gnupg2 \
iputils-* \
net-tools \
software-properties-common \
wget
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
RUN apt-key fingerprint 0EBFCD88
RUN add-apt-repository -y \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"
# hadolint ignore=DL3008,DL3009,DL3015
RUN apt-get update \
&& apt-get install -yq \
docker-ce-cli
VOLUME /var/run/docker.sock
# ------------ extra-tools ------------
# hadolint ignore=DL3008,DL3015
RUN apt-get update \
&& apt-get -yq install \
graphviz \
rsync \
vim \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# ------------ Jenkins configurations ------------
# Change to Jenkins user
USER jenkins
# increase http login session timeout
ENV JENKINS_OPTS --sessionTimeout=10080
# https://www.jenkins.io/doc/book/managing/casc/
ENV CASC_JENKINS_CONFIG "/usr/share/jenkins/ref/jenkins_config.yaml"
# disable setup wizard on first start
ENV JAVA_OPTS=-Djenkins.install.runSetupWizard=false
# install plugins
# hadolint ignore=DL3059
COPY --chown=jenkins:jenkins jenkins_plugins.txt /usr/share/jenkins/ref/jenkins_plugins.txt
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/jenkins_plugins.txt
# copy user contents which might include images and other files
COPY --chown=jenkins:jenkins userContent/* /usr/share/jenkins/ref/userContent/
# hadolint ignore=DL3059
RUN chown -R jenkins:jenkins /var/backups
# hadolint ignore=DL3059
RUN chown -R jenkins:jenkins /var/jenkins_home
EOF
To ensure that we follow the Docker’s best practices, we use Hadolint which is a Dockerfile
linter that helps you build best practice Docker images. I use it in all of my projects to ensure I’m creating small, secure, efficient, and maintainable images.
Let’s run our Dockerfile
through Hadolint
:
docker run --rm -i hadolint/hadolint < Dockerfile
If you are a VS Code user, there is the Hadolint extension. If you want to use it directly in Github, there is a Hadolint Github action.
Once we have established that our Dockerfile
is valid (no linting errors), we can explore the jenkins_config.yaml
, jenkins_plugins.txt
, populate_jenkins_casc.py
, requirements.txt
empty files created above.
Jenkins Plugins
To learn more about Jenkins plugins (jenkins_plugin.txt
), read the blog post on Managing Jenkins Plugins was written.
Jenkins Configuration as Code
To learn more about Jenkins configuration as code (jenkins_config.yaml,
populate_jenkins_casc.py, and
requirements.txt`), read the blog post on Managing Jenkins Configuration As Code And Secrets Management was written.
Putting it all together
Once all of the files are created, we can execute the sequence of commands that are needed to assemble the Jenkins image using through the Dockerfile
that was created.
Let’s build the image using the following command:
docker build -t jenkins-docker .
Ensure that the image was successfully built by listing all available images:
docker image ls
which gives us a similar output
Now let’s tag the image before publishing (push) it to Docker Hub.
To tag a docker image you use the following command:
docker tag jenkins-docker <<dockerhub username>>/jenkins-image .
Now let’s publish the new image by logging into Docker Hub using our Docker Hub credentials, if you do not have any then sign up for Docker Hub.
Thereafter login using the following command in order to push the image to Docker Hub:
docker login
After a successful login, then push the image to Docker Hub using the following command:
docker push
Thereafter, we verify that the image was successfully pushed to Docker Hub
Conclusion
Congratulations! You have successfully created a Jenkins container (containing Jenkins plugins and configuration as code) and published it to Docker Hub. You can now use the instance as part of the Ansible playbooks.