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 PlatformKubernetes 가 포함된 제공 클러스터를 배포합니다.

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 모델을 성공적으로 배포했습니다!