Problem scenario
You want to create a rudimentary CI/CD pipeline (one that is simple and has no QA or automated testing of code). You want to trigger a deployment of code upon code being checked into a Git repository. You want to set up GitLab and Jenkins to be the main servers of this pipeline. You have two other servers; one is for developing code on and checking code into a Git repository and the other server is for receiving code once it is checked into GitLab. You want to use a "git" command to upload code to trigger the deployment of that code to a separate server. How do you set up the Continual (or "Continuous" as build and release Engineers usually insist) Integration and Continual Deployment pipeline from scratch?
Solution
Overview
This solution is limited in that it does not involve automated Junit or QA tests. It involves servers that are representative of other environments (e.g., development and production.) We consider this to be a rudimentary CI/CD pipeline because code is automatically integrated with a server when code from one server is uploaded it to GitLab.
There will be four servers:
- server1 will have GitLab (and it will communicate with the Jenkins server below)
- server2 will have Jenkins (and it will communicate with the GitLab server above) and push code to a server
- server3 will check code into GitLab (as a development server)
- server4 will receive code from the Jenkins server, server2 pushed down to it
Prerequisites
Preqrequisite #1
This solution assumes that GitLab can receive code pushed to it from mere git commands. That is, a server without GitLab can upload code to a repository to a GitLab project with just "git" commands. For comprehensive directions on setting up GitLab this way see this posting which will walk you through setting up GitLab from scratch.
Prerequisite #2
This solution assumes you have Jenkins set up to push code through its "Builds" to another server. For directions on setting up Jenkins like this, see one of these postings that most nearly fits your needs:
How Do You Get Jenkins 2.x Running on Linux to Do Code Deployments to Windows Servers?
How Do You Get Jenkins to Push Files to Other Servers in AWS?
How Do You Install Jenkins 2.x on Debian Linux Running in Google Cloud Platform?
Prerequisite #3
This assumes that there is no firewall rule between your Jenkins and GitLab servers that prevents connectivity from your GitLab server to your Jenkins server.
Prerequisite #4
The Jenkins server should have Git installed on it. Verify it is installed with "git --version" (with no quotes). Many distributions of Linux come with Git installed. If it is not installed and you are running a Red Hat derivative of Linux, you can try "sudo yum -y install git" (with no quotes). You may also want to see this posting if you are having trouble installing it on CentOS. For a Debian/Ubuntu Linux server, you may try "sudo apt-get -y install git" (with no quotes).
Procedures
1. Log into the Jenkins web UI. Install these plugins: Build Authorization Token Root, GitLab Plugin, and GitLab Hook Plugin.* To do this go to the Dashboard (if you log on, you will be normally be on the Dashboard), Manage Jenkins -> Manage Plugins -> Available. Check the box for the Build Authorization Token Root, GitLab Plugin (it will say "GitLab" by itself), and GitLab Hook Plugin.* Once these three options are checked, click "Install without restart".
2. Check the box for "Restart Jenkins when installation is complete and no jobs are running"
3.a. Summary of step #3: This step (#3) is done purely from the backend of the Linux servers. Configure passwordless SSH connectivity from your Jenkins server to your GitLab server.
Details if you need them:
Normally the Linux user on the Jenkins server that would be involved in this SSH key pair configuration (the user whom the id_rsa.pub file belongs) would be the "jenkins" user. If you do not know how to configure passwordless SSH connectivity, see this posting. You may want to use "sudo su jenkins" or "sudo su -; su jenkins" to become the jenkins user for the purpose of configuring SSH as the Jenkins server will need a ./ssh directory for an authorized_keys file. SSH once from the Jenkins server (usually under the jenkins user) to the GitLab server; the command will be: ssh jdoe@x.x.x.x
# Where jdoe is the local Linux user on the GitLab server whose authorized_keys file was updated. For this user you will want to either configure the settings to not verify the fingerprint with the initial connection or connect via SSH one time manually (with ssh jdoe@x.x.x.x) to confirm the fingerprint (e.g., type "yes" to a prompt to continue).
# x.x.x.x will be the hostname or IP address of the GitLab server. The command will be run as the "jenkins" user from the jenkins server (unless your jenkins server operates as a different Linux user).
# On the jenkins server as the jenkins user, you may need to run this command if there was no /home/jenkins/ directory:
chmod 600 /var/lib/jenkins/.ssh/authorized_keys
3.b On GitLab server backend, run this command:
sudo chmod o+rx -R /var/opt/gitlab/git-data/
# Be aware that this opens every repository to be readable with via other Linux users. As long as you control who has access to this server, it will be ok.
# It may not be recommended in a hardened environment. But this is necessary for this proof-of-concept.
4. Log into the web UI of the GitLab server. (If you do not know how, see this posting for more information.) From the web UI of the GitLab server, create a GitLab project that has at least one file. The project should be "Public." If you do not know how to do these steps, see this posting. If you already have a project, skip this step 4 and start on step 5.
5.a. From the web UI of the GitLab server, obtain the repository URL for the project. It will look something like this:
git@goodserver/my-contint-project.git
If you cannot readily find this repository URL, you can log into the web UI of GitLab, go to Projects -> Your projects. Click on the project you want. In the middle top of the resulting screen there should be a field in between a "Fork" button of the left and an icon of a cloud/download button on the right. It will say "SSH" or "HTTP" to the left of the URL (that will be in the format git@goodserver/my-contint-project.git). Copy this string.
5.b. Go to the backend of the GitLab server. Once you are at the character terminal, find the project. To do this search for name of your .git file with a command like this:
sudo find / -name my-contint-project.git # but substitute the "my-contint-project.git" with the name of your git file (as the name should have been determined in step 5.a).
5.c. Make a note of the full directory path found in step 5.b.
5.d. If you copied the HTTP URL in 5.a., you need not do anything else for step 5. If you copied the git@ constructor, replace the text between the colon ":" and the "/nameOfrepo.git" exclusive of these placeholders with the full directory path noted in 5.c. This should be done with no double forward slashes (no //). This transition may look like the following:
You start with this: git@x.x.x.x:root/my-contint-project.git
You end with this: jdoe@x.x.x.x:/var/opt/gitlab/git-data/repositories/root/my-contint-project.git
The lower (and in this instance the longer) of these strings will be used later on. (Remember to replace the /var/...my-contint-project.git with the path found; remember to also replace x.x.x.x with the IP address, or URL, of your GitLab server; remember to replace jdoe with the username involved in a backend, passwordless ssh session from the Jenkins server to the GitLab server)
6.a. Log into the GitLab web UI.
6.b. In the upper righthand corner, go to the circle icon next to an icon of a down arrow. Click on it and go to "Profile" (which should appear underneath this icon).
6.c. Click the pencil icon in the upper righthand portion of the screen. When you hover over this icon, it should say "Edit."
6.d. Click "Access Tokens" on the lefthand side of the screen.
6.e. Type an arbitrary name in the "Name" field.
6.f. Enter a date the token will expire.
6.g. Check the radio boxes under "Scopes" as you see fit. As a test or proof-of-concept you may want to check each one.
6.h. Click "Create personal access token."
6.i. Save the alphanumeric string under "Your New Personal Access Token". You will not be able to see it again.
7. This step is written for Jenkins 2.x. (If you have Jenkins 1.x, this step will be different for you.) While in the Jenkins web UI, click "New Item" in the upper lefthand corner. Enter a name, then click "Freestyle project." Click "OK" at the bottom.
8.a. For configuring this item, scroll to the "Source Code Management" section. Click the radio option for "Git".
8.b. Under the "Repository browser" field is a "URL" field. (Or depending on your version of Jenkins you will see a "Repository URL" field in the "Repositories" section.) Enter the GitLab URL there as constructed at the end of step 5.d above in this field you see.
8.c. For "Credentials" click "Add" -> "Jenkins". For the "Username" use "jenkins" with no quotes. For leave the password blank. Provide an "ID" and "Description" that will help your Jenkins human users identify this, but remember that there can be no spaces in the "ID" field. Click the "Add" button.
If you are deviating from the directions, enter the "Username" and "Password" the Linux "Jenkins" user would need to access the Git repo via backend commands. Ideally the "Password" will be blank and the "Username" will be "jenkins".
This assumes that the jenkins user can passwordlessly SSH into the GitLab server and read the Git repo (e.g., the .git file in its backend location as obtained in step #5d. One way to test which credentials you need is to log into the jenkins server on the backend, become the jenkins user, and run this command:
git ls-remote -h jdoe@x.x.x.x:/path/to/nameOfrepo.git HEAD
# where jdoe is the username on the GitLab server that had its authorized_keys file modified for the jenkins user to SSH from the jenkins server. Sometimes "git" works for jdoe.
# where x.x.x.x is the IP address or hostname of the GitLab server
# where /path/to/ is the full path to the git repo
# where nameOfrepo.git is the name of the Git repo.
# You could also run this to see what the credentials the jenkins user would need (using the substitutions described above):
git clone jdoe@x.x.x.x:/path/to/nameOfrepo.git
8.d. Optional step. This is for demonstration purposes. For the "Additional Behaviours" section click the "Add" button. Choose "Check out to a sub-directory". Type in a subdirectory in the field. This is a subdirectory you would have created on the backend of your Jenkins server or a subdirectory that you will create before you run this Jenkins job. Let's use "/tmp/d" as an example.
9.a. Scroll down to "Build Triggers".
9.b. Check the box for "Trigger builds remotely (e.g., from scripts)".
9.c. For the "Authentication Token" enter an arbitrary string (e.g., "coolfun" with no quotes).
9.d. Check the option for "Build when a change is pushed to GitLab. GitLab CI Service URL: "
9.e. Click the "Advanced" button that appears. (This button will be underneath this "Build when a change is pushed to GitLab. GitLab CI Service URL: " subsection.)
9.f. Underneath the "Allowed branches" subsection, click the "Generate" button. Copy this alphanumeric value to use later. Specifically keep this string handy for step #10. You will not need it for the rest of step 9.
9.g. Scroll down to the "Build" section. Click the "Add build step" button. Click "Execute shell". Enter a bash command. For this example we will assume that you have configured passwordless SSH to work as the jenkins user on the jenkins user by issuing a command such as this: ssh jdoe@x.x.x.x # where jdoe is the local user on the x.x.x.x server which is a client of the Jenkins server (e.g., a managed node also known as a server that receives code pushed down from the Jenkins server). (Step 3a has details on this configuration assumption.)
This example in this substep assumes that one SSH connection with the jdoe user from the Jenkins server to x.x.x.x has been established (or the fingerprint verification feature that happens with the first SSH connections has been turned off). Here are the five lines of code that should go into the "Command" text box:
rm -rf /tmp/my-continual-project
cd /tmp/
git clone jdoe@y.y.y.y:/var/opt/gitlab/git-data/repositories/root/my-continual-project.git
rsync -a --ignore-existing /tmp/my-continual-project/ userb@x.x.x.x:/tmp/my-continual-project/
rm -rf /tmp/my-continual-project
-Replace y.y.y.y with the URL or the IP address of the GitLab server.
-Replace x.x.x.x with the URL or the IP address of the client of the Jenkins server (e.g., the managed node aka the server that will get code pushed down to it from Jenkins).
-Replace "userb" with the user that would be involved in an SSH from the Jenkins server logged in as the jenkins user to the client of the Jenkins server (e.g., the managed node aka the server that will get code pushed down to it from Jenkins).
-Replace "my-continual-project" with the name of the Git repository you want to be pushed down.
(If you choose a different destination directory path from /tmp/, ensure that the "jdoe" user can write to that directory.)
9.h. Click Save.
9.i. Then draft in a Notepad application a URL like this based on this job you just created:
http://JENKINS_URL/buildByToken/build?job=JOBNAME&token=TOKENNAME
Substitute JENKINS_URL for the URL to your Jenkins server (e.g., with port 8080 if you use the default port). Substitute JOBNAME for the name of the job you just created (in step 7 above). Substitute TOKENNAME with the arbitrary string you entered in step 9.c. (an authentication token). Keep this draft of the URL handy for later.
10.a. Log into the GitLab GUI. The rest of the substeps in step 10 involve the GitLab web UI.
10.b. Click on Projects -> Your Projects.
10.c. Click on the project that will be the basis for triggering a Jenkins build (e.g., a one with a Git repo that when it receives a new file, you want a Jenkins build to start).
10.d. On the left click on "Settings" -> "Integrations".
10.e. In the URL field at the top, copy the draft you composed in 9.h. For the secret token use the value obtained in 9.f (with Jenkins).
10.f. Check whichever "Trigger" options you want. For this proof-of-concept, check the option for "Job events". Leave checked the "Push events" and any other options.
10.g. Click "Add webhook" (or if it was already created click "Save changes").
10.h. After the page refreshes, scroll to the middle of the page. You should see a section for "Webhooks". Click the "Test" button and go to "Push events" (or on option corresponding to what you triggered). It should say "Hook executed successfully: HTTP 201" at the top.
11. Test it. (This assumes one of the "Trigger" options configured in 4.f. was "Push events".
Draft the commands to test it and then run those commands. With the below sequence of commands, replace x.x.x.x with the FQDN or IP address of the GitLab server. With the below command examples (e.g., from "git clone..." to "git push origin master") replace "/var/opt/gitlab/git-data/repositories/root/my-contint-project.git" with the full path and name of the git repo as obtained in 5.c. above. Replace "my-contint-project" with the name of your Git repo. Replace jdoe with the Linux user on the GitLab server that can access the Git repo on the backend; in some cases jdoe could be "git" by itself.
git clone jdoe@x.x.x.x:/var/opt/gitlab/git-data/repositories/root/my-contint-project.git
cd my-contint-project
touch newfile
git add .
git commit -m "Added one file named newfile"
git push origin master
# Run the above commands from any server that has the ability to run the first git clone command above.
# The above commands should trigger a build of the Jenkins job. The job will push down code that does not yet exist on the destination server if the source repository has no such code.
Congratulations! You have now integrated Jenkins and GitLab. You have also configured a trigger for a Jenkins build and a rudimentary CI/CD pipeline (with no QA component).