How Do You Configure Jenkins Builds to Avoid Slave Servers That Are Either Windows or Linux?

Problem scenario
You are configuring Jenkins jobs.  You want certain jobs to only run on Windows slaves (e.g., PowerShell jobs) and certain jobs to only run on Linux slaves (e.g., Bash scripts).  You want to force a job force a selection of a slave server to avoid Windows or Linux servers.  How do you have Jenkins avoid slave servers based on the OS architecture?

Solution
1.  Go to the job and click on "Configure" or create a new job.

2.  Search for the word "Restrict".  

3.  Click the checkbox for Restrict.  Enter "windows" with no quotes if you want the job to only run on a Jenkins slave server running Windows.  Enter "linux" with no quotes if you want the job to only run on a Jenkins slave server running Linux.  

Optional step
There are more sophisticated restrictions you can place on the Jenkins build.  Click on the blue question mark icon on the right.  This will explain the syntax for how to force the Jenkins jobs to run on Linux or Windows exclusively.  One common usage is to use as a label the hostname of the slave server.  This will restrict the builds to this slave server exclusive of other servers.

How Do You Find the Version of SonarQube That You Have Installed?

Problem scenario
You have installed SonarQube.  You want to know what version you are using.  How do you find out?

Solution
Go to the web UI for SonarQube.  It may be as simple as opening a web browser and entering the external IP address of the server.  Without logging in scroll to the bottom.  You should see something like this near the bottom:  Version 6.7 (build 33306)

How Do You Change the Base URL for SonarQube?

Problem scenario
Currently you have to enter a URL like this to access SonarQube's web UI:

http://servername.com:9000/sonar

You want to be able to type no path (e.g., no "sonar") in the web browser.  You want to just enter the hostname in the web browser and the port number of the SonarQube server.  What should you do?

Solution
Go to the conf directory [that is a sibling] with the sonar.properties file.  Find the "context" stanza.  It will have a string "sonar.web.context".  Comment it out.  By default it will be "/".

To learn more, see this posting.

How Do You Prepend or Append to a Variable in Ansible?

Problem scenario
You are using a variable in Ansible.  You want to attach a string to it to augment this variable.  You can rename it so later in the playbook you can call this modified, augmented version of the variable.

You assign the variable like this:

foo: " {{ bar }}"

You want foo to have an extra string (e.g., "/subdirectory/path/to").  What do you do?

Solution
Do the append inside the braces.  The string you append should be inside single quotes.  Here is an example of appending a string to a variable:

foo: " {{ bar + '/subdirectory/path/to' }}"

Here is an example of prepending a string to a variable:

foo: " {{ '/subdirectory/path/to' + bar }}"

With two variables in {{ }} braces with strings of regular text, the syntax is a little more tricky.  We advise against using the + operator and single quotes.  Here is an example of correct syntax with previously defined variables "foo" and "vara":

varcomp: "{{ foo }}/path/to/version-{{ vara }}.exe"

The above will expand the variables "foo" and "vara" while inserting "/path/to/version-" (without quotes) in between these two strings; it will also append ".exe" (without quotes) at the end of this string.  The above will be assigned to a variable called varcomp.

How Do You Troubleshoot the SonarQube Problem with Nothing Displaying in the Web Browser?

Problem scenario
You go to the SonarQube web UI to log in.  You see nowhere to log in but no error either.  What could be wrong?

Solution
Have your web browser accept cookies.  If you block cookies and using Apache web server with SonarQube 7.x, you may experience this problem.

What is the Sonar Scanner?

Question
You have read about the Sonar Scanner.  What is it exactly?

Answer
It is now called the SonarQube Scanner -- not the Sonar Scanner.  Source: https://docs.sonarqube.org/latest/

"The SonarQube Scanner is recommended as the default launcher to analyze a project with SonarQube."  This quote was taken from
https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner.

What Is the Difference between a Node and a Pod in Kubernetes?

Question
You are using Kubernetes.  You read about Nodes and Pods.  What is the difference?

Answer
A Pod is a collection of one or more containers (e.g., Docker or rkt containers).  A node is a server that is the home of one or more Pods.

"A Node is a worker machine in Kubernetes and may be either a virtual or a physical machine..." (taken from Kubernetes.io).  A Node can support one or more Pods.

"A Pod always runs on a Node." (Taken from Kubernetes.io.) A worker node is the following: kube proxy, kubelet, and a container runtime. A Pod can be defined as an "atomic unit of scheduling" according to page 6 of Kubernetes Patterns by Bilgin Ibryam and Roland Huß (O'Reilly), Copyright 2019 Bilgin Ibryam and Roland Huß, 978-1-492-05028-5.

"Each individual non-master node in your cluster runs two processes:

  1.   kubelet, which communicates with the Kubernetes Master.
  2.   kube-proxy, a network proxy which reflects Kubernetes networking services on each node."

(The numbering of the two items above was introduced for clarity, but the above statements were otherwise quoted from Kubernetes.io.)

To learn more about Pods, see this Kubernetes.io page. Pods are ephemeral (according to page 48 of Kubernetes in Action). A Pod is "the smallest deployable object for building applications" according to this page. The term pod can be a group of Killer Whales; Docker's icon is a whale.

There is a Downward API in Kubernetes which has information about a given pod and its node. You can read more about this component (that has information about pods and nodes) here.

You may want to consider purchasing a book on Kubernetes as it is arguably the most in-demand skill with the greatest potential growth.  Benjamin Franklin said that an investment in knowledge pays the best interest.

How Do You Find the Number of Active Connections That an Nginx HTTP Load Balancer Has?

Problem scenario
You have an Nginx instance configured to be an HTTP load balancer (aka invisible landing page, pass-through distributor or reverse proxy).  You want to analyze the inbound web traffic that your Nginx server is currently receiving.  How do you find out how many active connections there are through the load balancer?

Solution
1.  Modify the /etc/nginx/conf.d/default.conf file in the Nginx HTTP load balancer.  In the server section (also known as the server block of the .conf file) and inside the braces themselves of this section create these three  lines:

location /nginx_status {
stub_status on;
}

# The above will appear inside server  { ... }.

2.  Restart the Nginx service of the Nginx HTTP load balancer.
3.  Run this command from the back-end of the OS that has Nginx: curl localhost/nginx_status

Bonus problem scenario
How do you do the above when Nginx is running in a Docker container?

Bonus solution

It is very much the same.

1.  Go inside the Docker container.
2.  Modify the /etc/nginx/conf.d/default.conf file

In the server {} section create these three lines:

location /nginx_status {
stub_status on;
}

3.  Restart the Docker container.
4.  Go inside the Docker container again.
5.  Run this command from the Docker container that has Nginx: curl localhost/nginx_status

FFR
If you want to need to troubleshoot problems with your Nginx load balancer, see this link.

How Do You Set up a Basic CI/CD Pipeline with GitLab and Jenkins?

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

How Do You Create a Docker Container?

Problem scenario
You want to create a Docker container.  What do you do?

Solution

Prerequisites
This assumes you have installed Docker on your Linux server.  If you have not, click the link below corresponding with your distribution of Linux:

CentOS/RHEL/Fedora
Debian/Ubuntu
SUSE

If you want to use a Dockerfile to build a Docker image, see this posting.

Procedures
1.  Find the image ID to base the container on.   Run this command:  docker images

# In the output, look for an alphanumeric value under the "IMAGE ID" column that corresponds with the REPOSITORY and TAG that you entered when creating the image.  (For example, to create a Docker image, you may have run a command like this:  docker build -t "foobar:contint".)

The output of the "docker images" command include something like this:

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
foobar              contint             d2b34ea87bda        3 seconds ago       465 MB

2.  Create the container with this command:  docker create -it d2b34ea87bda bash
# Replace "d2b34ea87bda" with the image ID that you want the container to be created out of.

3.  This step is optional, but if you want to use your Docker container, run these three commands:

docker ps -a  # find the container ID
docker start 5e0bc0b18e36  # replace "5e0bc0b18e36" with the container ID associated with the output from the above command.  It should be identifiable because of the CREATED column is temporal (and based on when the container was created)

docker exec -it 5e0bc0b18e36 bash #  replace "5e0bc0b18e36" with the container ID used in the above command

4.  This step is optional.  If you want more information about this whole process, see this external link.