Yawme Containerization

This article is dedicated for Yawme

When I wrote this post, I was on an internship at Yawme Badr Interactive and had a job for making use of Docker for their flask application. I wish this article could help many people in learning Docker.

Actually, the main goal is load balancing the application. Because lately, they found their application are getting heavy handling a lot of user requests to the server.

The approach is using Docker for making container and isolates the application environment so it will be easier to maintain and load balance. The load balancing stack is not defined in the initiation state. They said I could learn about HAProxy and other load balancers, but I found that Docker Swarm (explained in next article) might also satisfy the need.

There are lots of articles explaining about Docker and its advantages (including my another on going article, just wait for it 😃) so I’m not going to explain about it here.

So, here’s what I’ve done:

  • Create Dockerfile (and .dockerignore) for docker image definition
  • Push to Yawme’s private repository on docker hub

Dockerfile

the files and commands below are just an example that can be used generally for any flask-gunicorn application (with some modification of course)

# DockerfileFROM python:3.5COPY requirements.txt manage.py /
COPY migrations /migrations
COPY src /src
COPY static /static
RUN pip install -r requirements.txtENV LOG_FILE="-" \
LOG_LEVEL="debug" \
WORKERS="3"
CMD python manage.py db upgrade; gunicorn src.server:app --access-logfile $LOG_FILE --log-level $LOG_LEVEL --workers $WORKERS --bind 0.0.0.0:80

This image definition is based on python’s image version 3.5. It copies all required files, installs dependencies, set up default variables for running gunicorn, and run it.

You probably ask why would I use ENV to set gunicorn’s arguments? Why wouldn’t just run it explicitly? Well, if we use environment variable, we can overwrite it when running the image as a container.

There’s also a file called .dockerignore which behave and formatted like .gitignore that ignore files sent to docker daemon for building the image. This could be useful for ignoring sensitive file, such as configuration files

Optimization

There are some optimization and other configuration options that may be applied on this image definition:

  • Docker image should only contain files that used for running the application, not for the preparation. In this case, requirements.txt and migrations file would be best if not included inside the image. But we need these files for building up our application’s environment, how could we do this? We can make use of Multi-Stage Build, dividing the image definition into two parts: setting up the environment, and running the application.
  • RUN, CMD, and ENTRYPOINT can be written in exec form or shell form. The documentation said that exec form is preferred than the shell form. But I haven’t know how to set the last command to be on exec form, mostly beacause it consist of two command (the migration with python manage.py and running the application with gunicorn)

Push to Docker Hub

After you’ve done with the image definition you can build the image, tag it, and push it to docker hub

$ docker build --tag yawme/example:latest .

Run the command above in the directory which contains Dockerfile

You also need to have a docker hub account, create a repository there, and log in to it from your CLI

$ docker login

If you already logged in to another account, you can switch to a defined account using -u <username> argument

When everything is ready, just push it.

$ docker push yawme/example:latest

The command above will try to push image with yawme/example:latest label to yawme user account, search for example repository, and push it with latest version label

After it, you can pull your image from any other server you have

$ docker pull yawme/example:latest

Don’t forget to login first if you want to pull from a private repository

Run It

You can create a container and run it based on the image you’ve created by using:

$ docker run yawme/example:latest

If docker can’t find the image on local repository, it will try to pull the image first from the online repository

The above command is documented as follow:

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

You can override the CMD command from image definition by adding [COMMAND] and [ARG...] as arguments to docker run command

The $ docker run command is actually equivalent with $ docker create and then $ docker start with the newly created container’s id

Inspection!

We can list all of the images and containers in our machine by using these commands

$ docker image ls
$ docker container ls

The container created itself is like a standalone machine, so we can ‘log into’ the machine and do stuff there. The change made will be written in the top layer of the image (as this is the only writable layer)

$ docker exec -it <container_name|container_id> bash

Other commands

Basically, all of the command needed for interacting with docker image and container are documented well as $ docker image and $ docker container child commands.

At the begining, for learning purpose, I recommend to use GUI tools for managing Docker and its stuff. One of my favorite is Portainer.

You can run it using these two simple command only once

$ docker volume create portainer_data
$ docker run -d -p 9000:9000 --restart always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer

And after it, you can access it through localhost:9000

Thanks to salingga yusrianda

Muhammad Ashlah Shinfain

Written by

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade