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 obtiene la configuración de 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 modelo guardado de TensorFlow de ResNet para el conjunto de datos de 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 modelo guardado:

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

Parte 2: Ejecutar en Docker

Confirmar imagen para implementación

Ahora queremos tomar una imagen de servicio 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 un demonio:

docker run -d --name serving_base tensorflow/serving

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

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

Finalmente, comprometemos el contenedor para servir el modelo ResNet:

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

Ahora detengamos el contenedor base de servicio.

docker kill serving_base
docker rm serving_base

Inicie el servidor

Ahora comencemos 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 &

consulta el servidor

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

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 una salida 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: Implementar en Kubernetes

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

Iniciar sesión en el proyecto GCloud

Aquí asumimos que ha creado e iniciado 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

Establece el clúster predeterminado para el comando gcloud container y pasa 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 empujemos nuestra imagen a Google Container Registry para que podamos ejecutarla en Google Cloud Platform.

Primero etiquetamos la imagen $USER/resnet_serving usando el formato de Container Registry y nuestro nombre de 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, empujamos 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 de Kubernetes junto con un equilibrador de carga externo .

Los creamos usando el ejemplo Kubernetes config 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 para 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.

consulta 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 actúa como un servicio en Kubernetes.