Skip to content

Commit

Permalink
Allow init containers if modelUri provided (#5059)
Browse files Browse the repository at this point in the history
  • Loading branch information
ukclivecox authored Jul 31, 2023
1 parent 8713b15 commit c5f150d
Show file tree
Hide file tree
Showing 4 changed files with 374 additions and 27 deletions.
152 changes: 145 additions & 7 deletions notebooks/protocol_examples.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
{
"data": {
"text/plain": [
"'1.16.0-dev'"
"'1.17.0-dev'"
]
},
"execution_count": 5,
Expand Down Expand Up @@ -186,14 +186,16 @@
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"execution_count": 10,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'data': {'names': ['proba'], 'ndarray': [[0.43782349911420193]]}, 'meta': {'requestPath': {'classifier': 'seldonio/mock_classifier:1.16.0-dev'}}}\n"
"{'data': {'names': ['proba'], 'ndarray': [[0.43782349911420193]]}, 'meta': {'requestPath': {'classifier': 'seldonio/mock_classifier:1.17.0-dev'}}}\n"
]
}
],
Expand All @@ -208,14 +210,14 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'meta': {'requestPath': {'classifier': 'seldonio/mock_classifier:1.16.0-dev'}}, 'data': {'names': ['proba'], 'ndarray': [[0.43782349911420193]]}}\n"
"{'meta': {'requestPath': {'classifier': 'seldonio/mock_classifier:1.17.0-dev'}}, 'data': {'names': ['proba'], 'ndarray': [[0.43782349911420193]]}}\n"
]
}
],
Expand All @@ -231,7 +233,143 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"seldondeployment.machinelearning.seldon.io \"example-seldon\" deleted\r\n"
]
}
],
"source": [
"!kubectl delete -f resources/model_seldon.yaml"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Seldon protocol Model with ModelUri with two custom models"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [],
"source": [
"%%writetemplate resources/model_seldon.yaml\n",
"apiVersion: machinelearning.seldon.io/v1\n",
"kind: SeldonDeployment\n",
"metadata:\n",
" name: example-seldon\n",
"spec:\n",
" protocol: seldon\n",
" predictors:\n",
" - componentSpecs:\n",
" - spec:\n",
" containers:\n",
" - image: seldonio/mock_classifier:{VERSION}\n",
" name: classifier\n",
" - image: seldonio/mock_classifier:{VERSION}\n",
" name: classifier2\n",
" graph:\n",
" name: classifier\n",
" type: MODEL\n",
" modelUri: gs://seldon-models/v1.17.0-dev/sklearn/iris\n",
" children:\n",
" - name: classifier2\n",
" type: MODEL\n",
" modelUri: gs://seldon-models/v1.17.0-dev/sklearn/iris\n",
" name: model\n",
" replicas: 1"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"seldondeployment.machinelearning.seldon.io/example-seldon unchanged\r\n"
]
}
],
"source": [
"!kubectl apply -f resources/model_seldon.yaml"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"seldondeployment.machinelearning.seldon.io/example-seldon condition met\r\n"
]
}
],
"source": [
"!kubectl wait --for condition=ready --timeout=300s sdep --all -n seldon"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'data': {'names': ['proba'], 'ndarray': [[0.07735472603574542]]}, 'meta': {'requestPath': {'classifier': 'seldonio/mock_classifier:1.17.0-dev', 'classifier2': 'seldonio/mock_classifier:1.17.0-dev'}}}\n"
]
}
],
"source": [
"X=!curl -s -d '{\"data\": {\"ndarray\":[[1.0, 2.0, 5.0]]}}' \\\n",
" -X POST http://localhost:8003/seldon/seldon/example-seldon/api/v1.0/predictions \\\n",
" -H \"Content-Type: application/json\"\n",
"d=json.loads(X[0])\n",
"print(d)"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'meta': {'requestPath': {'classifier': 'seldonio/mock_classifier:1.17.0-dev', 'classifier2': 'seldonio/mock_classifier:1.17.0-dev'}}, 'data': {'names': ['proba'], 'ndarray': [[0.07735472603574542]]}}\n"
]
}
],
"source": [
"X=!cd ../executor/proto && grpcurl -d '{\"data\":{\"ndarray\":[[1.0,2.0,5.0]]}}' \\\n",
" -rpc-header seldon:example-seldon -rpc-header namespace:seldon \\\n",
" -plaintext \\\n",
" -proto ./prediction.proto 0.0.0.0:8003 seldon.protos.Seldon/Predict\n",
"d=json.loads(\"\".join(X))\n",
"print(d)"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
Expand Down
2 changes: 1 addition & 1 deletion operator/controllers/seldondeployment_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ func (r *SeldonDeploymentReconciler) createComponents(ctx context.Context, mlDep
}

pi := NewPrePackedInitializer(ctx, r.ClientSet)
err = pi.createStandaloneModelServers(mlDep, &p, &c, &p.Graph, securityContext)
err = pi.addModelServersAndInitContainers(mlDep, &p, &c, &p.Graph, securityContext, log)
if err != nil {
return nil, err
}
Expand Down
69 changes: 50 additions & 19 deletions operator/controllers/seldondeployment_prepackaged_servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/go-logr/logr"
"strconv"
"strings"

Expand Down Expand Up @@ -401,25 +402,33 @@ func SetUriParamsForTFServingProxyContainer(pu *machinelearningv1.PredictiveUnit
}
}

func (pi *PrePackedInitialiser) createStandaloneModelServers(mlDep *machinelearningv1.SeldonDeployment, p *machinelearningv1.PredictorSpec, c *components, pu *machinelearningv1.PredictiveUnit, podSecurityContext *v1.PodSecurityContext) error {
func (pi *PrePackedInitialiser) findDeployment(c *components, depName string) (*appsv1.Deployment, bool, error) {
for i := 0; i < len(c.deployments); i++ {
d := c.deployments[i]
if strings.Compare(d.Name, depName) == 0 {
return d, true, nil
}
}
return nil, false, nil
}

func (pi *PrePackedInitialiser) addModelServersAndInitContainers(mlDep *machinelearningv1.SeldonDeployment,
p *machinelearningv1.PredictorSpec,
c *components,
pu *machinelearningv1.PredictiveUnit,
podSecurityContext *v1.PodSecurityContext,
log logr.Logger) error {

sPodSpec, idx := utils.GetSeldonPodSpecForPredictiveUnit(p, pu.Name)
if sPodSpec == nil {
return fmt.Errorf("Failed to find PodSpec for Prepackaged server PreditiveUnit named %s", pu.Name)
}
depName := machinelearningv1.GetDeploymentName(mlDep, *p, sPodSpec, idx)
if machinelearningv1.IsPrepack(pu) {
sPodSpec, idx := utils.GetSeldonPodSpecForPredictiveUnit(p, pu.Name)
if sPodSpec == nil {
return fmt.Errorf("Failed to find PodSpec for Prepackaged server PreditiveUnit named %s", pu.Name)
}
depName := machinelearningv1.GetDeploymentName(mlDep, *p, sPodSpec, idx)
seldonId := machinelearningv1.GetSeldonDeploymentName(mlDep)

var deploy *appsv1.Deployment
existing := false
for i := 0; i < len(c.deployments); i++ {
d := c.deployments[i]
if strings.Compare(d.Name, depName) == 0 {
deploy = d
existing = true
break
}

deploy, existing, err := pi.findDeployment(c, depName)
if err != nil {
return err
}

// might not be a Deployment yet - if so we have to create one
Expand Down Expand Up @@ -462,7 +471,7 @@ func (pi *PrePackedInitialiser) createStandaloneModelServers(mlDep *machinelearn
}

if !existing {

seldonId := machinelearningv1.GetSeldonDeploymentName(mlDep)
// this is a new deployment so its containers won't have a containerService
for k := 0; k < len(deploy.Spec.Template.Spec.Containers); k++ {
con := &deploy.Spec.Template.Spec.Containers[k]
Expand All @@ -478,10 +487,32 @@ func (pi *PrePackedInitialiser) createStandaloneModelServers(mlDep *machinelearn
c.deployments = append(c.deployments, deploy)
}
}
} else {
// add model uri initializer for non server components
if pu.ModelURI != "" {
log.Info("Add rclone init container for predictive unit", "predictive unit", pu.Name)
deploy, existing, err := pi.findDeployment(c, depName)
if err != nil {
return err
}
if !existing {
return fmt.Errorf("Expected to find a deployment for predictive unit %s", pu.Name)
}
mi := NewModelInitializer(pi.ctx, pi.clientset)
c := utils.GetContainerForDeployment(deploy, pu.Name)
if c == nil {
return fmt.Errorf("Expected to find container for predictive unit %s", pu.Name)
}
envSecretRefName := extractEnvSecretRefName(pu)
_, err = mi.InjectModelInitializer(deploy, c.Name, pu.ModelURI, pu.ServiceAccountName, envSecretRefName, pu.StorageInitializerImage)
if err != nil {
return err
}
}
}

for i := 0; i < len(pu.Children); i++ {
if err := pi.createStandaloneModelServers(mlDep, p, c, &pu.Children[i], podSecurityContext); err != nil {
if err := pi.addModelServersAndInitContainers(mlDep, p, c, &pu.Children[i], podSecurityContext, log); err != nil {
return err
}
}
Expand Down
Loading

0 comments on commit c5f150d

Please sign in to comment.