Automating Jenkins Setup using Docker and Jenkins Configuration as Code

Abraham Morales
6 min readDec 22, 2020

--

Jenkins Configuration as Code (JCasC)

Introduction

In this article, we will talk about how to automate the installation and configuration of Jenkins, using Docker and the Jenkins Configuration as Code (JCasC) method. After reading it you will be able to create a custom docker image of a Jenkins server with the basic configuration.

We won’t cover topics like Jenkins setup, Job Dsl plugin, Jenkins security, or Docker, we will focus on using the Jenkins Configuration as Code.

Prerequisites

The prerequisite for this article are:

  • Basic usage and configuration of Jenkins.
  • Basic usage of Docker.
  • Have Docker installed and been able to run the community Jenkins image.
  • All the code shown here has been tested on a Mac OS environment but can be easily adapted to a Windows environment or Linux.

Jenkins Configuration as Code

The ‘as code’ paradigm is about being able to reproduce and/or restore a full environment within minutes based on recipes and automation, managed as code.

Setting up Jenkins is a complex process, as both Jenkins and its plugins require some tuning and configuration, with dozens of parameters to set within the web UI manage section.

Jenkins Configuration as Code (JCasC) provides the ability to define this whole configuration as a simple, human-friendly, plain text YAML syntax. Without any manual steps, this configuration can be validated and applied to a Jenkins controller in a fully reproducible way.

JCasC makes use of the Configuration as Code plugin, which allows you to define the desired state of your Jenkins configuration as one or more YAML files.

On the initialization, the Configuration as Code plugin will configure Jenkins according to the YAML configuration files.

In the following sections, we will see how to implement the custom docker image and manage the configuration of Jenkins using YMAL files.

Step 1. Creating a custom Docker image.

We will create a custom docker image of Jenkins that:

  • Setup initial wizard disabled.
  • Update system dependencies.
  • Define the location for the YAML configuration files.
  • Copy a file with the list of necessary plugins.
  • Copy the YAML configuration file.
  • Install all the plugins listed on a file.

In order to disable the setup wizard we have to define the following environment variable:

ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false

In order to set up the path for the YAML configuration file we have to define the following environment variable:

ENV CASC_JENKINS_CONFIG /var/jenkins_home/jenkins-configuration.yaml

For installing the plugins from a file we will run the script provided by Jenkins:

jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt

So putting everything together the resultant Dockerfile is:

FROM jenkins/jenkins:2.375.3
LABEL maintainer="agmc22mx@gmail.com"
USER root
RUN apt-get update && apt-get install -y lsb-release
RUN curl -fsSLo /usr/share/keyrings/docker-archive-keyring.asc https://download.docker.com/linux/debian/gpg
RUN echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.asc] \
https://download.docker.com/linux/debian $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
RUN apt-get update && apt-get install -y docker-ce-cli
USER jenkins
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
COPY jenkins-configuration.yaml /var/jenkins_home/jenkins-configuration.yaml
COPY seedjob.groovy /usr/local/seedjob.groovy
ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false
ENV CASC_JENKINS_CONFIG /var/jenkins_home/jenkins-configuration.yaml
RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt

Step 2. Defining basic configuration files.

Once we have defined the docker file, we will define the content for plugins.txt and Jenkins-configuration. YAML files.

The plugins.txt file is a list of plugins, where every line has the name and version of one plugin to be installed.

ant:latest
antisamy-markup-formatter:latest
build-timeout:latest
cloudbees-folder:latest
configuration-as-code:latest
credentials-binding:latest
email-ext:latest
git:latest
github-branch-source:latest
gradle:latest
ldap:latest
mailer:latest
matrix-auth:latest
pam-auth:latest
pipeline-github-lib:latest
pipeline-stage-view:latest
ssh-slaves:latest
timestamper:latest
workflow-aggregator:latest
ws-cleanup:latest
job-dsl:latest
blueocean:latest
authorize-project:latest

The Jenkins-configuration.YAML file contains the configuration for the Jenkins instance, at this moment we will set the Jenkins URL.

unclassified:
location:
url: http://server_ip:8080/

We have created a Dockerfile, the plugin list file, and the YAML configuration file. If we build and run the docker image, at this point we will get running a Jenkins instance that has all the plugins installed, disable the setup wizard, and configure the Jenkins URL.

docker build -t jenkins:customJenkins .docker run — name jenkins — rm -p 8080:8080 jenkins:customJenkins

Step 3. Defining basic security.

So far, our setup has not implemented any authentication and authorization mechanisms. In this step, we will set up a basic, password-based authentication scheme and configure the authorization strategy.

Basic authentication mechanisms

We will set up a basic, password-based authentication scheme and create a new user, and we will disable the self-registration form.

Instead of hard-coding the user ID and password, we are going to use variables that can be filled in at runtime.

Add the following to the Jenkins-configuration.YAML file:

jenkins:
securityRealm:
local:
allowsSignup: false
users:
— id: ${JENKINS_ADMIN_ID}
password: ${JENKINS_ADMIN_PASSWORD}

At this point, if we build and run the Jenkins image we will have a Jenkins server with only one user that we specify when running the image.

Before running the container we have to create a directory “jenkins-data” to map the Jenkins Home directory.

docker build -t jenkins:custumJenkins .mkdir jenkins-datadocker run --name jenkins -d --rm --volume jenkins-data:/var/jenkins_home -p 8080:8080 --env JENKINS_ADMIN_ID=admin --env JENKINS_ADMIN_PASSWORD=admin jenkins:custumJenkins http://localhost:8080/login
Jenkins Login Screen
Jenkins Login Screen

Basic authorization strategy

We will use the Matrix Authorization Strategy plugin to configure permissions for the user we have created before, adding the following to the Jenkins-configuration.YAML file:

authorizationStrategy:
globalMatrix:
permissions:
— “Overall/Administer:admin”
— “Overall/Read:authenticated”

The globalMatrix property sets global permissions, with the format:

<permission-group>/<permission-name>:<role>

As part of the basic security settings, we will set up the build authorization, we will set Jenkins to run all the jobs with the same user as the user has triggered to avoid privilege escalation, adding the following to the Jenkins-configuration.YAML file:

security:
queueItemAuthenticator:
authenticators:
-global:
strategy: triggeringUsersAuthorizationStrategy

As final we will enable the agent to controller access control, to be able to control which commands and files the agents have access to.

Add the following to the Jenkins-configuration.YAML file:

remotingSecurity:
enabled: true

Step 4. Using the JobDSL plugin.

Now that we have configured and secured our Jenkins instance we will create a sed job, this job will create all the necessary from a set of groovy files, stored in one repository.

Add the following to the Jenkins-configuration.YAML file:

jobs:
— file: /usr/local/seedjob.groovy

Create a groovy file named seedjob.groovy and add the following code:

job(‘seed_job’) {
scm {
git {
remote {
url(‘https://github.com/abrahamNtd/jcasc-poc.git')
}
branch(‘main’)
}
}
steps {
dsl {
external(‘hellow_world_job.groovy’)
}
}
}

Finally, add the following code to the Dockerfile:

COPY seedjob.groovy /usr/local/seedjob.groovy

At this point, if we build and run, the image we will have a Jenkins server with only one user that we specify when running the image, the basic security is set and we have a sedJob ready to run and build all other projects.

docker build -t jenkins:custumJenkins .docker run --name jenkins -d --rm --volume jenkins-data:/var/jenkins_home -p 8080:8080 --env JENKINS_ADMIN_ID=admin --env JENKINS_ADMIN_PASSWORD=admin jenkins:customJenkinshttp://localhost:8080/job/seed_job/
Jenkins main page
Jenkins main page

The full code for this article can be found at:

https://github.com/abrahamNtd/poc-jenkins-jcasc

Reference

Web Visited on 02/13/2023https://github.com/jenkinsci/configuration-as-code-plugin/Web Visited on 02/13/2023https://www.jenkins.io/doc/book/managing/plugins/

--

--

Abraham Morales

Just a DevOps engineer and passionate about technology in general