Usa TensorFlow Serving con Kubernetes

Questo tutorial mostra come utilizzare i componenti TensorFlow Serving in esecuzione nei contenitori Docker per servire il modello TensorFlow ResNet e come distribuire il cluster di servizio con Kubernetes.

Per ulteriori informazioni su TensorFlow Serving, consigliamo il tutorial di base di TensorFlow Serving e il tutorial avanzato di TensorFlow Serving .

Per saperne di più sul modello TensorFlow ResNet, ti consigliamo di leggere ResNet in TensorFlow .

  • La parte 1 fornisce la configurazione dell'ambiente
  • La parte 2 mostra come eseguire l'immagine di servizio Docker locale
  • La parte 3 mostra come eseguire la distribuzione in Kubernetes.

Parte 1: installazione

Prima di iniziare, installa Docker .

Scarica il modello salvato ResNet

Cancellamo la directory dei nostri modelli locali nel caso ne avessimo già una:

rm -rf /tmp/resnet

Le reti residue profonde, o ResNet in breve, hanno fornito l'idea rivoluzionaria delle mappature di identità per consentire l'addestramento di reti neurali convoluzionali molto profonde. Per il nostro esempio, scaricheremo un TensorFlow SavedModel di ResNet per il set di dati ImageNet.

# Download Resnet model from TF Hub
wget https://tfhub.dev/tensorflow/resnet_50/classification/1?tf-hub-format=compressed -o resnet.tar.gz

# Extract SavedModel into a versioned subfolder ‘123’
mkdir -p /tmp/resnet/123
tar xvfz resnet.tar.gz -C /tmp/resnet/123/

Possiamo verificare di avere il SavedModel:

$ ls /tmp/resnet/*
saved_model.pb  variables

Parte 2: esecuzione in Docker

Commetti l'immagine per la distribuzione

Ora vogliamo prendere un'immagine di servizio ed eseguire il commit di tutte le modifiche in una nuova immagine $USER/resnet_serving per la distribuzione Kubernetes.

Per prima cosa eseguiamo un'immagine di servizio come demone:

docker run -d --name serving_base tensorflow/serving

Successivamente, copiamo i dati del modello ResNet nella cartella del modello del contenitore:

docker cp /tmp/resnet serving_base:/models/resnet

Infine impegniamo il contenitore a servire il modello ResNet:

docker commit --change "ENV MODEL_NAME resnet" serving_base \
  $USER/resnet_serving

Ora fermiamo il contenitore base di servizio

docker kill serving_base
docker rm serving_base

Avviare il server

Ora avviamo il contenitore con il modello ResNet in modo che sia pronto per essere servito, esponendo la porta gRPC 8500:

docker run -p 8500:8500 -t $USER/resnet_serving &

Interroga il server

Per il client, dovremo clonare il repository GitHub di TensorFlow Serving:

git clone https://github.com/tensorflow/serving
cd serving

Interroga il server con resnet_client_grpc.py . Il client scarica un'immagine e la invia tramite gRPC per la classificazione in categorie ImageNet .

tools/run_in_docker.sh python tensorflow_serving/example/resnet_client_grpc.py

Ciò dovrebbe comportare un output come:

outputs {
  key: "classes"
  value {
    dtype: DT_INT64
    tensor_shape {
      dim {
        size: 1
      }
    }
    int64_val: 286
  }
}
outputs {
  key: "probabilities"
  value {
    dtype: DT_FLOAT
    tensor_shape {
      dim {
        size: 1
      }
      dim {
        size: 1001
      }
    }
    float_val: 2.41628322328e-06
    float_val: 1.90121829746e-06
    float_val: 2.72477100225e-05
    float_val: 4.42638565801e-07
    float_val: 8.98362372936e-07
    float_val: 6.84421956976e-06
    float_val: 1.66555237229e-05
...
    float_val: 1.59407863976e-06
    float_val: 1.2315689446e-06
    float_val: 1.17812135159e-06
    float_val: 1.46365800902e-05
    float_val: 5.81210713335e-07
    float_val: 6.59980651108e-05
    float_val: 0.00129527016543
  }
}
model_spec {
  name: "resnet"
  version {
    value: 123
  }
  signature_name: "serving_default"
}

Funziona! Il server classifica con successo l'immagine di un gatto!

Parte 3: distribuzione in Kubernetes

In questa sezione utilizziamo l'immagine del contenitore creata nella Parte 0 per distribuire un cluster di servizio con Kubernetes in Google Cloud Platform .

Accesso al progetto GCloud

Qui presupponiamo che tu abbia creato e effettuato l'accesso a un progetto gcloud denominato tensorflow-serving .

gcloud auth login --project tensorflow-serving

Crea un cluster di contenitori

Per prima cosa creiamo un cluster Google Kubernetes Engine per la distribuzione del servizio.

$ gcloud container clusters create resnet-serving-cluster --num-nodes 5

Che dovrebbe produrre qualcosa come:

Creating cluster resnet-serving-cluster...done.
Created [https://container.googleapis.com/v1/projects/tensorflow-serving/zones/us-central1-f/clusters/resnet-serving-cluster].
kubeconfig entry generated for resnet-serving-cluster.
NAME                       ZONE           MASTER_VERSION  MASTER_IP        MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
resnet-serving-cluster  us-central1-f  1.1.8           104.197.163.119  n1-standard-1  1.1.8         5          RUNNING

Imposta il cluster predefinito per il comando del contenitore gcloud e passa le credenziali del cluster a kubectl .

gcloud config set container/cluster resnet-serving-cluster
gcloud container clusters get-credentials resnet-serving-cluster

che dovrebbe risultare in:

Fetching cluster endpoint and auth data.
kubeconfig entry generated for resnet-serving-cluster.

Carica l'immagine Docker

Ora eseguiamo il push della nostra immagine su Google Container Registry in modo da poterla eseguire su Google Cloud Platform.

Per prima cosa tagghiamo l'immagine $USER/resnet_serving utilizzando il formato Container Registry e il nome del nostro progetto,

docker tag $USER/resnet_serving gcr.io/tensorflow-serving/resnet

Successivamente, configuriamo Docker per utilizzare gcloud come assistente per le credenziali:

gcloud auth configure-docker

Successivamente inseriamo l'immagine nel registro,

docker push gcr.io/tensorflow-serving/resnet

Crea distribuzione e servizio Kubernetes

La distribuzione è composta da 3 repliche del server resnet_inference controllato da una distribuzione Kubernetes . Le repliche vengono esposte esternamente da un servizio Kubernetes insieme a un sistema di bilanciamento del carico esterno .

Li creiamo utilizzando l'esempio di configurazione Kubernetes resnet_k8s.yaml .

kubectl create -f tensorflow_serving/example/resnet_k8s.yaml

Con uscita:

deployment "resnet-deployment" created
service "resnet-service" created

Per visualizzare lo stato della distribuzione e dei pod:

$ kubectl get deployments
NAME                    DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
resnet-deployment    3         3         3            3           5s
$ kubectl get pods
NAME                         READY     STATUS    RESTARTS   AGE
resnet-deployment-bbcbc   1/1       Running   0          10s
resnet-deployment-cj6l2   1/1       Running   0          10s
resnet-deployment-t1uep   1/1       Running   0          10s

Per visualizzare lo stato del servizio:

$ kubectl get services
NAME                    CLUSTER-IP       EXTERNAL-IP       PORT(S)     AGE
resnet-service       10.239.240.227   104.155.184.157   8500/TCP    1m

Potrebbe volerci un po' di tempo prima che tutto sia attivo e funzionante.

$ kubectl describe service resnet-service
Name:           resnet-service
Namespace:      default
Labels:         run=resnet-service
Selector:       run=resnet-service
Type:           LoadBalancer
IP:         10.239.240.227
LoadBalancer Ingress:   104.155.184.157
Port:           <unset> 8500/TCP
NodePort:       <unset> 30334/TCP
Endpoints:      <none>
Session Affinity:   None
Events:
  FirstSeen LastSeen    Count   From            SubobjectPath   Type        Reason      Message
  --------- --------    -----   ----            -------------   --------    ------      -------
  1m        1m      1   {service-controller }           Normal      CreatingLoadBalancer    Creating load balancer
  1m        1m      1   {service-controller }           Normal      CreatedLoadBalancer Created load balancer

L'indirizzo IP esterno del servizio è elencato accanto a LoadBalancer Ingress.

Interroga il modello

Ora possiamo interrogare il servizio al suo indirizzo esterno dal nostro host locale.

$ tools/run_in_docker.sh python \
  tensorflow_serving/example/resnet_client_grpc.py \
  --server=104.155.184.157:8500
outputs {
  key: "classes"
  value {
    dtype: DT_INT64
    tensor_shape {
      dim {
        size: 1
      }
    }
    int64_val: 286
  }
}
outputs {
  key: "probabilities"
  value {
    dtype: DT_FLOAT
    tensor_shape {
      dim {
        size: 1
      }
      dim {
        size: 1001
      }
    }
    float_val: 2.41628322328e-06
    float_val: 1.90121829746e-06
    float_val: 2.72477100225e-05
    float_val: 4.42638565801e-07
    float_val: 8.98362372936e-07
    float_val: 6.84421956976e-06
    float_val: 1.66555237229e-05
...
    float_val: 1.59407863976e-06
    float_val: 1.2315689446e-06
    float_val: 1.17812135159e-06
    float_val: 1.46365800902e-05
    float_val: 5.81210713335e-07
    float_val: 6.59980651108e-05
    float_val: 0.00129527016543
  }
}
model_spec {
  name: "resnet"
  version {
    value: 1538687457
  }
  signature_name: "serving_default"
}

Hai distribuito con successo il modello ResNet che funge da servizio in Kubernetes!