Many articles of this kind exist, but most of these tutorials start from the easiest Flask example possible:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
Although these examples are very useful to understand the Docker setup, the objective of this post is to present a situation that is closer (just a little bit) to the real life. The final Docker image will contain the REST services, and it will be possible to install the whole application on any machine supporting Docker. In addition to having a fully functional web application that can be quickly deployed, this approach also allows the versioning, the configurability, the portability and the share of the deploy objects, a.k.a. the Docker images.
The architecture is composed by two back-end modules: the main REST service and an additional service implemented as a Flask blueprint. A Flask blueprint “works similarly to a Flask application object, but it is not actually an application. Rather it is a blueprint of how to construct or extend an application“. The use of such objects allows the implementation of a modular back-end.
Back-end projects have been organized in a core and a rest package to isolate even more the responsibilities of each element. The structure of such projects is shown below:
simple_flask/
├── requirements.txt
├── setup.py
├── simple_flask
│ ├── core
│ │ └── core.py
│ └── rest
│ └── rest.py
└── simple_flask_test
├── core
│ └── core_test.py
└── rest
└── rest_test.py
Enters Docker. The final goal is to encapsulate the whole application inside Docker. As shown in the next figure this process will be seamless for the final user, that will keep interacting with the application with no extra settings required.
The source code of all the projects used in this tutorial is available on GitHub: feel free to download, fork and/or star it!
Project | URL |
---|---|
Simple Flask | https://github.com/Kalimaha/simple_flask |
Simple Flask Blueprint | https://github.com/Kalimaha/simple_flask_blueprint |
Simple Flask Dockerizer | https://github.com/Kalimaha/simple_flask_dockerizer |
As mentioned before blueprints are objects used to create a modular application. This simple component follows the structure previously described and contains a core library and, of course, a blueprint. The only function of the core is used to greet the user:
def say_hallo(name=None):
s = 'Hallo ' + str(name) + '!' if name is not None else 'Hallo!'
return s
The blueprint itself is very simple as well: it exposes two routes to greet the user, one generic, and one customizable with the user name. The resulting service takes advantage of the core library to do so:
from flask import Blueprint
from simple_flask_blueprint.core.blueprint_core import say_hallo
bp = Blueprint('simple_flask_blueprint', __name__)
@bp.route('/')
def say_hallo_service():
return say_hallo()
@bp.route('/<name>/')
def say_hallo_to_guest_service(name):
return say_hallo(name)
As shown by the code the development of the service is very similar to the standard Flask, with the only exception of the Blueprint object declared at line 5.
The main back-end module has a core library as well, with the same
say_hallo
function, which returns a message that is only slightly
different from the previous one:
s = 'Hallo ' + str(name) + ' from CORE!' if name is not None else 'Hallo from CORE!'
This difference will be used to test that both the main service and the
blueprint are working correctly. The main REST service, defined in rest.py
,
register the blueprint on the /blueprint
entry point as follows:
from simple_flask_blueprint.rest.blueprint_rest import bp
class RootREST:
def __init__(self, host, run_flask):
[...]
self.app.register_blueprint(bp, url_prefix='/blueprint')
In addition to that the RootREST
class exposes two routes, similar to the
ones described for the blueprint, but it also starts the Flask engine:
if self.run_flask:
self.app.run(host=self.host, debug=True)
Once the engine is up and running, it is possible to invoke the REST service
through the browser at http://localhost:5000/
(that will display Hallo from CORE!
) or
http://localhost:5000/Kalimaha/
(which will display Hallo Kalimaha from CORE!
instead). As mentioned before
the service has been designed to be modular through the use of blueprints. It is
possible to test such blueprint by hitting the
http://localhost:5000/blueprint/ URL
(Hallo!
), or
http://localhost:5000/blueprint/Kalimaha/
(Hallo Kalimaha!
).
The most important file is the Dockerfile, that basically tells Docker what to do. Some parts of this file deserve to be highlighted, starting with:
FROM ubuntu:14.04
RUN apt-get update
These instructions generate a new container starting from Ubuntu and update its repositories. In the next step Docker is instructed to install Python, and few other useful things, such as Virtualenv, in the newly create container:
RUN apt-get install -y -q build-essential python-gdal python-simplejson
RUN apt-get install -y python python-pip wget
RUN apt-get install -y python-dev
RUN pip install virtualenv
Subsequently the script creates a folder for the source code, creates and run a virtual environment in it, and finally adds and installs all the requirements:
RUN mkdir deployment
ADD requirements.txt /deployment/requirements.txt
ADD start.py /deployment/start.py
RUN virtualenv /deployment/env/
RUN /deployment/env/bin/pip install wheel
RUN /deployment/env/bin/pip install -r /deployment/requirements.txt
Requirements are specified through a simple text file:
watchdog
Flask
flask-cors
https://github.com/Kalimaha/simple_flask/archive/master.zip
https://github.com/Kalimaha/simple_flask_blueprint/archive/master.zip
This file tells Docker to install watchdog, Flask and flask-cors from Python repositories, while back-end modules will be retrieved and installed straight from their GitHub repositories. The last file required to dockerize the project is a simple Python script:
from simple_flask.rest import rest as rest_engine
rest_engine.run_engine('0.0.0.0')
This file imports and run the main REST engine of the application.
The host of this application has to be set to 0.0.0.0
, as shown at line 4,
in order to allow users to access the service inside Docker.
Time to run the build (and don’t forget the .
at the end!):
docker build -t simple_flask .
Once a new Docker image has been created it is possible to run it through the following instruction:
docker run -it -p 5000:5000 simple_flask /deployment/env/bin/python /deployment/start.py
This instruction runs the simple_flask
image (created in the previous step),
links the internal port 5000 with the host port 5000 (-p 5000:5000
), and
executes, through Virtualenv, the starting script
(/deployment/env/bin/python /deployment/start.py
). It is possible to test
the service through the URL’s specified in the Flask REST Service section.