Usar TensorFlow para servir con Kubernetes

Este tutorial muestra cómo usar los componentes de TensorFlow Serving que se ejecutan en contenedores Docker para servir el modelo TensorFlow ResNet y cómo implementar el clúster de servicio con Kubernetes.

Para obtener más información sobre TensorFlow Serving, recomendamos el tutorial básico de TensorFlow Serving y el tutorial avanzado de TensorFlow Serving .

Para obtener más información sobre el modelo TensorFlow ResNet, recomendamos leer ResNet en TensorFlow .

  • La parte 1 configura su entorno
  • La parte 2 muestra cómo ejecutar la imagen de servicio local de Docker
  • La parte 3 muestra cómo implementar en Kubernetes.

Parte 1: Configuración

Antes de comenzar, primero instale Docker .

Descargue el modelo guardado de ResNet

Limpiemos nuestro directorio de modelos locales en caso de que ya tengamos uno:

rm -rf /tmp/resnet

Las redes residuales profundas, o ResNets para abreviar, proporcionaron la idea innovadora de los mapeos de identidad para permitir el entrenamiento de redes neuronales convolucionales muy profundas. Para nuestro ejemplo, descargaremos un TensorFlow SavedModel de ResNet para el conjunto de datos 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/

Podemos verificar que tenemos el SavedModel:

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

Parte 2: Ejecutar en Docker

Confirmar imagen para implementación

Ahora queremos tomar una imagen de publicación y confirmar todos los cambios en una nueva imagen $USER/resnet_serving para la implementación de Kubernetes.

Primero ejecutamos una imagen de servicio como demonio:

docker run -d --name serving_base tensorflow/serving

A continuación, copiamos los datos del modelo ResNet a la carpeta del modelo del contenedor:

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

Finalmente comprometemos el contenedor para que sirva el modelo ResNet:

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

Ahora detengamos el recipiente base para servir.

docker kill serving_base
docker rm serving_base

Iniciar el servidor

Ahora iniciemos el contenedor con el modelo ResNet para que esté listo para servir, exponiendo el puerto gRPC 8500:

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

Consultar el servidor

Para el cliente, necesitaremos clonar el repositorio de TensorFlow Serving GitHub:

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

Consulta el servidor con resnet_client_grpc.py . El cliente descarga una imagen y la envía a través de gRPC para clasificarla en categorías de ImageNet .

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

Esto debería dar como resultado un resultado como:

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"
}

¡Funciona! ¡El servidor clasifica con éxito una imagen de gato!

Parte 3: Implementación en Kubernetes

En esta sección utilizamos la imagen del contenedor creada en la Parte 0 para implementar un clúster de servicio con Kubernetes en Google Cloud Platform .

Inicio de sesión del proyecto GCloud

Aquí asumimos que creó e inició sesión en un proyecto de gcloud llamado tensorflow-serving .

gcloud auth login --project tensorflow-serving

Crear un clúster de contenedores

Primero creamos un clúster de Google Kubernetes Engine para la implementación del servicio.

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

Lo que debería generar algo como:

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

Configure el clúster predeterminado para el comando del contenedor gcloud y pase las credenciales del clúster a kubectl .

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

lo que debería resultar en:

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

Sube la imagen de Docker

Ahora envíemos nuestra imagen al Registro de contenedores de Google para que podamos ejecutarla en Google Cloud Platform.

Primero etiquetamos la imagen $USER/resnet_serving usando el formato Container Registry y el nombre de nuestro proyecto,

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

A continuación, configuramos Docker para usar gcloud como asistente de credenciales:

gcloud auth configure-docker

A continuación enviamos la imagen al Registro,

docker push gcr.io/tensorflow-serving/resnet

Crear implementación y servicio de Kubernetes

La implementación consta de 3 réplicas del servidor resnet_inference controlado por una implementación de Kubernetes . Las réplicas se exponen externamente mediante un servicio Kubernetes junto con un balanceador de carga externo .

Los creamos usando el ejemplo de configuración de Kubernetes resnet_k8s.yaml .

kubectl create -f tensorflow_serving/example/resnet_k8s.yaml

Con salida:

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

Para ver el estado de la implementación y los pods:

$ 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

Para ver el estado del servicio:

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

Puede tomar un tiempo hasta que todo esté en funcionamiento.

$ 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

La dirección IP externa del servicio aparece junto a LoadBalancer Ingress.

Consultar el modelo

Ahora podemos consultar el servicio en su dirección externa desde nuestro host local.

$ 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"
}

¡Ha implementado con éxito el modelo ResNet que sirve como servicio en Kubernetes!