Automating Jenkins install and configuration with Docker and Terraform
This post is part of Jenkins tutorials on Popularowl. In the previous sections we did a manual install, configuration and production readiness for Jenkins CI server.
This part is about automating the manual provisioning using infrastructure as a code approach.
By the end of this tutorial you will have the automation scripts which will containerize and install Jenkins CI on virtual machine of your choice.
With auto-installed plugins, automatically skipped Jenkins setup wizard and pre created administrator user.
Prerequisites
- Access to the Debian Linux server. You can use VirtualBox locally or one of the cloud platforms (we are using Digital Ocean cloud as example in this tutorial).
- Docker installed (Mac or Windows).
Building Jenkins as a Docker container
In this section we will create a Docker container image containing our customized Jenkins setup.
By containerizing our Jenkins CI server instance, we gain the ability to deploy it on multiple cloud native environments as immutable image. And the same image can run locally on your development machine as well.
First, let's create file named Dockerfile
with the following content.
# specific version of jenkins container image
FROM jenkins/jenkins:2.480-jdk17
# volume for Jenkins settings
VOLUME /var/jenkins_home
We can go ahead and build Docker container image from this file. It would pull the latest official container provided by Jenkins CI team and would create the custom volume for us.
But we need couple more extras.
Skipping Jenkins default setup wizard
Remember, the setup wizard and new user creation screens we filled in manually in the previous tutorial?
We would like to skip these steps in our Jenkins container, so when Docker container starts it its ready to go.
The experience we want - is that after Terraform has finished running its steps, we should get a ready to be used Jenkins instance with admin user login screen.
In order to achieve this, we have to set the following Java environment option to instruct Jenkins to skip Setup Wizard during first startup.
ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false
Automate Jenkins admin user setup
We now need to programmatically add a Jenkins admin user.
Right after the startup, Jenkins loads all the .groovy files it finds in init.groovy.d/
directory.
We will use this behaviour and write the custom Groovy DSL file which uses Jenkins internal API. It will create admin user with the predefined username and password. Here is the file.
import jenkins.model.*
import hudson.security.*
def env = System.getenv()
def jenkins = Jenkins.getInstance()
if(!(jenkins.getSecurityRealm() instanceof HudsonPrivateSecurityRealm))
jenkins.setSecurityRealm(new HudsonPrivateSecurityRealm(false))
if(!(jenkins.getAuthorizationStrategy() instanceof GlobalMatrixAuthorizationStrategy))
jenkins.setAuthorizationStrategy(new GlobalMatrixAuthorizationStrategy())
def user = jenkins.getSecurityRealm().createAccount(env.JENKINS_USER, env.JENKINS_PASS)
user.save()
jenkins.getAuthorizationStrategy().add(Jenkins.ADMINISTER, env.JENKINS_USER)
jenkins.save()
In the above script we are creating Jenkins CI admin user, with username / password which we will define ss environment variables in Dockerfile.
We also add line to Dockerfile to copy above groovy file to the container image. Update the Dokerfile
with following
ENV JENKINS_USER admin
ENV JENKINS_PASS admin
# Jenkins runs all the grovy files from init.groovy.d dir
# use this for creating a default admin user
COPY default-user.groovy /usr/share/jenkins/ref/init.groovy.d/
There is another way to do admin user setup by using Jenkins Configuration as Code plugin
Installing Jenkins plugins from list
Next, we want to pre-install selected Jenkins CI plugins within container image.
Jenkins team has created a useful CLI utility, which automates Jenkins plugin install. It supports installation from the file with the list of plugin names.
We will use this official CLI and create the file named jenkins-plugins.txt
with the list of Jenkins plugins we want to be pre-installed as our custom container is starting.
Update the Dockerfile
with the following
# install jenkins plugins
COPY ./jenkins-plugins.txt /usr/share/jenkins/plugins.txt
RUN jenkins-plugin-cli -f /usr/share/jenkins/plugins.txt
Our Dockerfile now has all the basic setup we want.
You can find the completed Dockerfile
and plugins file in the source repository on GitHub.
If you now build the Docker image from this Dockerfile
, you will have ready to run container with pre-configured Jenkins CI server.
For local build you can run
docker build -t popularowl/jenkins:v1 .
docker run -d --name jenkins-server -p 8080:8080 popularowl/jenkins:v1
Provisioning Cloud Host
Next, we are going to use Terraform for auto provisioning Digital Ocean virtual machine instance and orchestrate Jenkins CI container start on it.
We will use a Terraform foundation project we have created in the previous tutorial on Popularowl.
It provides a basic Terraform structure for starting new VMs on Digital Ocean cloud platform.
Clone the above code repository and we will only have to amend couple of files in it.
Installing Dependencies
Update main.tf
file with the following
...
provisioner "remote-exec" {
inline = [
# steps to setup docker ce
"apt update",
"apt -y install apt-transport-https ca-certificates curl gnupg2 software-properties-common",
"curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -",
"add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable\" ",
"apt update",
"apt-cache policy docker-ce",
"apt -y install docker-ce",
# build Jenkins container image with default admin user
"cd /tmp && docker build -t popularowl/jenkins .",
# run newly built jenkins container on port 8080
"docker run -d --name jenkins-server -p 8080:8080 popularowl/jenkins",
# install remaining dependencies
"apt -y install nginx",
"apt -y install ufw",
# setup debian firewall
"ufw status verbose",
"ufw default deny incoming",
"ufw default allow outgoing",
"ufw allow ssh",
"ufw allow 22",
"ufw allow 80",
"yes | ufw enable",
# update nginx configuration
"rm -f /etc/nginx/sites-enabled/default",
"cp -f /tmp/jenkins-proxy /etc/nginx/sites-enabled",
"service nginx restart"
]
}
...
remote-exec
instructs Terraform to run commands on the cloud VM via ssh terminal. All the above shell lines will run one by one.
They will install Docker CE, build Docker image from Dockerfile, start the container, install and configure Nginx web server and finally, setup firewall to only allow specific requests.
Copy files
We also need to instruct Terraform to copy over the configuration files we have created in the previous steps.
Update main.tf
with following
...
// copy the files to newly created instance
provisioner "file" {
source = "files/jenkins-proxy"
destination = "/tmp/jenkins-proxy"
}
provisioner "file" {
source = "files/Dockerfile"
destination = "/tmp/Dockerfile"
}
provisioner "file" {
source = "files/jenkins-plugins"
destination = "/tmp/jenkins-plugins"
}
provisioner "file" {
source = "files/default-user.groovy"
destination = "/tmp/default-user.groovy"
}
...
See the a final version of Terraform code after all the changes applied.
Create and Destroy
Now you have fully funcioning Terraform project which can be ran by using
terraform init
terraform plan
terraform apply
terraform destroy
Summary
We now have the recreatable Jenkins CI installation with preconfigured admin user and pre installed plugins.