Usar a veiculação do TensorFlow com o Kubernetes

Este tutorial mostra como usar os componentes do TensorFlow Serving executados em contêineres do Docker para servir o modelo TensorFlow ResNet e como implantar o cluster de serviço com o Kubernetes.

Para saber mais sobre o TensorFlow Serving, recomendamos o tutorial básico do TensorFlow Serving e o tutorial avançado do TensorFlow Serving .

Para saber mais sobre o modelo TensorFlow ResNet, recomendamos a leitura de ResNet em TensorFlow .

  • A Parte 1 obtém a configuração do seu ambiente
  • A parte 2 mostra como executar a imagem de veiculação local do Docker
  • A Parte 3 mostra como implantar no Kubernetes.

Parte 1: Configuração

Antes de começar, primeiro instale o Docker .

Baixe o ResNet SavedModel

Vamos limpar nosso diretório de modelos locais caso já tenhamos um:

rm -rf /tmp/resnet

Redes residuais profundas, ou ResNets, forneceram a ideia inovadora de mapeamentos de identidade para permitir o treinamento de redes neurais convolucionais muito profundas. Para nosso exemplo, faremos o download de um TensorFlow SavedModel do ResNet para o conjunto de dados 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 se temos o SavedModel:

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

Parte 2: Executando no Docker

Confirmar imagem para implantação

Agora queremos pegar uma imagem de serviço e confirmar todas as alterações em uma nova imagem $USER/resnet_serving para implantação do Kubernetes.

Primeiro, executamos uma imagem de serviço como um daemon:

docker run -d --name serving_base tensorflow/serving

Em seguida, copiamos os dados do modelo ResNet para a pasta do modelo do contêiner:

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

Por fim, comprometemos o contêiner para servir ao modelo ResNet:

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

Agora vamos parar o contêiner base de veiculação

docker kill serving_base
docker rm serving_base

Inicie o servidor

Agora vamos iniciar o container com o modelo ResNet para que esteja pronto para servir, expondo a porta gRPC 8500:

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

Consultar o servidor

Para o cliente, precisaremos clonar o repositório do TensorFlow Serving GitHub:

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

Consulte o servidor com resnet_client_grpc.py . O cliente baixa uma imagem e a envia pelo gRPC para classificação em categorias do ImageNet .

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

Isso deve resultar em uma saída 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! O servidor classifica com sucesso uma imagem de gato!

Parte 3: implantar no Kubernetes

Nesta seção, usamos a imagem de contêiner criada na Parte 0 para implantar um cluster de serviço com Kubernetes no Google Cloud Platform .

Login do projeto GCloud

Aqui, presumimos que você criou e fez login em um projeto gcloud chamado tensorflow-serving .

gcloud auth login --project tensorflow-serving

Criar um cluster de contêiner

Primeiro, criamos um cluster do Google Kubernetes Engine para implantação de serviço.

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

Que deve produzir 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

Defina o cluster padrão para o comando gcloud container e passe as credenciais do cluster para kubectl .

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

que deve resultar em:

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

Carregar a imagem do Docker

Vamos agora enviar nossa imagem para o Google Container Registry para que possamos executá-la no Google Cloud Platform.

Primeiro, marcamos a imagem $USER/resnet_serving usando o formato Container Registry e o nome do nosso projeto,

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

Em seguida, configuramos o Docker para usar o gcloud como auxiliar de credenciais:

gcloud auth configure-docker

Em seguida, enviamos a imagem para o Registro,

docker push gcr.io/tensorflow-serving/resnet

Criar implantação e serviço do Kubernetes

A implantação consiste em 3 réplicas do servidor resnet_inference controladas por uma implantação do Kubernetes . As réplicas são expostas externamente por um serviço Kubernetes junto com um balanceador de carga externo .

Nós os criamos usando o exemplo de configuração do Kubernetes resnet_k8s.yaml .

kubectl create -f tensorflow_serving/example/resnet_k8s.yaml

Com saída:

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

Para visualizar o status da implantação e dos 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 visualizar o status do serviço:

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

Pode demorar um pouco para que tudo esteja funcionando.

$ 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

O endereço IP externo do serviço é listado ao lado de LoadBalancer Ingress.

Consultar o modelo

Agora podemos consultar o serviço em seu endereço externo de nosso 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"
}

Você implantou com sucesso o modelo ResNet servindo como um serviço no Kubernetes!