Containerizing an ML Model - Part 2

Prerequisites

  • Conda (miniconda or anaconda)
  • Docker

Description

This is the second of a two part post that shows how to containerize an ML model. In the first part Containerizing an ML Model - Part 1, we created the application and tested it. In this part we will take that application and package it up into a Docker container.

A note on flask

When you run flask out of the box it warns you that the bundled web server that it comes with is not meant for production. Therefore we need to add a production ready web server to expose our application. I chose Gunicorn when developing this, but you can use any WSGI application server (others are Waitress and uWSGI). Gunicorn only works on Linux/Unix type operating systems so you can’t use it on Windows. On Windows you can use another WSGI server like Waitress.

Adding Gunicorn

NOTE: This part was done in a Linux environment (Ubuntu). If you don’t have a Linux environment to work with skip this part and use a WSGI server that can run on Windows.

From your spacy environment run conda install gunicorn to install gunicorn.

We then need to add a few files in the same folder where you have saved the flask app ner.py,

  • a blank file with the filename __init__.py
  • a file called wsgi.py with the following content
from ner import app

if __name__ == "__main__":
    app.run(debug=False)

From that same folder test your app by running the following command

gunicorn -w 1 -b :5000 -t 360 wsgi:app

Then you can send a POST request to http://0.0.0.0:5000/entities using the same method as in Part 1.

Export your environment

We need to recreate our conda environment inside the docker container. So the first step is to export the environment we used to develop the api. You can do this using the following command

This will save the environment as a YAML file containing a list of all the packages and their versions. This will be used to recreate the environment when building the container.

Building the container

Create the dockerfile

Next create a text filw called dockerfile with the following contents and place it outside the api folder.

#sets the base image for subsequent instructions
FROM continuumio/miniconda3

#copy files into the container
COPY ./api /api

#set the current working directory
WORKDIR /api

#dowloads the package lists form the repositories and 'updates' them to get incormation on the newsest versions of packages and their dependancies
RUN apt-get update 

# update conda
RUN conda update -n base -c defaults conda

# create the environment
RUN conda env create -f environment.yml

# activate spacy environment
RUN echo "source activate spacy" > ~/.bashrc
ENV PATH /opt/conda/envs/spacy/bin:$PATH

# download model
RUN python -m spacy download en_core_web_sm

# run the gunicorn command to start the api in docker
CMD ["gunicorn", "-w", "1", "-b", ":5000", "-t", "360", "wsgi:app"]

Your final folder structure should look like this

├── api
│   ├── environment.yml
│   ├── __init__.py
│   ├── ner.py
│   └── wsgi.py
└── dockerfile

Build

You can do this part on any machine regardless of what packages are being installed as long as you have Docker installed and you have it set to Linux containers (not Windows containers).

Remove the following from the environment.yml file, it should be located at the bottom

- pip:
  - en-core-web-sm==2.2.3

The model will be installed from within the container by the following command in the dockerfile above.

# download model
RUN python -m spacy download en_core_web_sm

Without this step you will get the following error

Pip subprocess error:
ERROR: Could not find a version that satisfies the requirement en-core-web-sm==2.2.3 (from -r />api/condaenv.v5c498fx.requirements.txt (line 1)) (from versions: none)
ERROR: No matching distribution found for en-core-web-sm==2.2.3 (from -r /api/condaenv.v5c498fx.>requirements.txt (line 1))
CondaEnvException: Pip failed

To build the container run the following command from the same location as the dockerfile, -t tags the container image with the name ner

Run

Once complete if you run the docker images command you should see your container image in the local registry.

Run the following command to run your container. It will run the application and expose port 5000 (the port the application is listening on) to port 5001 on your machine.

Test

Test it the same way as above by sending a POST request to http://0.0.0.0:5001/entities using the same method as in Part 1.

Sample Code

https://github.com/erotavlas/Containerizing-an-ML-Model

References

Gunicorn and flask