Note: This is part 3 of the Jenkins docker tutorial series. Click here for part 1. And here for part 2.

Problem

You want to set up a Jenkins Docker service that automatically sets up the Jenkins settings so you don't have to do it manually.

Solution

TLDR: Bitbucket code here.

There is a Jenkins plugin called Jenkins Configuration as Code (JCasC) which provides the ability to define your whole Jenkins configuration as a YAML file. In this tutorial we will extend our initial Jenkins Docker service from previous posts to utilize the JCasC plugin such that it automatically sets up the configuration for

Prerequisites

Following plugins must be installed:

In a previous tutorial, we learned how to create a Docker image that comes with default plugins pre-installed (we even installed exactly those 2 plugins).

Implementation

The realization as code is threefold:

  1. We create a jenkins.yml file that contains our role-based config and the credentials for the admin user
  2. We set the environmental variable CASC_JENKINS_CONFIG to point to the location where we will place the jenkins.yml file from step 1
  3. We will bind-mount the jenkins.yml file into the location of CASC_JENKINS_CONFIG

jenkins.yml

We will create two global roles:

  • an "admin" role with overall access-rights on the Jenkins server
  • and a "readonly" role for all the other authenticated users, that is users that can log in.

For the first point, we need to use authorizationStrategy key, where we specify roleBased before we set up the global roles.

For the latter we populate the securityRealm key, define allowsSignup: false and set the username (id) and password of the admin user.

jenkins:
  systemMessage: "Jenkins configured automatically by Jenkins Configuration as Code plugin\n\n"
  authorizationStrategy:
    roleBased:
      roles:
        global:
          - name: "admin"
            description: "Jenkins administrators"
            permissions:
              - "Overall/Administer"
            assignments:
              - "admin"
          - name: "readonly"
            description: "Read-only users"
            permissions:
              - "Overall/Read"
              - "Job/Read"
            assignments:
              - "authenticated"
  securityRealm:
    local:
      allowsSignup: false
      users:
        - id: "admin"
          password: "admin"

Next, we update our docker-compose.yml file.

docker-compose.yml

We extend our initial compose file by adding the CASC_JENKINS_CONFIG environmental variable and bind-mounting the jenkins.yml file to that location:

version: "3.7"

services:
  jenkins:
    build:
      context: .
      args:
        USER_GROUP_ID: $USER_GROUP_ID
        USER_ID: $USER_ID
    environment:
      CASC_JENKINS_CONFIG: $REF/casc_configs/jenkins.yml
    ports:
      - 9080:8080
    volumes:
      - $HOME/data/jenkins:$JENKINS_HOME
      - ./bashrc:$JENKINS_HOME/.bashrc
      - ./jenkins.yml:$REF/casc_configs/jenkins.yml

Remember, that the bash variables (e.g. $REF) are exported in our helper-script docker_run.sh which now looks as following

#!/usr/bin/env bash

export USER_GROUP_ID=$(id -g)
export USER_ID=$(id -u)

export JENKINS_HOME=/var/jenkins_home
export REF=/usr/share/jenkins/ref

docker-compose "$@"

That's it.

Whenever you start the docker service now, it will parse the jenkins.yml configuration file and load the specified settings:

apoehlmann:~/workspace/blog/jenkins$ ./docker_run.sh up --build
Building jenkins
Step 1/9 : FROM jenkins/jenkins:lts
 ---> a3f949e5ebfd
Step 2/9 : USER root
 ---> Using cache
 ---> d6391fcb8ed0
Step 3/9 : ARG USER_GROUP_ID
 ---> Using cache
 ---> 174db0b158ea
Step 4/9 : ARG USER_ID
 ---> Using cache
 ---> 9ce5401f06e0
Step 5/9 : RUN usermod -u ${USER_ID} -g ${USER_GROUP_ID} jenkins     && chown -R jenkins ${REF}
 ---> Running in 3bb0c3d6baf9
Removing intermediate container 3bb0c3d6baf9
 ---> b9385713ba6b
Step 6/9 : USER jenkins
 ---> Running in 02c1132e853c
Removing intermediate container 02c1132e853c
 ---> aa6af15a8b9e
Step 7/9 : COPY plugins.txt ${REF}/init.groovy.d/plugins.txt
 ---> 4567cce4e1c3
Step 8/9 : RUN install-plugins.sh < ${REF}/init.groovy.d/plugins.txt
 ---> Running in 880ec219900b
Creating initial locks...
Analyzing war /usr/share/jenkins/jenkins.war...
Registering preinstalled plugins...
Using version-specific update center: https://updates.jenkins.io/2.204...
Downloading plugins...
Downloading plugin: configuration-as-code from https://updates.jenkins.io/download/plugins/configuration-as-code/1.35/configuration-as-code.hpi
Downloading plugin: role-strategy from https://updates.jenkins.io/download/plugins/role-strategy/2.16/role-strategy.hpi
 > role-strategy depends on configuration-as-code:1.29;resolution:=optional,matrix-auth:2.2
Skipping optional dependency configuration-as-code
Downloading plugin: matrix-auth from https://updates.jenkins.io/2.204/latest/matrix-auth.hpi
 > matrix-auth depends on configuration-as-code:1.12;resolution:=optional,cloudbees-folder:6.1.0;resolution:=optional
Skipping optional dependency configuration-as-code
Skipping optional dependency cloudbees-folder

WAR bundled plugins:


Installed plugins:
configuration-as-code:1.35
matrix-auth:2.5
role-strategy:2.16
Cleaning up locks
Removing intermediate container 880ec219900b
 ---> e24e43fd83d2
Step 9/9 : WORKDIR $JENKINS_HOME
 ---> Running in 7f76199c26ab
Removing intermediate container 7f76199c26ab
 ---> 60ad8d96cc8f

Successfully built 60ad8d96cc8f
Successfully tagged jenkins_jenkins:latest
Creating jenkins_jenkins_1 ... done
Attaching to jenkins_jenkins_1
jenkins_1  | Running from: /usr/share/jenkins/jenkins.war
jenkins_1  | webroot: EnvVars.masterEnvVars.get("JENKINS_HOME")
...

And since the jenkins.yml file is bind-mounted into the container, changes can be applied without having to re-build the whole image. Instead you just have to edit the file and restart the service with ./docker_run.sh restart.

We can now sign in with the username and password specified in the jenkins.yml:

Jenkins login screen

Bonus: Skip Jenkins' Unlock Wizard

Since we already set up an admin user, we could now skip the default setup wizard that shows up the first time you start the Jenkins instance.

This can be done by adding the following environmental variable to the environment: block of the docker-compose.yml:

JAVA_OPTS: -Djenkins.install.runSetupWizard=false

More Jenkins Configurations as Code

More config examples and how the corresponding YAML blocks have to look like can be found in the demo sub-directory of the official jenkinsci Github repo.

Getagged mit:
Jenkins Docker Tutorial Jenkins-Docker-Tutorial
blog comments powered by Disqus