Introduction

Using Docker, we can distribute our applications much more quickly and easily on local Linux, Windows or any cloud server without complicated setup. Deployment with Docker can save us a ton of time, particularly for Python Machine Learning applications that need to preinstall numerous libraries or tools.

In this post, I will write a simple Flask web service application and build this application to a Docker image. You will learn how to write the dockerfile, build the Docker image, save and load the Docker image.

1. Prepare Sample Python Code

The sample Python code provided below shows how to launch a Flask web service. You can launch the application by executing the command python3 app.py or python3 app.py --name your-name, and view the webpage on http://127.0.0.1:5000/ and http://127.0.0.1:5000/hello.

# app.py

import sys

from gevent.pywsgi import WSGIServer
from flask import Flask, jsonify, request
from flask_cors import CORS

app = Flask(__name__)

@app.route('/')
def index():
    return "Hi from " + name + " in index page"

@app.route('/hello')
def hello():
    return jsonify({'message': 'Hi from ' + name + ' in hello page'})

if __name__ == '__main__':
    name = 'Jacob'
    if (len(sys.argv) > 2 and sys.argv[1] == "--name"):
        name = sys.argv[2]

    CORS(app, resources=r'/*')
    http_server = WSGIServer(('0.0.0.0', 5000), app)
    http_server.serve_forever() 

2. Define Dependencies Requirements File

List down all the libraries in your Python project and write them in a text file with name requirements.txt. The requirements file for the above sample application shoud be:

# requirements.txt
flask==20.0.2
flask_cors==3.0.8
gevent==1.5.0

3. Write Dockerfile

Before writing Dockerfile, make sure you have installed Docker Engine on your machine. Dockerfile is an instruction manual that guide Docker Engine how to build the required environment for the application to run on. The file name is Dockerfile.

The directory structure should be like this:

├── project/
│   ├── app.py
│   ├── requirements.txt
│   └── Dockerfile

Here is the Dockerfile for the sample application:

# Dockerfile
FROM python:3.8-slim-bullseye
COPY . /code/
# it equals to
# COPY app.py /code/
# COPY requirements.txt /code/
WORKDIR /code
RUN /usr/local/bin/pip3 install -r /code/requirements.txt && rm -r /root/.cache/pip
EXPOSE 5000
CMD ["python3", "app.py"]

Let me explain some points:

  • FROM must be the first instruction of Dockefile, it lets you define what base image you want to use. python:3.8-slim-bullseye has smaller image size than python:3.8-bullseye.
  • COPY . /code/ means that copy ALL files in the directory /project/ of your machine to the directory /code/ of the Docker image.
  • WORKDIR sets the working directory for the application in the Docker image.
  • RUN is used to run Linux command, just like we type command in command line of Linux OS. If you have many commands to run, try to use RUN command_1 && command_2 && command_3 && ... instead of using RUN separately. Because each separate RUN instruction will build one layer, RUN command_1 && command_2 && command_3 && ... requires one layer only, which save building time and reduce image size.
  • You should write an instruction to clean the cache or downloaded file/package after downloading or installing (apt install, pip install…).
  • Add \ at end-of-line to change to a newline, add # at start-of-line to add comments in Dockerfile.

4. Build the Docker Image

We can start to build the Docker image using the following command:

$ cd project
$ docker build -t myApp:v1.0 .
  • -t myApp:v1.0 is used to tag the image with a name and version number.
  • . in the end indicates the current directory, which is the path of the Dockerfile.

After building, you can check the new Docker image with command docker images.

5. Run the Application with Docker

Run the application container with default name:

$ docker run -p 5000:5000 -it myApp:v1.0

Or run the application container with custom name argument:

$ docker run -p 5000:5000 -it myApp:v1.0 python3 app.py --name your-custom-name

Now you can view the webpage on http://127.0.0.1:5000/ and http://127.0.0.1:5000/hello.

Save and Load Local Image

Sometimes project requires us to use the Docker image in the machine/server without internet, we can save the Docker image to a tar file and copy it to another machine to load.

Save the image (not container) in the development machine:

# operaton in development machine
# myApp:v1.0 is the image tag 
$ docker save -o myApp.tar myApp:v1.0

You will see myApp.tar in the current directory of development machine, then copy it to production machine to load.

# operaton in production machine
$ docker load -i myApp.tar

Now you can check the loaded Docker image with command docker images.

You can also use docker export and docker import command to export and import container (not image).

Notes: The image file saved using docker save is a little bigger than the file exported using docker export. docker import can rename the new image, docker load can NOT rename the new image. docker save can pack multiple images to one file, docker export can not do it. The image using docker export loses history and meta data, but the image using docker save keeps all the history. docker export is usually used to create base image.

You can now give it a try!