Automating Jenkins install and configuration with Docker and Terraform

automating jenkins install and configuration terraform docker

This is part of Jenkins tutorial on Popularowl. We did manual install, configuration and production readiness for Jenkins in the previous tutorial.

This part is about automating the provisioning with infrastructure as a code mindset.

By the end of this tutorial you will have the automation scripts to containerise and install Jenkins on your VM.

With auto-installed plugins, automatically skipped Jenkins setup wizard and automatically created admin user.

Prerequisites

Provisioning Host

We are going to use Terraform for auto provisioning Digital Ocean VM instance.

Terraform is a great tool, which allows you to maintain cloud infrastructure as a version controlled code. It has multiple providers to connect to the public cloud providers like AWS, Google Cloud, Azure, DigitalOcean etc.

This tutorial is not the Terraform 101. If you have to learn the basics, start here.

Create the variables.tf file.

This file will hold the variables which we will use for automation. Add the following contents:

variable "token" {
  description = "Digital Ocean Api Token"
}

variable "region" {
  description = "Digital Ocean Region"
  default = "lon1"
}
variable "droplet_image" {
  description = "Digital Ocean Droplet Image"
  default = "debian-9-x64"
}

variable "jenkins_droplet_size" {
  description = "Droplet size for Jenkins server"
  default = "1gb"
}

# location of the private ssh key 
# used for ssh connection by terraform
# change the location if different on 
# your local machine
variable "pvt_sshkey" {
  description = "Location of the local private ssh key"
  default = "~/.ssh/id_rsa"
}

# ssh_fingerprint should be exported in the local
# shell environment to avoid hardcoded values
# see readme.md for examples
variable "ssh_fingerprint" {
  description = "Fingerprint of the public ssh key stored on Digital Ocean"
}

Next, create another file main.tf

This will be the main file Terraform will use for automation tasks. For now add the following content:

#
# main Terraform file to describe automation
#

# choose Digital Ocean provider
provider "digitalocean" {
  token = "${var.token}"
}

# create VM instance on Digital Ocean
resource "digitalocean_droplet" "jenkinsci-server" {
  image = "${var.droplet_image}"
  name = "jenkinsci-server"
  region = "${var.region}"
  size = "${var.jenkins_droplet_size}"
  ssh_keys = [
      "${var.ssh_fingerprint}"
  ]

  connection {
        user = "root"
        type = "ssh"
        private_key = "${file(var.pvt_sshkey)}"
        timeout = "2m"
  }
   
}

# print out ip address of created Jenkins server VM
output "service-ip" {
  value = "${digitalocean_droplet.jenkinsci-server.ipv4_address}"
}

You can see the variables we have defined earlier in the variables.tf file referenced here.

We are choosing the Terraform provider, which in our case is Digital Ocean. Describing the name and size of the VM we want Terraform to create.

And specifying ssh connection details.

I’m referring to the location of the private ssh key on the local machine (the one where you are running Terraform).

Also to the public key fingerprint – this public key you will upload and store in Digital Ocean platform.

Digital Ocean token is the equivalent of API key and is specific to your account.

Another important bit – I’m not hardcoding the sensitive values of the key fingerprint or Digital Ocean token.

It is a bad practice to store such secrets in plain in the code.

We will export them as the environment variables with TF_VAR_xx prefix. Terraform will automatically have an access to these.

The below works for Mac OS / Linux. Windows is slightly different.

export TF_VAR_token=xxxxxxxxx
export TF_VAR_ssh_fingerprint=xxxxxxxxx

Now we are ready to initialise the Terraform project and create the VM server.

terraform init
terraform apply

Terraform should now connect to Digital Ocean, create the VM for you and establish the connectivity via ssh.

It will print out the IP address of newly created server.

You can run terraform destroy to remove the VM for now.

Installing Dependencies

Now add the following to your main.tf file.

provisioner "remote-exec" {
      inline = [

        "apt update",
        "apt -y install apt-transport-https ca-certificates curl gnupg2 software-properties-common",
        "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"        
      ]
  }

remote-exec instructs Terraform to run commands via ssh terminal.

It’s a great way to automate any manual commands you want to run via ssh.

Once you run terraform apply again, terraform will execute all the commands as described in main.tf. Give it a try.

Now, add all the manual commands we ran in the previous tutorial.

Your terraform will now be automating all these steps. In addition, we will also need Docker to be installed on the VM.

See the final version of main Terraform file in source code repository with all the dependencies added.

Building Jenkins as a Docker container

Great, we have the automated setup of the server and all infrastructure dependencies.

We should automate the Jenkins install and setup.

For this, we will create Docker container image with customised Jenkins setup.

By containerising our Jenkins CI server instance, we gain the ability to deploy it on multiple cloud native environments as immutable image. And test locally as well.

Let’s create file named Dockerfile with the following content.

FROM jenkins/jenkins:latest

# volume for Jenkins settings
VOLUME /var/jenkins_home

If we build Docker image now it would pull the latest official container provided by Jenkins team and would create the custom volume for us.

But it’s not enough.

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 during our automated Jenkins install.

The experience we want – is that after Terraform has finished running, we should get a ready to be used Jenkins with 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

Now we need to automatically add Jenkins admin user.

Automate Jenkins admin user setup

Right after 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.

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()

This file will be copied over during the Docker build stage.

Installing Jenkins plugins from file

We also wanted to auto pre-install specific plugins.

Jenkins official Docker image ships with the script which installs any plugin we pass as argument.

We are going to create the file named jenkins-plugins with the list of Jenkins plugins we want to be pre-installed.

Simple loop during our Docker container build will pass the plugin names to the plugin installation script one by one.

Update the Dockerfile with:

# install jenkins plugins
COPY ./jenkins-plugins /usr/share/jenkins/plugins
RUN while read i ; \ 
      do /usr/local/bin/install-plugins.sh $i ; \
    done < /usr/share/jenkins/plugins

You can find the completed version in source files for this tutorial.

Launching Jenkins Docker container with Terraform

We now have all the necessary files and setup for automating the build of our Jenkins container, starting Jenkins server and provisioning Nginx / firewall in front of it.

Finally, add the following steps to main.tf file, remote-exec section.

# build jenkins image with default admin user
"cd/tmp && dockerbuild-tpopularowl/jenkins.",

# run newly built jenkins container on port 8080
"dockerrun-d--namejenkins-server-p8080:8080popularowl/jenkins"

This will tell Terraform to run Docker build command on the VM and after successful Docker build, start the docker container.

Summary

In this tutorial, we have created files and the setup needed for automating provisioning and setup of Jenkins CI server.

We used Docker and Terraform toolset to accomplish that.

The full source code of this tutorial can be found in dedicated repository on GitHub.

Did you like this post?
Subscribe to receive new Popularowl
tutorials and posts

One Response

  • Jeremy

    When I try to setup the admin user using the Groovy script I get this problem when I attempt to use the account:

    Access Denied
    admin is missing the Overall/Read permission

    Then I can’t access anything in the web frontend anymore.

    Any idea why this might be?

    I see this is a pretty recent post, so I wonder what I’m doing wrong.

Post Your Comment

Your email address will not be published.