Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dockerize the entier wrapping process of building sklearn_iris example #51

Merged
merged 1 commit into from
Jan 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/models/sklearn_iris_docker/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.dockerignore
Dockerfile
Makefile
sklearn_iris_deployment.json
84 changes: 84 additions & 0 deletions examples/models/sklearn_iris_docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
## Use alpine as build time and runtime image
FROM alpine:3.7 as build-alpine

## Install build dependencies
RUN apk add --update \
build-base \
freetype-dev \
gcc \
gfortran \
libc6-compat \
libffi-dev \
libpng-dev \
openblas-dev \
openssl-dev \
py2-pip \
python2 \
python2-dev\
wget \
&& true

## Symlink missing header, so we can compile numpy
RUN ln -s /usr/include/locale.h /usr/include/xlocale.h

## Copy package manager config to staging root tree
RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/
## Install runtime dependencies under staging root tree
RUN apk add --no-cache --initdb --root /out \
alpine-baselayout \
busybox \
ca-certificates \
freetype \
libc6-compat \
libffi \
libpng \
libstdc++ \
musl \
openblas \
openssl \
python2 \
&& true
## Remove package manager residuals
RUN rm -rf /out/etc/apk /out/lib/apk /out/var/cache

## Enter model source tree and install all Python depenendcies
COPY . /src
WORKDIR /src
## TODO this does take a while to build, maybe a good idea to
## put all related build dependencies into a separate public image
RUN pip install --requirement requirements.txt
## Train the model
RUN python train_iris.py

## Copy source code and Python dependencies to the saging root tree
RUN mkdir -p /out/src && cp -r /src/* /out/src/
RUN mkdir -p /out/usr/lib/python2.7/ && cp -r /usr/lib/python2.7/* /out/usr/lib/python2.7/

## Use Seldon Core wrapper image to wrap the model source code
FROM seldonio/core-python-wrapper:0.4 as build-wrapper

ARG MODEL_NAME
ARG IMAGE_VERSION
ARG IMAGE_REPO

## Copy staging diretory here
COPY --from=build-alpine /out /out
## Wrap the Python model
WORKDIR /wrappers/python
RUN python wrap_model.py /out/src $MODEL_NAME $IMAGE_VERSION $IMAGE_REPO --force

## Copy wrapped model source code into staging tree and cleanup what is not neccessary at runtime
RUN mkdir -p /out/microservice && cp -r /out/src/build/* /out/microservice/ && rm -rf /out/src
WORKDIR /out/microservice
RUN rm -f Dockerfile Makefile requirements*.txt build_image.sh push_image.sh
## TODO dockerfile doesn't support build argument interpolation in array notation for ENTRYPOINT & CMD
## to get rid of `/bin/sh` wrapper, it'd help to make $MODEL_NAME an environment variable and let the
## Python script pick it up
RUN printf '#!/bin/sh\nexec python microservice.py %s REST --service-type MODEL --persistence 0' $MODEL_NAME > microservice.sh && chmod +x microservice.sh

## Copy staging root tree onto an empty image
FROM scratch
COPY --from=build-wrapper /out /
WORKDIR /microservice
EXPOSE 5000
ENTRYPOINT ["/microservice/microservice.sh"]
9 changes: 9 additions & 0 deletions examples/models/sklearn_iris_docker/IrisClassifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from sklearn.externals import joblib

class IrisClassifier(object):

def __init__(self):
self.model = joblib.load('IrisClassifier.sav')

def predict(self,X,features_names):
return self.model.predict_proba(X)
11 changes: 11 additions & 0 deletions examples/models/sklearn_iris_docker/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
IMAGE_REPO?=seldonio
IMAGE_NAME?=irisclassifier
IMAGE_VERSION?=0.1
MODEL_NAME?=IrisClassifier

container_image:
docker build \
--build-arg IMAGE_REPO=$(IMAGE_REPO) \
--build-arg IMAGE_VERSION=$(IMAGE_VERSION) \
--build-arg MODEL_NAME=$(MODEL_NAME) \
--tag $(IMAGE_REPO)/$(IMAGE_NAME):$(IMAGE_VERSION) .
9 changes: 9 additions & 0 deletions examples/models/sklearn_iris_docker/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
numpy==1.11.2
pandas==0.18.1
grpc==0.3.post19
grpcio==1.8.4
Flask==0.11.1
futures
redis==2.10.5
scipy==0.18.1
scikit-learn==0.19.0
53 changes: 53 additions & 0 deletions examples/models/sklearn_iris_docker/sklearn_iris_deployment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"apiVersion": "machinelearning.seldon.io/v1alpha1",
"kind": "SeldonDeployment",
"metadata": {
"labels": {
"app": "seldon"
},
"name": "seldon-deployment-example"
},
"spec": {
"annotations": {
"project_name": "Iris classification",
"deployment_version": "0.1"
},
"name": "sklearn-iris-deployment",
"oauth_key": "oauth-key",
"oauth_secret": "oauth-secret",
"predictors": [
{
"componentSpec": {
"spec": {
"containers": [
{
"image": "seldonio/irisclassifier:0.1",
"imagePullPolicy": "IfNotPresent",
"name": "sklearn-iris-classifier",
"resources": {
"requests": {
"memory": "1Mi"
}
}
}
],
"terminationGracePeriodSeconds": 20
}
},
"graph": {
"children": [],
"name": "sklearn-iris-classifier",
"endpoint": {
"type" : "REST"
},
"type": "MODEL"
},
"name": "sklearn-iris-predictor",
"replicas": 1,
"annotations": {
"predictor_version" : "0.1"
}
}
]
}
}
25 changes: 25 additions & 0 deletions examples/models/sklearn_iris_docker/train_iris.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import numpy as np
import os
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.externals import joblib
from sklearn import datasets

def main():
clf = LogisticRegression()
p = Pipeline([('clf', clf)])
print 'Training model...'
p.fit(X, y)
print 'Model trained!'

filename_p = 'IrisClassifier.sav'
print 'Saving model in %s' % filename_p
joblib.dump(p, filename_p)
print 'Model saved!'

if __name__ == "__main__":
print 'Loading iris data set...'
iris = datasets.load_iris()
X, y = iris.data, iris.target
print 'Dataset loaded!'
main()