# Packaging a Python model for Seldon Core using s2i In this guide, we illustrate the steps needed to wrap your own python model in a docker image ready for deployment with Seldon Core using [S2I](https://github.com/openshift/source-to-image). [Source-to-Image](https://github.com/openshift/source-to-image) (S2I) is a toolkit and workflow for building reproducible container images from source code. S2I produces ready-to-run images by injecting source code into a container image and letting the container prepare that source code for execution. ## Step 1 - Install s2i [Download and install s2i](https://github.com/openshift/source-to-image#installation) * Prerequisites for using s2i are: * Docker * Git (if building from a remote git repo) To check everything is working you can run ```bash s2i usage seldonio/seldon-core-s2i-python3:1.19.0-dev ``` ## Step 2 - Create your source code To use our s2i builder image to package your python model you will need: * A python file with a class that runs your model * Your model's dependencies and environment, which can be described using either of: - `requirements.txt` - `setup.py` - `environment.yml` * `.s2i/environment` - model definitions used by the s2i builder to correctly wrap your model We will go into detail for each of these steps: ### Python file Your source code should contain a python file which defines a class of the same name as the file. For further details see [details on creating your python class](python_component.md) ### Dependencies You can describe your model's dependencies using either of: `requirements.txt`, `setup.py` or `environment.yml`. #### requirements.txt Populate a `requirements.txt` with any software dependencies your code requires. These will be installed via pip when creating the image. #### setup.py Similar to a `requirements.txt` file, you can also describe your model's dependencies using a `setup.py` file: ```python from setuptools import setup setup( name="my-model", # ... install_requires=[ "scikit-learn", ] ) ``` #### environment.yml Describe your Conda environment using an `environment.yml` file: ```yaml name: my-conda-environment channels: - defaults dependencies: - python=3.6 - scikit-learn=0.19.1 ``` During image creation, `s2i` will create your Conda environment, fetching all the required dependencies. At run time, the created Conda environment will get activated at startup. ### .s2i/environment Define the core parameters needed by our python builder image to wrap your model. An example is: ```bash MODEL_NAME=MyModel SERVICE_TYPE=MODEL ``` These values can also be provided or overridden on the command line when building the image. See below for the possible keys and values for this file. ## Step 3 - Build your image Use `s2i build` to create your Docker image from source code. You will need Docker installed on the machine and optionally git if your source code is in a public git repo. You can choose from three python builder images Using s2i you can build directly from a git repo or from a local source folder. See the [s2i docs](https://github.com/openshift/source-to-image/blob/master/docs/cli.md#s2i-build) for further details. The general format is: ```bash s2i build seldonio/seldon-core-s2i-python3:1.19.0-dev ``` An example invocation using the test template model inside seldon-core: ```bash s2i build https://github.com/seldonio/seldon-core.git --context-dir=wrappers/s2i/python/test/model-template-app seldonio/seldon-core-s2i-python3:1.19.0-dev seldon-core-template-model ``` The above s2i build invocation: * uses the GitHub repo: https://github.com/seldonio/seldon-core.git and the directory `wrappers/s2i/python/test/model-template-app` inside that repo. * uses the builder image `seldonio/seldon-core-s2i-python3` * creates a docker image `seldon-core-template-model` For building from a local source folder, an example where we clone the seldon-core repo: ```bash git clone https://github.com/seldonio/seldon-core.git cd seldon-core s2i build wrappers/s2i/python/test/model-template-app seldonio/seldon-core-s2i-python3:1.19.0-dev seldon-core-template-model ``` For more help see: ```bash s2i usage seldonio/seldon-core-s2i-python3:1.19.0-dev s2i build --help ``` ## Using with Keras/Tensorflow Models To ensure Keras models with the Tensorflow backend work correctly you may need to call `_make_predict_function()` on your model after it is loaded. This is because Flask may call the prediction request in a separate thread from the one that initialised your model. See the [keras issue](https://github.com/keras-team/keras/issues/6462) for further discussion. ## Environment Variables The required environment variables understood by the builder image are explained below. You can provide them in the `.s2i/environment` file or on the `s2i build` command line. ### MODEL_NAME The name of the class containing the model. Also the name of the python file which will be imported. ### SERVICE_TYPE The service type being created. Available options are: * MODEL * ROUTER * TRANSFORMER * COMBINER * OUTLIER_DETECTOR ### EXTRA_INDEX_URL .. Warning:: ``EXTRA_INDEX_URL`` is recommended to be passed as argument to ``s2i`` command rather than adding in ``.s2i/environment`` as a practice of avoiding checking in credentials in the code. For installing packages from private/self-hosted PyPi registry. ### PIP_TRUSTED_HOST For adding private/self-hosted unsecured PyPi registry by adding it to pip trusted-host. ```bash s2i build \ -e EXTRA_INDEX_URL=https://:@mypypi.example.com/simple \ -e PIP_TRUSTED_HOST=mypypi.example.com \ \ seldonio/seldon-core-s2i-python3:1.19.0-dev \ ``` ### PAYLOAD_PASSTHROUGH If enabled, the Python server won't try to decode the request payload nor encode the response back. That means that the `predict()` method of your `SeldonComponent` model will receive the payload as-is and it will be responsible to decode it. Likewise, the return value of `predict()` must be a serialised response. By default, this option will be disabled. ## Creating different service types ### MODEL * [A minimal skeleton for model source code](https://github.com/SeldonIO/seldon-core/tree/master/wrappers/s2i/python/test/model-template-app) * [Example using SKLearn Spacy NLP](../examples/sklearn_spacy_text_classifier_example) * [Example with SKLearn Iris Classifier](../examples/iris) ### ROUTER * [Description of routers in Seldon Core](../analytics/routers.html) * [A minimal skeleton for router source code](https://github.com/SeldonIO/seldon-core/tree/master/wrappers/s2i/python/test/router-template-app) ### TRANSFORMER * [A minimal skeleton for transformer source code](https://github.com/SeldonIO/seldon-core/tree/master/wrappers/s2i/python/test/transformer-template-app) * [Example transformers](https://github.com/SeldonIO/seldon-core/tree/master/examples/transformers) ## Advanced Usage ### Model Class Arguments You can add arguments to your component which will be populated from the `parameters` defined in the SeldonDeloyment when you deploy your image on Kubernetes. For example, our [Python TFServing proxy](https://github.com/SeldonIO/seldon-core/tree/master/servers/tfserving_proxy) has the class init method signature defined as below: ```python class TfServingProxy(object): def __init__(self,rest_endpoint=None,grpc_endpoint=None,model_name=None,signature_name=None,model_input=None,model_output=None): ``` These arguments can be set when deploying in a Seldon Deployment. An example can be found in the [MNIST TFServing example](https://github.com/SeldonIO/seldon-core/blob/master/examples/models/tfserving-mnist/tfserving-mnist.ipynb) where the arguments are defined in the [SeldonDeployment](https://github.com/SeldonIO/seldon-core/blob/master/examples/models/tfserving-mnist/mnist_tfserving_deployment.json.template) which is partly show below: ```json { "graph": { "name": "tfserving-proxy", "endpoint": { "type": "REST" }, "type": "MODEL", "children": [], "parameters": [ { "name": "grpc_endpoint", "type": "STRING", "value": "localhost:8000" }, { "name": "model_name", "type": "STRING", "value": "mnist-model" }, { "name": "model_output", "type": "STRING", "value": "scores" }, { "name": "model_input", "type": "STRING", "value": "images" }, { "name": "signature_name", "type": "STRING", "value": "predict_images" } ] } } ``` The allowable `type` values for the parameters are defined in the [proto buffer definition](https://github.com/SeldonIO/seldon-core/blob/44f7048efd0f6be80a857875058d23efc4221205/proto/seldon_deployment.proto#L117-L131). ### Local Python Dependencies `from version 0.5` To use a private repository for installing Python dependencies use the following build command: ```bash s2i build -i :/whl seldonio/seldon-core-s2i-python3:1.19.0-dev ``` This command will look for local Python wheels in the `` and use these before searching PyPI. ### Custom Metrics `from version 0.3` To add custom metrics to your response you can define an optional method `metrics` in your class that returns a list of metric dicts. An example is shown below: ```python class MyModel(object): def predict(self, X, features_names): return X def metrics(self): return [{"type": "COUNTER", "key": "mycounter", "value": 1}] ``` For more details on custom metrics and the format of the metric dict see [here](../analytics/analytics.html#custom-metrics). There is an [example notebook illustrating a model with custom metrics in python](../examples/custom_metrics.html). ### Custom Request Tags `from version 0.3` To add custom request tags data you can add an optional method `tags` which can return a dict of custom meta tags as shown in the example below: ```python class MyModel(object): def predict(self, X, features_names): return X def tags(self): return {"mytag": 1} ```