Making a docker container for a Flask application

plaza_docker_ft

Recently I presented to you a project called PLAZA which basically serves as a Web UI for python scripts. It was a pleasure to see that this project was welcomed well and some folks even asked how they can get PLAZA to play with. My fault, I wanted to release it so bad that I missed the part describing how to actually get it.

One way to get PLAZA that was available since the beginning – is cloning the github repo and building python virtual environment with all the packages. And I understand that nowadays this way seems too complicated and the case is ideal for learning some docker!

Join me in this journey for integrating a simple Flask application into a docker container.

Prerequisites

Should I say that you need to install docker (if you running Linux) or docker toolbox (if you are on Windows or Mac) before we begin? Once you have it, go check the docker architecture overview to get the basics of this framework. I won’t mind if you go into other parts of the official documentation and come back skilled. I will try to build this artical in a linear fashion, solving one task at a time and linking the corresponding documentation section.

Composing a Dockerfile

To build a working container with a simple Flask application (i.e. no nginx and DB used) you will need just one file, Dockerfile . Seriously, one file will transform your Flask project into a docker image suitable for instantiating containers.

Docker can build images automatically by reading the instructions from a Dockerfile, a text file that contains all the commands, in order, needed to build a given image. Dockerfiles adhere to a specific format and use a specific set of instructions. You can learn the basics on the Dockerfile Reference page. If you’re new to writing Dockerfiles, you should start there.

So making a docker image with some application actually means that we have to build an entire environment for our app which will have nothing, but an OS, application itself and the tools it depends on. Take a look at this

Take a look at this  Dockerfile  I built for PLAZA and see what parts of it are clear to you from the first sight?

Do not worry if you don’t get it right away, I will cover each step consequently starting with a file structure. At this time you can go ahead and read this official comprehensive tutorial on building images at docs.docker.com site.

Organizing file structure

For starters, it is a good practice to place your application’ files in a separate directory. So instead of this structure:

you should have this:

This will make your life a bit easier when you will be composing instructions for your Dockerfile .

Choosing a base image

Dockerfile starts with a statement  FROM alpine:latest . This tells docker engine what base image we would like to use.  A base image serves as a ground layer in your layered docker container structure.

You can choose an official Linux distro image (like ubuntu) as your base, or you can go with an image containing not just Linux, but additional packages (like databases, web servers, java) built by contributors. Either way, you will end up searching in a place dedicated for storing images (both official and unofficial) called Docker Hub .

Alpine Linux as a base image

For a typical Flask application any Linux distro will do a good base image. Yet the tiny one in terms of disk space is Alpine Linux —  just 5Mb added to a kernel size! So I chose this image over ubuntu/debian as you can see.

Being small and all Alpine has some drawbacks you should be aware of — this comment on reddit covers most of them. Check it out if your app fails to install on Alpine, it might be that you hit an Alpine roadblock.

Installing Python

For now, we specified a simple Linux distro to use, of course, it is insufficient and we need to install Python. To do so I used a RUN  instruction on line 3  which runs Alpine’s package manager command.

Think of a  RUN  instruction as a way to perform tasks with shell commands as you would do normally within a shell environment. Note, that Alpine linux uses its own package manager, so instead of  apt-get install or  yum install we are dealing with  apk add .

On the next lines we are installing pip —  python3 -m ensurepip (yeah, in Alpine 3.3 Python3 comes without pip so we need this workaround with ensurepip module)

Installing Flask and python packages

Once we have Python in place, we can install all the packages we need. You can specify packages explicitly but the right way is to use requirements.txt  file exported from python virtual environment of your project.

Nice, another Dockerfile  instruction — COPY . Well, it copies ./web_app/requirements.txt  file from the host file system into the image you are building. Upon successful copy we can RUN pip3 install  and install all python packages we need. Of course, Flask is one of them.

Pay attention to the relative path, it is common practice to build an image from the root directory which has your app dir nested (as per file structure we discussed at the beginning).

Copying application files

With 9 lines we filled our image with all the necessary pieces. The closing step is pushing application’s files.

We met both of these instructions before, and their meaning is obvious. We simply copying application directory ./webapp to the directory created one step earlier in /opt/ directory. It is totally up to you where to place your application files.

Defining running options

Three more instructions and we are done:

At first set up a  WORKDIR where we placed our app so we can run it later without specifying the full path.

Next, tell docker engine to listen for connections on a given port with  EXPOSE . I am exposing port 80 since my Flask application configured that way that it listens on this port. If your Flask application running in a debug mode, then it defaults to port 5000.

The last step tells docker what command should it run at the very end. There is a good post describing what is CMD  command for and what are the differences with  ENTRYPOINT command? With  CMD ["python3", "app.py"] we run our Flask app as we would do without any containers in mind.

And do not forget to check Dockerfile’s best practices page.

Building a docker image

Building an image is easy once you have Dockerfile in place. Just go to the directory when your  Dockerfile is and issue  docker build -t <user/repository>:<tag> .

You can use whatever user/repository you want for testing purposes, but if you want to upload your image to the Docker Hub later, you should name it accordingly to your username and repo there. In my case I build my first image with this command:

The image has been built! You can see ensure of this fact with docker images  command.

Starting a container

Up to this moment, we haven’t had a container yet. What we have is the image ready to produce it. To run a container with a Flask application integrated we will be using docker run command (a simple tutorial on running containers).

Let’s elaborate a bit on this command:

  • with docker run <options> hellt/plaza:0.1 run we are telling to docker image to run a container from the image  hellt/plaza:0.1
  • with  -d parameter we specify that our container should run as a detached from our session (aka daemon)
  • to map a port 80 exposed in a Flask application residing in the container we are using -p host_port:target_port command. This let a user send HTTP requests to the port 44080 of the docker host and these requests will be transferred to the containerized Flask app

To see the state of the running container simply issue docker ps :

And to test my web app I can use a browser or curl:

The reason I used some IP address and not the localhost is because in mac os x docker host is running inside a linux VM, so I needed to use this VM’s IP to get to the container. On a Linux host I could use http://localhost:44080 .

Pushing a container to Docker Hub

Distribution and storing of your containerized app could be done via Docker Hub. Docker Hub is a perfect place for your container, people can pull your image from the hub and praise your efforts.

For starters, you have to have an account on the hub to start pushing your images. Once you have it log in from a terminal:

And push!

Cool, now this image is available for pulling to everyone on the Internet. I pushed the hellt/plaza  image without mentioning a tag. This effectively pushes all the images with user hellt  and repository plaza . As so if you want to push every version of your app to the hun – do not mention a tag. And, you got it, to push a specific image – tell it to do so.

Automated builds

Docker has a stunning feature called Automated Builds. With this option, your docker image will rebuild every time you push to a github/bitbucket repo automatically. One note on this – if you want to create an automated docker repo, you must not create a docker repo beforehand, new automated repo will be created and linked to your github repo.

So, in the end, my automated docker repo could be found as plaza-demo and I hope you will like it.

Roman Dodin

Network engineer at Nokia
Eagerness to learn multiplied by passion to share.
You can reach me at LinkedIn

You Might Also Like