Debojit Kaushik

@kaushik.debojit

DevOps: Setting up GitLab + Jenkins CI with Docker

Every serious developer after a point will definitely want to deploy his/her product or step up their game to production. Being well versed with DevOps pipelines surely helps once you are done writing the software/product. So we segue into setting up our own server of Git along with a continuous deployment/integration pipeline. Here is a easier introduction into getting your hands dirty with something which comes after back-end engineering. Welcome to DevOps!

After searching for quite a few days, I couldn’t find enough information on how to set up my own GitLab server and Jenkins CI server. Especially how to integrate them and set up the minimal, yet complete eco-system. There are some very nice blogs out there demonstrating the process, but in pieces. Which is why this blog might help people who are trying to reign in the involving concepts and have a one stop tutorial to follow.

I have curated the whole process using screenshots as I know how much it helps to see things even after reading through. To give a clear idea of how it works and what this tutorial will follow, here is a flow chart of the complete setup and flow.

The tutorial follows the code available here. You can choose to clone the repository and follow the code as well. Or you can follow the guide and setup your own code. The setup requires docker. Find information to install docker here.

Run the following command to install Gitlab. Once installed, we can see Gitlab container running. You can find this info by running the command docker ps -a

sudo docker run --detach \ --hostname gitlab.example.com \ --publish 443:443 --publish 80:80 --publish 22:22 \ --name gitlab \ --restart always \ gitlab/gitlab-ce:latest

Now go to https://localhost:80/ to find Gitlab up and running. If its not available yet, wait for few minutes till you can see Gitlab up and healthy.

Setup a new password and this will be the password for the user root which is by default an admin user. You can create more users as required. Once the password is setup, you can login to Gitlab as shown below.

Now navigate to user’s settings.

Navigate to Access Tokens to proceed to generate an access token. Give it a name and expiry date.

Once you generate the token, keep it in a secure place. We’ll use this token to auto-populate the projects in Gitlab server.

We’ll now run fetch_repo.pyto pull projects of a specified language from Github and push them to GitLab.

python3 fetch_repos.py [your_github_username] [your_github_password]

Running this command with valid Github credentials will then prompt to provide the personal access token which we obtained earlier. Upon entering the token, projects are set up in Gitlab.

We can now check Gitlab whether all the repos have been set up. We’ll setup Jenkins now.

Jenkins — Setup

sudo docker run -p 8080:8080 --name=jenkins-master jenkins

Once the Jenkins container is running, we need to do a one time setup. As you can see in the terminal, copy the password printed or the password is always available at : /var/jenkins_home/secrets/initialAdminPassword

For this, first login into the container by running the command,

sudo docker exec -i -t jenkins-master(name_of_the_image) /bin/bash

Then navigate to the above path and copy the initialAdminPassword to the clipboard. Now goto http://localhost:8080/ to find the Jenkins up and running as in the following figure.

Now Paste the initialAdminPassword in the clipboard in the Jenkins webpage.

Click continue. Now Jenkins gives options for a custom/suggested installation as in the next figure. We select suggested installation.

This will install the standard components.

After you complete the installation, Jenkins brings you to creating the first user. Create a user as shown in the figure.

Once the user is created, click complete and Jenkins in now ready to use!

A fresh view of Jenkins should look like this.

We now need to setup plugins in Jenkins. Run the install_plugins script — python3 install_plugins.py
 If the output is True, run the command docker restart jenkins-master to restart Jenkins

Now we need to configure the Gitlab plugin.

Go to Manage Jenkins > Configure System. Here Gitlab section is not configured and should look like this one below

We need to give some details:
 • Connection name : A name for Gitlab connection which we’ll later refer in each of the job created as well as job DSL. This connection is global for Jenkins. We’ll give gitlab as the name.

• Gitlab host url: This is the url of the Gitlab container running in the system. We enter http://172.17.0.2:80/

• Credentials : We don’t have any Gitlab credentials setup as of now. We’ll create a new one as below. Click on the Add button to setup a new credential. Enter the Gitlab personal access token generated before. Jenkins will use this credential for various purposes like fetching the repos for jobs

Once this is setup, click on test connection. This should return success as shown below.

Uncheck Enable authentication for project Endpoint. We’ll disable authentication so that web hook works without a token.
If you are getting any error, check the url of Gitlab. It should not be localhost. Since we are running Gitlab and Jenkins through docker, Jenkins and Gitlab are running in the LAN of docker. Jenkins needs to refer to Gitlab within that network. So, to get the address of Gitlab, in bash of Gitlab, find the url at /etc/hosts file. Enter that url here.

Job Creation

We need to create jobs for each of the repos setup in Gitlab. For this we’ll use job DSL plugin to do it. Job DSL is written in groovy to fetch all the projects in Gitlab and setup a job for each of the repo setup in Gitlab. Below is the code for job DSL

Note: We are using the IP of Jenkins and the private_token is the Personal Access Token obtained from Gitlab earlier. The token will be different for you. Replace the token in create_master_job.py before running the script. If the IP is also different, replace that too.
//This is the Private Access token obtained in GitLab. Please //replace this with the one you obtained in create_master_job.py.  
String private_token = "DjotJ94w7GRsRdU6eDWt" // If the address of jenkins is different from this, please replace that too. String ip = "http://172.17.0.3:80/" // We need to fetch URLs of all the repos in order to create a job for each of them def jdata = new groovy.json.JsonSlurper().parseText(new URL("http://172.17.0.3:80/api/v3/projects?private_token="+private_token).text) jdata.each { String repo_url = it.ssh_url_to_repo repo_url = repo_url.replace("git@gitlab.example.com:",ip) String proj = repo_url.substring(repo_url.lastIndexOf('/') + 1); String project_name = proj[0..-5] job(project_name) { // Basic details of the job description('A job for the project: ' + project_name) displayName(project_name) // SCM details of the repo scm { git { branch('master') remote { url(repo_url) credentials('gitlab-root-user') } } } // Build steps steps { gradle('check') gradle { tasks('clean') tasks('build') switches('--stacktrace') switches('--debug') } } // Setting up Jacoco Code coverage publishers { jacocoCodeCoverage { execPattern '**/**.exec' classPattern '**/classes' sourcePattern '**/src/main/java' exclusionPattern '' inclusionPattern '' } } // Setting up triggers for Gitlab triggers { gitlabPush { buildOnMergeRequestEvents(true) buildOnPushEvents(true) } } authenticationToken('auhgtbereb675nksnwewrhbbe==') } }

We use this code as part of the script in a master job xml. We again use python Jenkins to create a master job and then build the created job. This job will in turn create a job for each of the repo in Gitlab. It is important to verify the url and replace the private_token with the one we got in Gitlab.

We need to now run the create_master_job script which will accomplish all of this. Open terminal and run the command python3 create_master_job.py

This has now created a job for each of the repo present in the Gitlab as shown below

Web hooks

Once all the jobs are created in the Jenkins, we need to create web hooks. For this we’ll run create_webhooks.py which will look at all the jobs created and add web hook to each of the repo in Gitlab using python Gitlab. Run the command python3 create_webhooks.py. Then head to Gitlab to see web hooks created for each project as shown below.

You can naviagate to Integrations part of any of the repo to find the hook created.

In the integrations part, once you scroll down, you can see the hook. Click on test and you should see a success message on top. As you can see, the url in the the web-hook is the project url of Jenkins job.

Now any push in the repo will automatically trigger a build in Jenkins. Lets try that out!

Lets edit the README.md file and push the changes to see if its triggering a build.

Once the changes are pushed, we head to Jenkins job for the same project MPAndroidChart and we can see builds have been triggered. On the left bottom corner, we can see builds and a message indicating Started ​by ​GitLab ​push ​by ​Administrator

Code Coverage

If the gradlew configuration and build.gradle is correct, builds should work fine and code coverage reports should be generated as below.

If a project does not build successfully, we have a repo for which Jacoco works very well. You can find that repo here

Understand

This part is to analyse the output of the Jenkins CI code coverage tool. By now you must have everything working for Gitlab and Jenkins. We use understand to generate the reports for the code analysis. The script understand.py interactively asks for the following parameters

  • Path to the folder containing understand tool
  • Path to the repo to be analyzed
  • path for the results to be stored
  • Name of understand project

Running the routine

Run the following command to initiate the script. This script is independent of directory due to the fact that its talking to the Github API directly.

python3 git_analytics.py <your_github_username> <your_github_password>

This will then ask for a a language for which you want to see the analytics for. You may input any language of your choice, but for this excercise input the exact same language as you did to fetch the repositories in 'fetch_repos.py'.

After you input the argument, the script will start talking to the API and loop over 15 (predifened range for repos in the script) repositories and analyse for each of the last 4 commits, which files were changed the most. This is indicative of more bugs arising in these file as larger changes tend to be more prone to failed testing.

It will output something like:

It will also create a file named “analytics.md”, which is the output file generated in markdown and will contain the output for all 15 repositories.

Note: You may delete the analytics.md file as its an auto generated file and run your script fresh to generated a new file.

Tests

fetch_repo_test.py runs the fetch_repo.py and checks whether the no of repos set to import from Github is indeed pushed to itlab

2. Plugin Installation

plugin_install_test.py installs the plugins by running the install_plugins.py and fetches all plugins installed in Jenkins to see if the plugins installed by the script was indeed installed.

3. Jenkins Job creation

jenkins_job_test.py runs the create_master_job.py to create jobs for each of the repo present in Gitlab. Then checks if no of jobs created are equal to the no of repos present.

4. Web-hook creation

Now once web-hooks are setup, we push a sample text file to one of the repo and then check if there was an increment in the build number for the corresponding Jenkins job.

Limitations of the Project

  • Gradle : Gradle configurations of projects can be very different and build often fails. It is very difficult to analyze each repo and then make changes to the job created in the Job DSL script. If the project is built using maven or sbt, again its very difficult to setup the build options in Job DSL for all possible build configurations sepcified in the project repositories.
  • Due to the programmatic creatino of webhooks, we were not able to put authentication of webhooks and they were created without the token. This can be overcome by creating the webhooks manually.
  • We are using python-jenkins package. While we can do almost all Jenkins tasks with the python-jenkins, some very specific things can’t be done. For example, we needed to configure Gitlab plugin in system configuration in Jenkins. We couldn’t do it with python-jenkins or jenkins-cli. The API support is not there for all possible plugin configuration. The only possible options are to use automation tools like selenium or to do it manually.

There you are you have successfully setup Gitlab and Jenkins, integrated JaCoCo and Understand Code analysis tools with it. I have tried to be as comprehensive as possible. Feel free to comment and leave any questions regarding this tutorial. If you like this blog don’t forget to like and share! And thanks for reading. Cheers!

Originally published at dkaushik94.github.io on May 15, 2018.

Topics of interest

More Related Stories