Vignesh Dharuman
6 min readMay 18, 2020

--

Docker Beginner : Example Voting Application

Basic Introduction to docker concept and the terminologies involved in it can be read from here and here.

As we know the best way to learn something is by exploring one of its use-case, lets try understanding docker by exploring the sample-voting-application.

The architecture of the application is as below:

Voting-app architecture

Each component in our application are called services in docker term.We have five services in our application.

  1. voting-app : Front-end of the application written in python flask framework, which allows the users to cast their votes.
  2. redis : An in-memory data structure store, used as a temporary database to store the votes casted by the users.
  3. worker : service written in .NET, that retrieves the votes data from redis and stores it into PostgreSQL DB service.
  4. postgreSQL DB : PostgreSQL DB used as persistent storage database.

5. result-app : service written in node js, displays the voting results to the user.

There are many options when it comes to how we can deploy a dockerized application. We are going to look at deploying the application using docker-compose.

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.

The voting app repository can be cloned from git. Inside the app’s root directory(example-voting-app) we can find the docker-compose.yml file and sub-directories for each services containing their respective configuration codes.We don’t have to be familiar with all the stacks(like flask, redis, .NET, node.js) in order for us to understand the docker concepts. An high level understanding on what each service is doing and how they are related to each other is enough, we should be able to understand the docker concepts.

As mentioned in docker-compose definition, all our app configuration is specified in a yaml file named docker-compose.yml which can be found in app’s root directory. This file is the starting point from where docker begins to build our application. Lets go through the contents of the file.

version: “3”

The first line of our file specifies the version of the docker-compose used. Docker-compose has undergone some evolution on the format of how we specify our services. Hence the version is needed by the docker to understand how it has to interpret our file.

services:

This keyword indicates that all the services contained in our application will be specified under this section.

build: ./vote
command: python app.py
volumes:
— ./vote:/app
ports:
— “5000:80”
networks:
— front-tier
— back-tier

Lets go through the definition of our first service ‘vote’. The build keyword specifies the build configuration of the service. It can be specified as a string containing a path to the build context. Here we specify that the build configuration for our vote service can be found in the ./vote dir.( assuming that our current working directory is app’s root directory).

Now in order to get vote service’s build configuration, docker will search for a file named Dockerfile in the ./vote directory. This file will contain a list of instructions on how the image for the vote service has to be build. Lets quickly go over vote service’s Dockerfile.

# Using official python runtime base image
FROM python:2.7-alpine

# Set the application directory
WORKDIR /app # move to /app directory of the container. all the instruction below will be performed in /app directory of the container.

# Install our requirements.txt
ADD requirements.txt /app/requirements.txt # copy the requirements.txt in docker host to container.

RUN pip install -r requirements.txt # installing the modules given in requirements.txt

# Copy our code from the current folder to /app inside the container
ADD . /app # copy the contents in current host dir (./vote) to containers /app dir.

# Make port 80 available for links and/or publish
EXPOSE 80 # configure this service listen to port 80

# Define our command to be run when launching the container
CMD [“gunicorn”, “app:app”, “-b”, “0.0.0.0:80”, “ — log-file”, “-”, “ — access-logfile”, “-”, “ — workers”, “4”, “ — keep-alive”, “0”]

I hope the comments provided explain the action carried out by each instruction. For any doubt you can refer to the documentation on docker instructions.

With the vote service container build we go back to where we left in the docker-compose file. The next keyword ‘command: python app.py’ will run the command python app.py on the vote service container build.[one point to note here is we provide command key in the docker-compose file and also CMD instruction in our Dockerfile. Here the command provided by docker-compose will override the Dockerfile CMD when we run].

The volumes key maps the ./vote directory of docker host to /app directory of the container. this allows the code changes in ./vote dir to be reflected in vote service without the need to rebuild the image.

ports key maps port 5000 of docker host to port 80 of container. Traffics on port 5000 of host will be directed to port 80 of the container.

networks key specifies to attach this container to both the front-tier and back-tier networks which are user-defined networks.

At this point with the configurations for our vote service set, we can test it by running the vote service alone by executing the following command in app’s root directory

sudo docker-compose run — service-ports vote

— service-ports tells docker to build the image with port mapping.We should be able to see the logs of the flask server running. Go ahead and open the browser and go to http://localhost:5000 to see our vote service up and running. We can now stop the container by pressing ctrl + c.

Next we will look at the configuration provided for result service in docker-compose file

result:
build: ./result
command: nodemon server.js
volumes:
— ./result:/app
ports:
— “5001:80”
— “5858:5858”
networks:
— front-tier
— back-tier

contains similar configurations as that of vote service. The same flow of reading the ./result Dockerfile and building the image is followed here.

worker:
build:
context: ./worker
depends_on:
— “redis”
— “db”
networks:
— back-tier

The configuration of worker service contains some extra keywords that are not present in vote and result. Here the build context is specified as an object with the path specified under context. This allows us to pass optional argument like Dockerfile and args ( for now consider it as just an other way of specifying the build context). It again looks for the Dockerfile in ./worker dir and builds the image with that file’s instruction.

The depends_on keywords lists all the service which has to be up and running in order for this service to start.

redis:
image: redis:alpine
container_name: redis
ports: [“6379”]
networks:
— back-tier

For redis service, we a not going to build any custom image, we are just going to use an existing redis image named redis:alpine. When executing this line docker will try downloading this image from the docker hub which is a public registry for pre-build and tested docker images.

Here we also specify the name for the container and also the port on which the container should run.

db:
image: postgres:9.4
container_name: db
environment:
POSTGRES_USER: “postgres”
POSTGRES_PASSWORD: “postgres”
volumes:
— “db-data:/var/lib/postgresql/data”
networks:
— back-tier

Here again we specify to use the postgres:9.4 image from the docker hub.

The environment keyword is used to add environment variables. In this case we add the postgres_username and porstgres_password.

We can test each service one by one by running

sudo docker-compose run <service_name>

Finally with the configurations of all services defined, we are now ready to bring up our entire application ( bring up all services) by running the below command in the app’s root directory

sudo docker-compose up

For further references on Docker-compose command you can refer docker-compose documentation.

Also i recommend this YouTube tutorial for beginners.

I hope this blog was useful to you. This is my first blog, so please provide any comments on improvisation.

Thanks for reading. Happy Learning ..

--

--