Deploying Jenkins with Docker and Terraform: A Comprehensive Guide

Md. Abdullah Al Arif
DevOps.dev
Published in
6 min readMar 16, 2024

--

In modern software development, efficient CI/CD processes are crucial. Jenkins, Docker, and Terraform offer a robust solution for seamless CI environments. In this guide, we’ll deploy Jenkins using Docker containers managed by Terraform. Whether you’re a seasoned DevOps pro or an enthusiast, this tutorial provides a precise roadmap for scalable CI infrastructure. Let’s dive in.

We’ll start by establishing the groundwork. Creating the project structure and configuration files sets the foundation for deploying Jenkins with Docker and Terraform.

Setting Up the Project Directory

Create Project Directory: Begin by creating a dedicated directory for your Jenkins deployment project. Open your terminal or command prompt and execute the following command:

mkdir jenkins-docker-terraform

Navigate to the Project Directory:
Move into the newly created directory:

cd jenkins-docker-terraform

Creating Configuration Files

Now, let’s create the essential configuration files for our Jenkins deployment using Docker and Terraform. We hope Docker and Terraform is installed in your machine.

Dockerfile

The Dockerfile defines the specifications for building a Docker image containing the Jenkins server.

Create a file named Dockerfile in your project directory and add the following contents:

FROM jenkins/jenkins:lts-jdk17

USER root # We need to install some required packages

# Install required packages
RUN apt-get update && \
apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release \
software-properties-common

# Install Docker(optional)
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - && \
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" && \
apt-get update && \
apt-get install -y docker-ce docker-ce-cli containerd.io

# Install Docker Compose(optional)
RUN curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && \
chmod +x /usr/local/bin/docker-compose


COPY --chown=jenkins:jenkins plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN ls -lsta /usr/share/jenkins/ref/plugins.txt

RUN jenkins-plugin-cli -f /usr/share/jenkins/ref/plugins.txt

List of Jenkins common plugins

Create a file named plugins.txt in your project directory and add the following plugins. You can customize this list based on your specific requirements:

# Core Functionality
workflow-aggregator
git
pipeline
docker-workflow

# Code Quality and Analysis
checkstyle
findbugs
pmd
jacoco
cobertura
sonar

# Notifications and Reporting
email-ext
slack
htmlpublisher
junit

# Version Control
git
github
subversion

# Deployment and Provisioning
ansible
docker-build-publish
kubernetes
terraform

# Monitoring and Metrics
metrics
performance

# User Authentication and Authorization
matrix-auth
ldap
role-strategy

# Miscellaneous
credentials-binding
envinject
build-timeout
pipeline-github-lib

Terraform Configuration

The Terraform configuration file defines the infrastructure resources required for deploying Jenkins.

Create a file named main.tf in your project directory and add the following contents:

terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0.1"
}
}
}

provider "docker" {}

resource "docker_image" "jenkins" {
name = "jenkins-gundard:latest"
build {
context = "."
dockerfile = "Dockerfile"
}
}

resource "docker_volume" "jenkins_data" {
name = "jenkins_data"
}

resource "docker_container" "jenkins" {
name = "jenkins"
image = docker_image.jenkins.name
ports {
internal = 8080
external = 8080
}
ports {
internal = 50000
external = 50000
}
restart = "unless-stopped"

volumes {
container_path = "/var/jenkins_home"
host_path = "/home/arif/jenkins_data"
read_only = false
}

}

Let’s break down the provided Terraform configuration code into chunks and describe each part:

Terraform Configuration

This block specifies the required provider for Docker and its version. It ensures that Terraform uses the correct Docker provider when managing Docker-related resources.

terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0.1"
}
}
}

Docker Provider

This block configures the Docker provider, indicating that Terraform will interact with Docker to manage container-related resources.

provider "docker" {}

Docker Image Resource

This block defines a Docker image resource named “Jenkins”. It specifies the name of the Docker image to build (jenkins-gundard:latest) using the Dockerfile present in the current directory (.).

resource "docker_image" "jenkins" {
name = "jenkins-gundard:latest"
build {
context = "."
dockerfile = "Dockerfile"
}
}

Docker Volume Resource

This block defines a Docker volume resource named “jenkins_data”. It creates a Docker volume with the name “jenkins_data”.

resource "docker_volume" "jenkins_data" {
name = "jenkins_data"
}

Docker Container Resource

This block defines a Docker container resource named “jenkins”. It specifies the Docker image to use (docker_image.jenkins.name) and exposes ports 8080 and, 50000 for Jenkins web UI and agent communication, respectively. The restart attribute is set to “unless-stopped”, indicating that the container should automatically restart unless explicitly stopped by the user.

resource "docker_container" "jenkins" {
name = "jenkins"
image = docker_image.jenkins.name
ports {
internal = 8080
external = 8080
}
ports {
internal = 50000
external = 50000
}
restart = "unless-stopped"

volumes {
container_path = "/var/jenkins_home"
host_path = "/home/arif/jenkins_data" # It should be an absolute path.
read_only = false
}

}

The volumes section specifies the volume mappings between the container and the host. In this case:

  • container_path: Specifies the path inside the container where the volume will be mounted (/var/jenkins_home).
  • host_path: Specifies the path on the host system where the volume data will be stored (/home/arif/jenkins_data). It should be an absolute path.
  • read_only: Specifies whether the volume should be mounted as read-only or read-write (false in this case).

Terraform Workflow: Deploying Infrastructure as Code

Initializing Terraform

Before using Terraform to manage your infrastructure, you need to initialize the working directory. This step downloads the necessary provider plugins and initializes the backend.

terraform init

Validating Configuration Syntax

Once initialized, you can validate the syntax and configuration of your Terraform files to ensure they’re correctly formatted and without errors.

terraform validate

Planning Infrastructure Changes

Terraform generates an execution plan based on your configuration files. This plan describes what actions Terraform will take to change the infrastructure to match the desired state defined in the configuration.

terraform plan -out=tfplan

This command generates a plan and saves it to a file named tfplan for review before applying.

Applying Infrastructure Changes

After reviewing the execution plan, you can apply the changes to your infrastructure. Terraform will prompt for confirmation before proceeding.

terraform apply tfplan

Alternatively, you can apply changes directly without saving a plan:

terraform apply

You can apply the planned changes with the --auto-approve flag:

terraform apply --auto-approve tfplan

Terraform will start provisioning the infrastructure according to the defined specifications in our configuration files. This includes:

  • Building the Docker image for Jenkins using the specified Dockerfile.
  • Creating a Docker volume to persist Jenkins data.
  • Spinning up a Docker container named “jenkins” based on the Jenkins image.
  • Exposing ports 8080 and, 50000 for Jenkins web UI and agent communication, respectively.
  • Mounting the Docker volume to store Jenkins data persistently.

Sometimes the plugins installations creates some issues. You can remove/comment the line and manually install the plugins on the built docker container:

RUN jenkins-plugin-cli -f /usr/share/jenkins/ref/plugins.txt

Monitoring Deployment Progress

During the deployment process, Terraform will provide real-time feedback on the progress of each step. You’ll see logs indicating the creation of resources and any associated changes.

Once the deployment is complete, Terraform will display a summary of the changes applied and the overall state of the infrastructure like the following:

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

If your Jenkins plugins installation failed earlier, follow the bellow command on docker container.

docker ps
docker exec jenkins sh -c "jenkins-plugin-cli -f /usr/share/jenkins/ref/plugins.txt"

Verifying Jenkins Deployment

To verify that Jenkins has been successfully deployed, you can access the Jenkins web interface using your web browser. Navigate to http://<your-server-ip>:8080 and follow the instructions to complete the initial setup wizard.

Run the following command to get the initial password:

docker exec jenkins sh -c "cat /var/jenkins_home/secrets/initialAdminPassword"

Destroying Infrastructure

If you need to tear down the infrastructure managed by Terraform, you can use the destroy command. Be cautious, as this will permanently delete all resources defined in your configuration.

terraform destroy --auto-approve

However, if you reapply the same Terraform code after destroying the infrastructure, it will not restore the data stored in the volume folder automatically. Terraform treats each execution as a fresh deployment and does not retain state between executions.

To ensure that data stored in the volume folder persists across infrastructure deployments and destruction, you may need to implement additional mechanisms, such as backing up and restoring data manually or integrating with external storage solutions.

When working with persistent data in Docker containers managed by Terraform, it’s essential to consider data management strategies to maintain data integrity and continuity across infrastructure lifecycle events.

Recap and Conclusion

In this article, we’ve covered the essential Terraform commands for managing infrastructure as code. From initializing the working directory to applying changes and destroying resources, Terraform provides a streamlined workflow for provisioning and managing infrastructure across various cloud providers and environments.

Stay tuned for the next part, where we’ll delve into advanced Terraform concepts and techniques for infrastructure orchestration.

Check my other development and deployment articles.

--

--