The CI/CD pipeline is one of the best practices for DevOps teams to implement, for delivering code changes more frequently and reliably. The fact is that it is so important to create your CI-CD cycle in order to implement new changes, test, and launch new features that matter for your users in a more reliable strategy way.

Continuous integration

It is the process of taking the developed code from the relevant repo, passing it through tests (unit, integration, etc.), packaging, and automating its installation in the environment to be tested.

Continuous Deployment

We can think of it as the last step of the process. CD deals with the distribution process of the system that successfully passed CI.

As test engineers, we all know that it is also so important to trigger our test automation when there is a new deployment in the project we are involved in. Do not forget that our test is still necessary even if the new deployment passed the CI. These two things are completely different.

The second important thing is to check whether the test automation we designed obeys the clean code rules and completely free from bugs and vulnerabilities. Therefore, in this work, it is aimed to create our CI-CD pipeline to provide such requirements that make our test automation better.

Before we start setting up our CI / CD pipeline, some of the platforms we use are briefly explained.

What is Sonarqube? Why do we use it?

SonarQube is an open-source platform, which is used for continuous analysis of source code quality by performing analysis on your code.

It is a tool that checks if the code in your project obeys certain rules. While doing this checking it does not compile the code. It is installed as a server and shows the errors in your project. It also gives you an example of how to fix these errors.

What is Jenkins? Why do we use it?

Jenkins is an open-source automation server written in java used for the Continuous Integration (CI) method. It helps us automate software development processes.

It reaches the project from the specified source and performs the requested operations. It delivers the results to the designated people. In this way, since our project is constantly tested, errors are detected quickly and kept in working condition.

Before dockerizing them, I want to explain the concept of Nginx which is going to be used for reverse proxy.

What is Nginx? Why do we use it?

When someone requests to open a webpage, the browser contacts the server of that website. Then, the server looks for the requested files for the page and sends them to the browser. This is only the simplest kind of request.

Nginx is especially suited for websites with high traffic and high demand input. It meets and transmits incoming requests to the server and transmits the returned responses to the client. You simply provide a URL like, and whenever people access that URL, your reverse proxy will take care of where that request goes. All requests will be coming into your network on two ports (80–443 / HTTPS — HTTPS), and the reverse proxy will take care of the rest.

docker-compose.yml :

version: "3.7"


server {


FROM jenkins/jenkins:lts


1. Docker-Compose

Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. Compose works in all environments: production, staging, development, testing, as well as CI workflows.

Services: Docker service will be the image for a microservice within the context of some larger application. When you create a service, you specify which container image to use and which commands to execute inside running containers. You also define options for the service including

Image: Specify the image to start the container from. Can either be a repository/tag or a partial image ID. For example, image : Nginx: latest will start a container from Nginx's latest image.

Build Configuration options that are applied at build time.

Content: Either a path to a directory containing a Dockerfile, or a URL to a git repository.

Dockerfile: A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession.

Network: Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name. Making a network is important since the Nginx must talk to other services (Jenkins and Sonarqube).

· Network driver bridge: Bridge networks are usually used when your applications run in standalone containers that need to communicate.

Container Name: Specify a custom container name, rather than a generated default name. If you do not specify, it will be the name of the file that your YAML file in.

Depends On: Express dependency between services. Sonarqube depends on Postgres DB in this work. If the service DB fails to start somehow, sonarqube would not be up as expected since it depends on the DB. Services are started according to the dependency order (i.e DB is started first.)

Environment: Using the Compose environment option allows us to declare environment variables and their values inside our Compose file. For instance, “JENKINS_OPTS= — prefix=/jenkins” is used to give a path to URL (localhost:<portNumber>/jenkins) .

Ports: In Docker, the containers themselves can have applications running on ports. When you run a container, if you want to access the application in the container via a port number, you need to map the port number of the container to the port number of the Docker host (HOST: CONTAINER).

Volumes: In order to be able to save (persist) data and also to share data between containers, Docker came up with the concept of volumes. If your volume configuration is wrong, you will see that, for instance, you are always redirected to the authentication page every time you start the Jenkins.

2. Nginx default.conf

You have to create or use the default.conf file that configures the settings of Nginx.

The server part is that you are defining the port receiving the incoming requests, what domain this configuration should match, and where it should be sent to.

  • listen: The IP address/port combination that this server block is designed to respond to. If a request is made by a client that matches these values, this block will potentially be selected to handle the connection.
  • server_name: This directive is the other component used to select a server block for processing. If there are multiple server blocks with listening to directives of the same specificity that can handle the request, Nginx will parse the “Host” header of the request and match it against this directive.

The next context that you will deal with regularly is the location context. Location contexts share many relational qualities with server contexts. For example, multiple location contexts can be defined, each location is used to handle a certain type of client request, and each location is selected by matching the location definition against the client request through a selection algorithm.

There’s only one setting that really matters to what we’re doing here, and that’s the proxy pass setting:

proxy_pass http://jenkins-local:8080;

You can’t set this to localhost. That’s because each Docker container is its own localhost, and it’d think you’re referring to the host of the NGINX container, which isn’t running Jenkins on port 8080.

Important Notes

  1. For installation Jenkins, you have to pull the initialAdminPassword inside var/jenkins_home/secrets. You can run the command as in the following (container id or name is the name of Jenkins container id or name)
- docker exec -it <container id or name > /bin/bash

2. Usually the initial credential configuration is like admin/admin for both Jenkins and Sonarqube. However, it is better to check and change it later properly.

3. Make sure in the docker-compose YAML file you put Jenkins prefix /jenkins in the env area. This is a crucial step for Nginx.

4. Make also sure that in sonarqube container, you remove the comment in front of the sonar.web.content and set it to /sonarqube (sonar.web.content = /sonarqube) in the file. (container id or name is the name of sonarqube container id or name)

docker exec -it <container id or name> /bin/bash

5. If you have troubles while reaching the location of default.conf : (container id or name is the name of Nginx container id or name)

docker exec -it <container id or name> /bin/bash

6. In order to get the reverse proxy to actually work, we need to reload the Nginx service inside the container. From the host, run

docker exec <container-name> nginx -t

This will run a syntax checker against your configuration files. This should output that the syntax is ok. Now run

docker exec <container-name> nginx -s reload

This will send a signal to the nginx process that it should reload. This must be done after you change the default.conf.

7. Make sure that your docker-compose.yml and Dockerfile are in the same directory.

For more information:

I hope you did all the steps successfully and enjoyed it.

QA Engineer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store