In this article, I will try to show viable ways to develop python applications (mainly web applications) with Docker. While I personally focus on the Flask microframework for Python, the purpose of this article is to demonstrate how applications, (developed by any language and framework), can be better developed and shared through Docker, which greatly reduces the gap between development environments and the official product by encapsulating dependencies.
Most Python developers use thevirtualenvIt provides an easy-to-use mechanism for applications to use their own specialized dependencies that may conflict with other applications or operating systems (especially different Pyhton versions, different library versions, etc.). It provides an easy-to-use mechanism for applications to use their own specialized dependencies that may conflict with other applications or operating systems (especially different Pyhton versions, different library versions, etc.). Personally, I've never been too interested in virtualenv for the following reasons:
- I often forget to enable it, or forget to switch it when switching projects, which can be confusing with vague error messages.
- It does not provide "pure" isolation, only Python level isolation (system libraries and non-python dependencies can still cause problems).
- I usually don't want to run it in the official product, which means inconsistencies in the development environment and the official product.
- It feels a bit "hacky": it relies on modifying scripts and setting new paths.
( ViewThis article from pythonrants Learn more about why you might not want to use virtualenv )
So what can be done to make Docker better, Docker essentially provides very lightweight VMs (which can be referred to as "containers" in the parlance) that we can use to create a development and product environment with high standards of isolation and greatly reduced mismatch. (If you're not familiar with Docker and would like to learn more, you can check out my talk at the Edinburgh Tech Symposium, "Docker: A new way to use Docker".Introducing the Docker talk)。
Myself and Mark Coleman use this approach when we build a small visual Web APP (Documentation is here). This (inside) sets aside a basic image to install Python 2.7, and some Flask administration as well as PostgreSQL. I'll be developing a hello world web application based on this image. I'm assuming you're developing on Linux, and that you already have git, and Docker installed, and that the macOS instructions should be very similar. Start by cloning and building the base image:
$ git clone /mrmrcoleman/python_webapp $ docker build -t python_webapp .
Now, we need to add some code to the container and write it in detail. We're going to create a new project that just points to the Docker image to do this, rather than modifying the previous project directly.
Create a new project with the following structure:
├── Dockerfile
├── example_app
│ ├── app
│ │ ├── __init__.py
│ │ └──
│ └── __init__.py
├── example_app.wsgi
Or clone the example project at this address: /amouat/example_app.git
Write in example_app/app/_init_.py:
from flask import Flask app = Flask(__name__) from app import views
Make the other _init_.py empty. Write in:
from app import app @('/') @('/index') def index(): return "Hello, World!"
Above is the minimal flask version of one of our hello world apps. I've created a new version of the flask in theThis tutorialhas used similar code in it, so if you're new to Flask or Python, you can follow the tutorials mentioned above and continue learning using Docker instead of virtualenv.
In order to make it run inside the Docker container, we need to do a few more operations. In our example Apache server, the example_app.wsgi file contains instructions for connecting the Python code to the web server. The file should contain the following:
import site ('/opt/example_app/') from app import app as application
Ultimately, we need a Dockerfile to build the container and run it. In our example, it looks like this:
FROM python_webapp MAINTAINER amouat ADD example_app.wsgi /var/www/flaskapp/ CMD service apache2 start && tail -F /var/log/apache2/
The ADD line injects some code to start WSGI. the CMD line gets any possible error messages when starting the container, starting the apache web server, and sends them to stdout.
If you the following operations:
$ docker build -t example_app . $ docker run -p 5000:5000 -v $(pwd)/example_app:/opt/example_app/ -i -t example_app
You should get this back: open the address localhost:5000 through your browser and you will see your site running. If you're actually running in a VM or vagrant, remember to open port 5000.
Now we're running the web server, which is pretty close to what we use in the product (I intentionally used Apache for this instead of Python's default web server). We inject code into the container by mapping from the host to the container; it's also possible to add code from the Dockerfile command line is with ADD, but then when we team code to make changes, we need to rebuild the container each time.
However, this is still not very good; in development we really want to use a Python web server that helps us debugging to a great extent. Happily we don't have to make any changes to the Dockerfile. Start by creating a file in the example_app file and follow this:
!flask/bin/python from app import app (debug = True, host='0.0.0.0')
This will start Python's web server with debugging and listen for all connections, which we can also access from outside the container. Now restart the container with the following command:
$ docker run -p 5000:5000 -v $(pwd)/example_app:/opt/example_app/ -i -t example_app python /opt/example_app/
You can see that the page is running again. This time we explicitly provide the command to run ("python /opt/example_app/"), which overrides the setting of the CMD line in the Dockerfile. Now if you edit the source program on the host, you can immediately see the changes on the web page.
Let's take a moment to look at what we've got:
- A web application running in an isolated container that completely encapsulates the application's Python dependencies and system dependencies.
- Ability to develop code using an existing editor or IDE and view changes directly as if you were editing locally.
- Closer to a formal product runtime environment than ever before.
- Virtualenv is not used.
If you want to know how to establish a path to program distribution in this way, take a look at theMark Coleman writes about the previously mentioned visualization web apps。
Unfortunately, it's not perfect. There are still several problems:
- You may still encounter situations where you need to use virtualenv or its equivalent solution, such as a conflict between the operating system version of a library and the version required for your program.
- We haven't fully resolved the data hosting issue and still need to do certain tests.
- I'm assuming the "product" is a Docker container, but that's often not the case and Docker hosting itself is just getting started.
Nonetheless, I still think this is a big step towards a better future for software development, greatly easing the pain of deploying software and managing dependencies.