Continuous integration and continuous deployment has become one of the most common use cases of Docker early adopters. CI/CD merges development with testing, allowing developers to build code collaboratively, submit the master branch, and check for issues.
This allows developers to not only build their code, but also test their code in any environment type and as often as possible to catch bugs early in the applications development life cycle. Since Docker can integrate with tools like Jenkins and GitHub, developers can submit code in Git Hub, test the code and automatically trigger a build using Jenkins. Once the image is completed, images can be added to Docker registries.
This streamlines the process, saves time on build and set up processes, all while allowing developers to run tests in parallel and automate them so that they can continue to work on other projects while tests are being run. Since Docker works on prem, in the cloud or virtual environment and supports both Linux and Windows, enterprises no longer have to deal with inconsistencies between different environments types. Perhaps one of the most widely known benefits of the Docker CaaS platform.
In this post, firstly we will create a simple flask application. We will create an image for our application. Image will use redis for storage and it will be used as a dependency application. Python application will be a simple application. It will simple print “Welcome to Docker” on page.
We will be using two containers one for redis and one python application. Next we will create a script to test our python application. An image will be created to run this test script in a container.
So to understand about CI you should be familiar with Docker and Docker compose. Let’s have an overview of what is Docker and Docker compose.
According to the official documentation of Docker, Docker is a platform for developers and sysadmins to develop, deploy, and run applications with containers. The use of Linux containers to deploy applications is called containerization. Containers are not new, but their use for easily deploying applications is.
2.1 Docker container
A container image is a lightweight, stand-alone, executable package of a piece of software that includes everything needed to run it: code, runtime, system tools, system libraries, settings.
Containers isolate software from its surroundings, for example, differences between development and staging environments. Also, containers help reduce conflicts between teams running different software on the same infrastructure.
2.2 Docker image
An image is an executable package that includes everything needed to run an application–the code, a runtime, libraries, environment variables, and configuration files.
And when you have a Docker image, you run the container by using the image. So in nutshell, a container is a runtime instance of an image–what the image becomes in memory when executed (that is, an image with the state, or a user process).
3. Install Docker
So this was the overview of Docker. Let’s install Docker now. Docker Community Edition (CE) is ideal for developers. Small teams are looking to get started with Docker and experiment with container-based apps. So if you are not sure you should install CE.
3.1 Installing Docker on Ubuntu using the repository
1. Update the
apt package index.
sudo apt-get update
2. Install packages to allow
apt to use a repository over HTTPS:
sudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ software-properties-common
3. Add Docker’s official GPG key:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
3.2 Use the following command to set up the stable repository
1. Update the
apt package index.
sudo apt-get update
2. Install the latest version of Docker CE, or go to the next step to install a specific version. Any existing installation of Docker is replaced.
sudo apt-get install docker-ce
3. Verify that Docker CE is installed correctly by running the hello-world image.
sudo docker run hello-world
For installing Docker on other Operating systems you can follow instructions from here depending on the operating system you are using.
4. Docker compose
According to Docker compose official documentation, 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.
Using Compose is basically a three-step process:
1. Define your app’s environment with a
Dockerfile so it can be reproduced anywhere.
2. Define the services that make up your app in
docker-compose.yml so they can be run together in an isolated environment.
docker-compose up and Compose starts and runs your entire app.
4.1 Installing Docker-compose
On Linux, you can download the Docker Compose binary from the Compose repository release page on GitHub. Follow the instructions from the link, which involve running the
curl command in your terminal to download the binaries. These step by step instructions are also included below.
1. Run this command to download the latest version of Docker Compose:
sudo curl -L https://github.com/docker/compose/releases/download/1.20.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/Docker-compose
2. Apply executable permissions to the binary:
sudo chmod +x /usr/local/bin/docker-compose
3. Optionally, install command completion for the
4. Test the installation.
$ docker-compose --version docker-compose version 1.20.1, build 1719ceb
5. Creating Sample Python Application
In this section we will be creating a simple python application using
redis. Then we will Dockerize this application using
docker-compose. I am just creating very simple application which will just print “Welcome to Docker”. So that you can focus on the process rather than the application logic.
Create a folder Dockerci for our project.
$ mkdir dockerci $ cd dockerci
Now let’s create our python application and create a file app.py:
Python application file app.py
from flask import Flask from redis import Redis app = Flask(__name__) redis = Redis(host="redis") @app.route("/") def hello(): visits = redis.incr('counter') html = "
Welcome to Docker ! " return html.format() if __name__ == "__main__": app.run(host="0.0.0.0", port=80)
So this is a python application using
redis. Application just returning “Welcome to Docker” text in HTML.
As I have mentioned our application is using Redis and Flask. So we have to define these dependencies in our application before using them.
Let’s create a file
requirements.txt and add the content below in this file.
5.1 Dockerizing Python Application
As I have explained in Docker image, to Dockerize an application we have to create an image for application. We will use that image to run inside our container.
Let’s create an image for our python application.
FROM python:2.7 WORKDIR /app ADD requirements.txt /app/requirements.txt RUN pip install -r requirements.txt ADD app.py /app/app.py EXPOSE 80 CMD ["python", "app.py"]
Let me explain you the meaning of each line in this
FROM python:2.7here we are using python(2.7) as our base image because we are creating a python application. The number after colon is the version of python we want to use for our app.
WORKDIR /appwe are setting our work directory to
/app. Our code will reside at this location inside container.
ADD requirements.txt /app/requirements.txtas our application will be using Flask and Redis dependencies. And we have defined these dependencies in
requirements.txt, so adding this file too inside our work directory.
RUN pip install -r requirements.txtinstalling application’s
ADD app.py /app/app.pyadding application code to the image.
EXPOSE 80our application will be listening on port 80.
CMD ["python", "app.py"]this is the command that will be used to start our application.
So in this
Dockerfile we have mentioned all the resources that will be required by our application.
5.2 Using Docker-Compose
Now we have to create
docker-compose.yml file so that we can use the image created by
Now add following content to this file.
web: build: . Dockerfile: Dockerfile links: - redis ports: - "80:80" redis: image: redis
docker-compose.yml file says that it will be using two containers, as it has created two services
web for our application and
redis as our data store.
web will be using the current folder to build the image and using
Dockerfile we have created above, we will create an image for our application. It defines a link to the
redis service which will use the standard redis image from
5.3 Deploying and Running Python Application
We are ready with
docker-compose.yml. Let’s put our code into a container and run that container.
For this use the below commands:
$ Docker-compose -f ~/javacodegeeks/dockerci/docker-compose.yml build $ Docker-compose -f ~/javacodegeeks/dockerci/docker-compose.yml up -d
The first command will set up all the components needed by our application and the second will up our container. Let’s see how many containers are running currently. As I explained above there should be two containers, one for our python application and one for
Execute this command to check the running containers:
sudo docker ps
This should result in the below output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2316c8d35133 helloworld_web "python app.py" About an hour ago Up About an hour 0.0.0.0:80->80/tcp helloworld_web_1 21c3a097b00f redis "Docker-entrypoint..." 2 days ago Up About an hour 6379/tcp helloworld_redis_1
So as I can see both of the containers are up and running. And we are good to go.
Now let’s check if our application is running or not. We will make a curl request on localhost and port 80, as this is the port we have exposed to connect to our application. Execute this command below:
And this will give us the below output:
Welcome to Docker !
So our application is up and running in a Docker container. Next, we will be Dockerzing our testing environment by creating a testing script. This script will be checking in our application if it gives us the expected response or not.
6. Creating the Testing Environment
So we will create a testing environment to test our python application. It will be a simple test script which will make a
curl request to our running application and check if it’s returning “Welcome Docker” text in response.
6.1 Creating Test Script
Create a simple script testing.sh.
And add following content to this file.
sleep 5 if curl web | grep -q '
Welcome to Docker ! '; then echo "Success!" exit 0 else echo "Failed!" exit 1 fi
So this is a simple testing script just looking for “Welcome to Docker !” in the application response and it prints
6.2 Creating Environment for testing
So to test our application, we will be creating a testing environment similar to the application environment.
Let’s Dockerize the testing script we have created and create a
And add the following content to this file.
FROM ubuntu:trusty RUN apt-get update && apt-get install -yq curl && apt-get clean WORKDIR /app ADD test.sh /app/testing.sh CMD ["bash", "testing.sh"]
So here we simply put our
testing.sh into the image. In the last line is the command that will be used to run our application.
Also this image is using
ubuntu:trusty as base image and this will be used to install all the curl dependencies.
Next we will connect our testing environment to our python application. We will be using
docker-compose for this. Let’s create a file
And add following content to this file.
sut: build: . Dockerfile: Dockerfile.test links: - web web: build: . Dockerfile: Dockerfile links: - redis redis: image: redis
So this file define a
sut container and this container will be responsible for running our integration tests.
sut container is using current directory as build context and specifies the
Dockerfile.test. Its linking to the
6.3 Testing Application
So we are ready with our application running and our testing environment to test this application is also ready.
Let’s execute the below command and see if our script is able to test that application.
Docker-compose -f ~/javacodegeeks/dockerci/docker-compose.test.yml -p ci build
So we are ready with the build and now let’s up our container.
docker-compose -f ~/javacodegeeks/dockerci/docker-compose.test.yml -p ci up -d
Now lets check the output of our container:
docker logs -f ci_sut_1 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 28 100 28 0 0 1402 0 --:--:-- --:--:-- --:--:-- 1473 Success!
So by using Docker and docker-compose we can Dockerize our application and we can build a testing image, to test our running application.
We can use integration testing using
docker-compose.test.yml file. So Continuous integration is one of the most popular use cases for Docker. Teams looking to build and deploy their applications quickly use Docker, combined with ecosystem tools like Jenkins, to drive apps from dev, testing staging and into production without having to tweak any code. Docker and its APIs generate automated Docker image builds and make pushes to Docker registries simple, fast and automated.
8. Download the Source Code
You can download the full source code of this example here: Docker Test Example