DevOps. Continuous integration with Jenkins as an example

    This article will present the main ideas of Continuous Integration, as well as an example of quick deployment of Jenkins on a project.

    Terms and definitions

    Before proceeding to the main part, it is necessary to define the used terms.

    Continuous Integration (CI) — is a practice of software development, which consists in merge of working copies into a common main development branch several times a day and performing frequent automated project builds to quickly identify potential defects and solve integration problems. [wiki]

    Jenkins is an open source continuous integration project written in Java. [jenkins.io]

    Automated testing is a software verification process in which the basic functions and steps of a test, such as launching, initializing, executing, analyzing, and outputting a result, are performed automatically using automated testing tools. [protesting (in Russian)]

    A bit about CI

    Continuous Integration is currently one of the practices used in the Agile methodology. In similar methodologies, it successfully combines with other practices, such as unit testing, refactoring, coding standard. But even without them, you can benefit from continuous integration. The main idea follows from the definition of Continuous Integration – finding an error at an early stage is easier for development and cheaper for business. Therefore, if great changes are made to the project, it is necessary to conduct testing (unit tests, automated tests).

    It is worth noting that there can be several launch modes:

    • tests run after each commit – this is an ideal option for unit tests and controversial for end-to-end, why so I’ll explain below;
    • tests run once a day (most often at night). This option is not suitable for unit tests, since the application can remain in non-operational state for a long time, but a pretty good option for automated functional end-to-end tests, especially when tests take a long time;
    • tests are run before the release – one of the worst cases of use, but it can also be used in conditions of limited resources, and new functionality developed in its branch is checked once or several times a day.

    There are a lot of tools for CI:

    • from the local ones we can distinguish – GitLab CI, TeamCity, Bamboo, Jenkins, Hudson, Circle CI;
    • Cloud – BitBucket Pipelines, Heroku CI, Travis, Codeship, Buddy CI, AWS CodeBuild.

    Jenkins

    Why is it worth using Jenkins:

    • free and reliable;
    • there’s many of work manuals, which means it is easier to learn how to work with it;
    • easy to backup and deploy on another machine for 5 minutes;
      you can control it by configuring and moving xml files.

    Also I would note the following downsides:

    • frequent release of new versions if not LTS is used;
    • integration with various services is tied to many different plugins;
    • sometimes there can appear a task that requires a non-standard approach.

    Let me give you a memorable example. It is necessary to support several versions of the product that work with different versions of java, and if the project launched with the wrong version, then the tests began to fail stably. To do this, I had to add a plugin – Environment Injector, also add a step in the build, which, depending on the version of the product, changed the working environment settings to use a specific version of java:

    In the situation of the case described in the example above, on TeamCity it was enough to indicate java version when starting the run.

    Also, I’d like to note that in this case, Jenkins was used exclusively to run automated tests, because they were long enough and required a high level of performance.

    The ideal development process can be represented as following:

    • developer sends the code to the repository;
    • on the continuous integration server, changes are combined with the main code, unit tests are performed;
    • artifacts obtained in the previous step are loaded into a separate test environment, where the application is tested by autotests;
    • then everything is checked to get into production;
    • deployment on production.

    Jenkins installation

    Quite a lot and in many details has been written about the installation, the best manual of course from the developers is Installing Jenkins.

    I want to share my backup experience and subsequent installation from a saved copy.

    Backup

    You can use the built-in plugins, but there may be compatibility problems for different versions of the plugins, so for this purpose we use bash scripts that were a part of a separate build that was run several times a day\week. For example, jenkins-backup.sh script kept current working configuration: view, job, environment settings, plugins, which were saved in a separate archive, which can be transferred and further used.

    Installation from a saved copy

    1. You need to install java and an archiving tool, in our case it’s unzip.
    2.Unzip previously saved file (after going to the directory with the saved archive):

    unzip Jenkins.zip -d %path_To_Jenkins%

    3. Go to the Jenkins directory and run Jenkins with the command.

    cd %path_To_Jenkins% && java -jar jenkins.war

    4. Go to the WEB interface using 8080 port.

    If you are working with linux machine via ssh, then after closing the connection, Jenkins will be probably stopped. In order to avoid this behavior, you can use the following technique: execute the command with the & symbol added – as a result, the command will be executed in the background, and control will be returned back to the command line, and so that when disconnecting from a remote system, running tasks do not end, you can use the nohup utility, that allows processes to continue running even after you log out.

    nohup java -jar jenkins.war &

    Adding Jenkins to services

    In order to avoid the situation described in the previous paragraph on linux systems, you can install Jenkins as a service, so that every time after a reboot Jenkins starts automatically. To do this, create the /etc/systemd/system/jenkins.service file with the command:

    1. sudo cat /etc/systemd/system/jenkins.service
    2. sudo vi /etc/systemd/system/jenkins.service

    and add contents to jenkins.service:

    1. [Unit]
    2. Description=Jenkins Daemon
    3. [Service]
    4. Environment=”JENKINS_HOME=%path_To_Jenkins%”
    5. ExecStart=/usr/bin/java -jar %path_To_Jenkins%/jenkins.war
    6. User=имя текущего пользователя
    7. [Install]
    8. WantedBy=multi-user.target

    Restart the service: sudo systemctl daemon-reload

    Command to start jenkins service: sudo systemctl start jenkins.service

    restart sudo systemctl restart jenkins.service

    It is important to note that jenkins.war file can be located anywhere. In order to “pick up’’ current project parameters, you can use the command that will mounting Jenkins section to working jenkins:

    sudo mount -o bind /%path_To_Jenkins%/ ~/.jenkins/

    This option will only work until the system reboot, so you can create a symlink in the directory ~/:

    cd ~/ && sudo ln -s /%path_To_Jenkins%/ .jenkins

    Adding and preparing to work of node in Jenkins

    This section is written taking into account the practice that the life cycle of Jenkins and node is limited, for example, to 2 weeks or a month.

    To add a slave node to Jenkins, you can add each machine by hand, but because of this, you need to constantly go to the machine and perform the same operations, and this leads to the idea that everything can be automated as much as possible.

    To do this, it’s enough to create several Jobs that will execute sequentially:

    1. machines order to add as a slave node in Jenkins;
    2. add previously ordered machines to Jenkins using rest api or Jenkins cli;
    3. deploy the required environment.

    All of the above actions can be performed using additional tools: ansible – to deploy and configure the necessary parameters and docker – both to deploy Jenkins and to set the environment on slave nodes.

    Conclusion

    The main thing is that the phrase “continuous integration” on your project should be not only beautiful new words, but also actions. And if you, at least minimally, follow the principles of CI, then there will be much less problems in development life cycle, and the process will be more pleasant. A survey with interesting statistics on the use of CI on the project is https://habr.com/post/122381/ (in Russian), which shows that just a few years ago, CI was practically not used among the respondents.

    Useful links

    1. Official Jenkins documentation – https://jenkins.io/doc/
    2. Jenkins Cookbook – https://github.com/chef-cookbooks/jenkins
    3. The article “Why does a programmer need Continuous Integration and where to start” (in Russian) – https://habr.com/post/353194/
    4. The list of CI systems, testing and deployment tools – https://github.com/ciandcd/awesome-ciandcd