Continuous Integration with GITLAB CI and Docker

Continuous Integration with GITLAB CI and Docker

Continuous Integration simply put is a software development practice where a group of developers working on smaller modules of a larger application integrate, build and test their changes as early and as often as possible. Without the right software tooling, this could be a very tedious and time-consuming task eating away a sizeable chunk of developers productive time. In this article we are going to learn how continuous integration can be achieved using GITLAB CI and Docker

Why GITLAB CI?

This question makes perfect sense in today’s world because there are so many options to choose from. We will set aside a comprehensive comparison of different CI tools for a different article, in this article, we will focus on GITLAB CI.

Here are a few things we will learn from this article:

Why GITLAB CI?

Let’s start by answering the purpose for this article, one of the best if not the best features of GITLAB CI is that its integrated with GITLAB version control system. Not having to switch between different tools for SCM and CI/CD and not having to deal with the integration between them is a great benefit.

Here are few other advantages of GITLAB CI:

  • YAML based build configurations: Not having to write the build instructions in any scripting language makes it easy to adopt and involves a smaller learning curve. YAML based files are easier to read and understand.
  • Pipelines: Building pipelines consisting of multiples stages is easy. GITLAB CI visually separates, tracks and logs individual stages.
  • Scalable builds: The CI builds can be distributed across multiple machines and it can be scaled on a need basis

Some other honorable mentions are Support for docker, Multi-platform, Multi-Language etc.

Creating a simple hello-world app

Let’s use a simple example to learn some GITLAB CI goodness. Lets get our hands dirty, shall we?

We will use the most basic flask hello-world app to demonstrate this, We recommend you to follow along to get a better understanding.

The pre-requisites are python, pip. Here is a good installation example. Now that our workspace is set up. Let’s go ahead and create a folder called hello-world which will be our project name. To create a flask project we would require the “flask” python module, it can be installed by running the following command, pip install flask.

Here is what our simple helloworld.py script looks like,

Let’s go ahead and execute our program:

App Launch
App Launch

 

Now, our app is up and listening on port 5000, let’s go ahead and fire up a browser and visit http://localhost:5000

First Launch
First Launch

There it is! Our very first python web-app!

Lets “Dockerize” our app:

Let’s “Dockerize” our app therefore making our application deployable anywhere. It will also help us realize some cool features of GITLAB CI. Here is what our Dockerfile look like:

Let’s look at some of the highlights of the above Dockerfile,

Line #1: We start with the python base image

Line #4: This is where we install the dependencies. Now that we already know that we need the Flask python module for our application to work, we need to place all the python dependencies under the file named requirements.txt. pip, which is our python package manager will parse through the file line-by-line and install the modules listed in it.

Since we have only one dependency, our requirement.txt only has one entry as shown below:

Line #6: We fire-up our app right in the docker start-up thus, the docker run command starts-up our application!

Setting up a GITLAB CI Project

Let’s start by setting up an account on https://gitlab.com and create a “Blank Project” with any name you like, make it public and initialize with a README.

GITLAB Project
GITLAB Project

 

Once the project is created, we will be taken to the Project Landing Page as shown in the following image:

Project Landing Page
Project Landing Page

 

We will be focusing more on the GITLAB features labeled as,

  1. The GITLAB CI/CD and
  2. GITLAB Container Registry

In order to get started with the gitlab CI/CD capabilities, we need to start by writing our .gitlab-ci.yml file which is used to manage our project.

This file must be placed at the root of our project and will contain the instructions to build our project. Let’s look at our .gitlab-ci.yml file

Let’s break down our script,

Line #1: We start with the image keyword, which defines which docker image must be used as the executor to build our application. We have chosen to the basic docker image with git installed on it.

Line #3: Here, we define a service, which is simply another docker container that runs during our job and links to our Docker image that the image keyword defines.

We use the docker:dind image as its recommended for building docker images using gitlab ci. Refer this article for other ways to achieve this.

Line #6: Here, we have defined the properties that will be referred during build time. The Property, CONTAINER_IMAGE is the tag that we will be using for our hello-world application

Here, we are also using some inbuilt environment variables that GITLAB CI offers,

$CI_PROJECT_NAME : The GITLAB project name, in our example this is hello-world

$CI_BUILD_TOKEN : This is the API token that will authenticate us to push images to the GITLAB container registry

Here is a full list of all the GITLAB CI variables.

Line #11: The keyword before_script defines the set of commands that will be executed before any job is executed. We will log in to the gitlab registry using the user gitlab-ci-token and use the inbuilt $CI_BUILD_TOKEN property to authenticate

Line #14: These are the different stages of our build job. Stages can be divided based on different tasks performed or phases of our build process and are declared globally using the stage keyword. Eg: Compile, Test and Deploy could be “stages” in a simple project build.

In our example, we will be using only one stage, build and stage

Line #16: The job 1, keyword defines the script that needs to be executed and is bound to the build and stage, stage declared earlier.

Our job executes 2 simple scripts, docker build and docker push, we version our images based on the GIT Commit SHA which is unique for each GIT commit.

Great! Now we are good to commit our .gitlab-ci.yml file into our project repository. GITLAB CI automatically builds the projects once a commit is made.

Let’s introduce a ripple by making a dummy commit 🙂

GITLAB CI in action
GITLAB CI in action

 

There it is! as easy as that to setup a simple project

All the GITLAB CI pipelines can be accessed from the CI/CD link from the side panel which is shown in the following image:

GITLAB CI
GITLAB CI

 

Lets examine our build logs now:

Logs 1
Logs 1
Logs 2
Logs 2

 

Lets look at the GITLAB container registry:

container registry
container registry

 

The container registry shows the list of all the pushed images!

Testing the App

Now, Lets pull this docker image and see if its working as expected.

docker run
docker run

Lets check the app by accessing http://localhost:5000

Final Launch
Final Launch

There it is! Our docker app in its full glory.

Conclusion

We have reached to the concluding part of our article, we learnt that GITLAB CI is a really a winner in terms of providing integrated CI/CD capabilities. We spent almost no time in integrating our SCM tool to our CI/CD tool, setting up the CI/CD pipeline is seamless.

Since the .gitlab-ci.yml is written in YAML syntax, there is no need to learn any complex scripting language.

This is the very first article on our blog! Thanks for patiently reading through it. The full project can be found under CodeBabel/hello-world feel free to fork it and try the project setup yourself!

Please Subscribe to our blog and share your feedback below!