Kubernetes와 함께 TensorFlow Serving 사용

이 튜토리얼에서는 Docker 컨테이너에서 실행되는 TensorFlow Serving 구성 요소를 사용하여 TensorFlow ResNet 모델을 제공하는 방법과 Kubernetes를 사용하여 제공 클러스터를 배포하는 방법을 보여줍니다.

TensorFlow Serving에 대해 자세히 알아보려면 TensorFlow Serving 기본 자습서 및 TensorFlow Serving 고급 자습서를 권장합니다.

TensorFlow ResNet 모델에 대해 자세히 알아보려면 TensorFlow의 ResNet을 읽어 보시기 바랍니다.

  • 1부에서는 환경 설정을 가져옵니다.
  • 2부에서는 로컬 Docker 제공 이미지를 실행하는 방법을 보여줍니다.
  • 3부 에서는 Kubernetes에 배포하는 방법을 보여줍니다.

파트 1: 설정

시작하기 전에 먼저 Docker를 설치하세요 .

ResNet 저장된 모델 다운로드

이미 로컬 모델 디렉토리가 있는 경우 로컬 모델 디렉토리를 삭제해 보겠습니다.

rm -rf /tmp/resnet

심층 잔차 네트워크(Deep Residual Networks, 줄여서 ResNets)는 매우 심층적인 컨벌루션 신경망을 훈련할 수 있도록 ID 매핑이라는 획기적인 아이디어를 제공했습니다. 이 예에서는 ImageNet 데이터 세트에 대한 ResNet의 TensorFlow SavedModel을 다운로드합니다.

# 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/

저장된 모델이 있는지 확인할 수 있습니다.

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

2부: Docker에서 실행

배포용 이미지 커밋

이제 제공 이미지를 가져와 Kubernetes 배포를 위한 새 이미지 $USER/resnet_serving 에 대한 모든 변경 사항을 커밋 하려고 합니다.

먼저 제공 이미지를 데몬으로 실행합니다.

docker run -d --name serving_base tensorflow/serving

다음으로 ResNet 모델 데이터를 컨테이너의 모델 폴더에 복사합니다.

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

마지막으로 ResNet 모델을 제공하기 위해 컨테이너를 커밋합니다.

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

이제 제공 기본 컨테이너를 중지해 보겠습니다.

docker kill serving_base
docker rm serving_base

서버 시작

이제 ResNet 모델로 컨테이너를 시작하여 gRPC 포트 8500을 노출하여 서비스 준비를 완료하겠습니다.

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

서버에 쿼리

클라이언트의 경우 TensorFlow Serving GitHub 저장소를 복제해야 합니다.

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

resnet_client_grpc.py 로 서버를 쿼리합니다. 클라이언트는 이미지를 다운로드하고 ImageNet 범주로 분류하기 위해 gRPC를 통해 보냅니다.

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

결과는 다음과 같습니다.

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

작동합니다! 서버가 고양이 이미지를 성공적으로 분류했습니다!

3부: Kubernetes에 배포

이 섹션에서는 0부에서 구축한 컨테이너 이미지를 사용하여 Google Cloud Platform 에 Kubernetes 가 포함된 제공 클러스터를 배포합니다.

GCloud 프로젝트 로그인

여기서는 tensorflow-serving 이라는 gcloud 프로젝트를 만들고 로그인했다고 가정합니다.

gcloud auth login --project tensorflow-serving

컨테이너 클러스터 만들기

먼저 서비스 배포를 위한 Google Kubernetes Engine 클러스터를 만듭니다.

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

다음과 같이 출력되어야 합니다.

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

gcloud 컨테이너 명령어의 기본 클러스터를 설정하고 클러스터 사용자 인증 정보를 kubectl 에 전달합니다.

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

결과는 다음과 같습니다.

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

Docker 이미지 업로드

이제 Google Cloud Platform에서 실행할 수 있도록 이미지를 Google Container Registry 에 푸시하겠습니다.

먼저 Container Registry 형식과 프로젝트 이름을 사용하여 $USER/resnet_serving 이미지에 태그를 지정합니다.

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

다음으로 gcloud를 사용자 인증 정보 도우미로 사용하도록 Docker를 구성합니다.

gcloud auth configure-docker

다음으로 이미지를 레지스트리에 푸시합니다.

docker push gcr.io/tensorflow-serving/resnet

Kubernetes 배포 및 서비스 생성

배포는 Kubernetes 배포 에 의해 제어되는 resnet_inference 서버의 복제본 3개로 구성됩니다. 복제본은 외부 로드 밸런서 와 함께 Kubernetes 서비스 에 의해 외부에 노출됩니다.

예제 Kubernetes 구성 resnet_k8s.yaml을 사용하여 이를 생성합니다.

kubectl create -f tensorflow_serving/example/resnet_k8s.yaml

출력:

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

배포 및 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

서비스 상태를 보려면:

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

모든 것이 준비되어 실행되는 데 시간이 걸릴 수 있습니다.

$ 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

서비스 외부 IP 주소는 LoadBalancer Ingress 옆에 나열됩니다.

모델 쿼리

이제 로컬 호스트의 외부 주소에서 서비스를 쿼리할 수 있습니다.

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

Kubernetes에서 서비스 역할을 하는 ResNet 모델을 성공적으로 배포했습니다!