• How to Write Dockerfiles for Python Web Apps
  • By Praveen Durairaj
  • Translation from: The Gold Project
  • This article is permalink: github.com/xitu/gold-m…
  • Translator: lsvih
  • Proofreader: Starriers, Steinliber

TL; DR

This article covers examples of building Python applications at multiple levels, from creating simple Dockerfiles to production environments. The following is a summary of the contents of this guide:

  • Use appropriate base images (Debian for development, Alpine for production).
  • Used at development timegunicornHeat loading.
  • Optimize Docker’s cache layer — use commands in the correct order and only run when necessarypip install.
  • useflaskStatic files (such as React, Vue, and Angular generated bundles) are deployed in the Static and template directories of Angular.
  • usealpinePerform a multilevel build in a production environment to reduce the size of the final image file.
  • # Easter Egg — You can use Gunicorn’s during development--reload--reload_extra_filesMonitor changes to files (including HTML, CSS, and JS).

If you need the code for the above steps, please refer to GitHub Repo.

content

  1. Simple Dockerfile with.dockerignore
  2. Use Gunicorn for hot loading
  3. Run a single-file Python script
  4. Deploying static files
  5. Direct build in a production environment
  6. Multilevel build in a production environment

Suppose we have an application called Python-app and prepare a simple directory structure for it. In the top-level directory, include the Dockerfile and SRC folders.

The python app source code is stored in the SRC directory, and the app dependencies are stored in requirements.txt. For the sake of brevity, let’s assume that server.py defines a flask service running on port 8080.

Python -app ├── Dockerfile ├─ SRC ├─ server.py ├─ requirements.txtCopy the code

1. Simple Dockerfile example

FROM python:3.6

# Create app directory
WORKDIR /app

# Install app dependencies
COPY src/requirements.txt ./

RUN pip install -r requirements.txt

# Package app source code
COPY src /app

EXPOSE 8080
CMD [ "python"."server.py" ]
Copy the code

We will use the latest version of Python :3.6 as the base image.

When the image is built, Docker gets all the files that are in the context directory. To speed up the docker build, add.dockerignore to the context directory to exclude unwanted files and directories.

Normally, your.dockerIgnore file should look like this:

.git
__pycache__
*.pyc
*.pyo
*.pyd
.Python
env
Copy the code

Build and run this image:

$ cd python-docker
$ docker build -t python-docker-dev .
$ docker run --rm -it -p 8080:8080 python-docker-dev
Copy the code

You will be able to access the app at [http://localhost:8080](http://localhost:8080.) Use Ctrl+C to exit the program.

Now, assuming you want to run the above code every time you change the code (for example, during local deployment), you need to mount the source code files into the container when you start or stop python services.

$ docker run --rm -it -p 8080:8080 -v $(pwd):/app \
             python-docker-dev bash
root@id:/app# python src/server.py
Copy the code

2. Hot updates using Gunicorn

Gunicorn is a Python WSGI HTTP server running under Unix, using the pre-fork worker model (note, Arbiter is gunicorn’s master, Therefore gunicorn is called the pre-fork worker). You can configure gunicorn using a variety of options. Passing –reload to the gunicorn command, or writing reload to the configuration file, will cause Gunicorn to automatically restart The Python service if a file changes.

FROM python:3.6

# Create app directory
WORKDIR /app

# Install app dependencies
COPY gunicorn_app/requirements.txt ./

RUN pip install -r requirements.txt

# Package app source code
COPY gunicorn_app /app

EXPOSE 8080
Copy the code

We’ll build the image and run Gunicorn to rebuild the code if the files in the app directory are changed.

$ cd python-docker
$ docker build -t python-hot-reload-docker .
$ docker run --rm -it -p 8080:8080 -v $(pwd):/app \
             python-hot-reload-docker bash
root@id:/app# gunicorn --config ./gunicorn_app/conf/gunicorn_config.py gunicorn_app:app
Copy the code

Any changes to the Python file in the app directory trigger rebuild, and the changes are displayed in real time on [http://localhost:8080](http://localhost:8080.). Note that we have already mounted the files into the container so that Gunicorn can work properly.

What about files in other formats? If you want Gunicorn to monitor other types of files (such as templates, views, etc.) while monitoring code changes, you can specify this in the reload_extra_files parameter. This parameter takes multiple filenames in the form of an array.

3. Run a single-file Python script

You can simply run python single-file scripts using Python images via Docker Run.

docker run -it --rm --name single-python-script -v "$PWD":/app -w /app python:3 python your-daemon-or-script.py
Copy the code

You can also pass some arguments to the script. In the example above, we have already mounted the current working directory, which means that files in the directory can be passed as arguments.

4. Deploy static files

The Dockerfile above assumes that you are running an API server in Python. Flask is used if you want to use Python to service your React. Js, vue.js, and Angular.js apps. Flask provides a convenient way to render static files: HTML files in a Templates directory, CSS, js, and images in a static directory.

Look at the directory structure of the simple Hello World static app in this Repo.

FROM python:3.6

# Create app directory
WORKDIR /app

# Install app dependencies
COPY static_app/requirements.txt ./

RUN pip install -r requirements.txt

# Package app source code
COPY static_app /app

EXPOSE 8080
CMD ["python"."server.py"]
Copy the code

In your server.py,

if __name__ == '__main__':
    app.run(host='0.0.0.0')
Copy the code

Note that host needs to be set to 0.0.0.0 – this allows your service to be accessed outside the container. If this parameter is not set, host is set to localhost by default.

5. Direct build in production

FROM python:3.6

# Create app directory
WORKDIR /app

# Install app dependencies
COPY gunicorn_app/requirements.txt ./

RUN pip install -r requirements.txt

# Package app source code
COPY . /app

EXPOSE 8080
CMD ["gunicorn"."--config"."./gunicorn_app/conf/gunicorn_config.py"."gunicorn_app:app"]
Copy the code

Build and run this all-in-one image:

$ cd python-docker
$ docker build -t python-docker-prod .
$ docker run --rm -it -p 8080:8080 python-docker-prod
Copy the code

Due to the underlying Debian, the image will be around 700MB after the build (depending on your source code). Let’s explore how to reduce the size of this file.

6. Multilevel build in a production environment

With multilevel builds, multiple FROM statements are used in the Dockerfile, but only the files that are built in the final stage are used. This way, the resulting image will contain only the required dependencies on the production server, and ideally the files will be very small.

This is useful when you need to use system-dependent modules or modules that need to be compiled. Examples such as PyCrypto and Numpy are suitable for this approach.

# ---- Basic Python image ----
FROM python:3.6 AS base
# Create app directory
WORKDIR /app

# ---- depends on ----
FROM base AS dependencies  
COPY gunicorn_app/requirements.txt ./
# Install app dependencies
RUN pip install -r requirements.txt

# ---- Copy the file and build ----
FROM dependencies AS build  
WORKDIR /app
COPY . /app
# Build or Compile when needed

# -- Publish ---- with Alpine
FROM python:3.6-alpine3.7 AS release  
# Create app directory
WORKDIR /app

COPY --from=dependencies /app/requirements.txt ./
COPY --from=dependencies /root/.cache /root/.cache

# Install app dependencies
RUN pip install -r requirements.txt
COPY --from=build /app/ ./
CMD ["gunicorn"."--config"."./gunicorn_app/conf/gunicorn_config.py"."gunicorn_app:app"]
Copy the code

Using the above method, the image file built with Alpine is about 90MB in size, eight times less than before. Building with the Alpine version can effectively reduce the size of the image.

Note: The above Dockerfiles were written for Python 3, you can change them to the Python 2 version with only a few changes. If you are deploying a Django application, you should also be able to make production-ready Dockerfiles with a few changes.

If you have any suggestions for the previous approach, or would like to see other use cases, please let the author know.

Join the discussion on Reddit or HackerNews 🙂


Also, have you tried deploying a Python Web app on Hasura? This is actually the fastest way to deploy Python applications to HTTPS domains (just use Git push). Try using hasura. IO /hub/project… Template quick start! All project templates in Hasura come with Dockerfile and Kubernetes standard files that you can define freely.


Diggings translation project is a community for translating quality Internet technical articles from diggings English sharing articles. The content covers the fields of Android, iOS, front end, back end, blockchain, products, design, artificial intelligence and so on. For more high-quality translations, please keep paying attention to The Translation Project, official weibo and zhihu column.