Deploy a Python web app using Gunicorn in Docker

Today we are going to deploy a python web app using gunicorn as a docker container. First we need to write our Dockerfile.

First, we have to create our very minimalistic python web app. First, we will create a root folder to contain the whole project including the Dockerfile. Inside this root folder, we will create another folder called app where our web app source code will reside. So, the folder structure will look like this:

root folder
    |__ app
    |__ Dockerfile

Inside the app folder, we will create three files: requirements.txt, gunicorn.config.py and serve.py. The first file will simply contain all the requirements for our python app. If you are already know about python app development, you are already know the idea. The second script will contain all gunicorn configurations (Wow! I didn't see this was coming :P). The serve.py script, as the name suggest, will be the python script which will receive all request from gunicorn, do some processing and will return appropriate reply.

For now, we have only one requirement - Gunicorn itself. So this is the content of our requirements.txt file:

gunicorn==19.9.0

Next, our serve.py looks like this:

import gunicorn
import json

def app(environ, start_response):
    response_code = '200 OK'
    response = json.dumps('Hakuna Matata!').encode('utf-8')

    start_response(response_code, [
        ("Content-Type", "text/plain"),
        ("Content-Length", str(len(response)))
    ])

    return iter([response])

This is a very barebone version of a gunicorn app serving script. But this will do for our purpose.

In the gunicorn.config.py, we will define only two configs for now, so it will look like this:

bind = '0.0.0.0:10080'
workers = 2

CAUTION: Avoid pulling your hair out

In `gunicorn.config.py`, set the binding address as `0.0.0.0:PORT_YOU_WISH`. This is very important. If you set the binding address to 127.0.0.1 like in non-container way, then it will only receive request from localhost which is the docker container itself. That means, gunicorn won't be reachable outside of the container.

Now, it is time to writ our Dockerfile. We are going to develop our app based on python:3.6. Please note that, python:3.6 docker container expects your app to be in /usr/src/app. So, you must put your python app source files there.

We will add this line in our Dockerfile.

FROM python:3.6

Next, we copy our requirements.txt file first. The reason we are doing first because, everytime we add a new requirements, it won't invalidate the docker cache. Pip will install the missing packages.

COPY app/requirements.txt /usr/src/app/

Next, we make /usr/src/app as our work directory and install the requirements there:

WORKDIR /usr/src/app
RUN pip install -r requirements.txt

Now we copy our app source files in their designated directory:

COPY app/ /usr/src/app

We expose the port 10080 for obvious reason.

EXPOSE 10080

Next, we run the command to start up the gunicorn process:

CMD ["gunicorn", "serve:app", "-c", "/usr/src/app/gunicorn.config.py"]

We are done writing our Dockerfile. The complete Dockerfile looks like this:

FROM python:3.6

COPY app/requirements.txt /usr/src/app/
WORKDIR /usr/src/app
RUN pip install -r requirements.txt

COPY app/ /usr/src/app
EXPOSE 10080
CMD ["gunicorn", "serve:app", "-c", "/usr/src/app/gunicorn.config.py"]

Now it's time to build and run our docker container. To build the container we will run this command in terminal from within our root folder.

$ docker build -t docker-gunicorn .

And to start our container, we will run this command:

$ docker run -p 10080:10080 docker-gunicorn

While our container is running, we can visit http://localhost:10080. We wish you Hakuna Matata! 🙂

Of course, to see all the docker images you have, you can run this command:

$ docker images

And to see the running container:

$ docker ps

The complete source code is available on github:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Back To Top