본문 바로가기

실습

Tensorflow Serving을 위한 Kubernetes Pod Readiness probe 설정

Kubernetes cluster에 tensorflow serving을 위한 pod를 만들때에는 pod probes 설정이 중요합니다. 특히, production 환경이라면 적절한 probe 설정은 필수라고 볼 수 있습니다. 왜냐하면, tensorflow serving이 servable 모델을 초기 로딩하는데 시간이 많이 소요되기 때문입니다. 필요한 모델이 많을 수록 초기 로딩 시간은 더욱 늘어나게 됩니다. Pod는 running 상태로 되었지만, 여전히 모델이 로드되지 않은 상태이기 때문에 연결된 로드 밸런서와 같은 kubernetes service로부터 트래픽을 받게되지만, 실제 작업 수행은 실패하게 되는 결과를 가져옵니다.

 

Probes의 종류

Pod probe는 다음 세가지 종류가 있습니다.

  • Pod liveness probe: 언제 컨테이너를 재시작할 것 인지 결정
  • Pod readiness probe: 언제 네트워크 요청을 받기 시작할 것 인지 결정. 백엔드 서비스를 위한 pod에 유용한 설정이다.
  • Pod startup probe: 컨테이너 어플리케이션이 언제 시작되었는지 확인. Pod가 시작되기 전까지는 liveness와 readiness probes를 비활성화 시킨다. 매우 느리게 시작되는 컨테이너의 경우 liveness probe가 계속해서 pod를 재시작 시키는 것을 막을 수 있다.

Liveness probe는 tensorflow serving이 실행 중이지만, 정상적으로 동작하지 않는 경우 재시작하기 위함이므로 tensorflow serving이 비교적 검증이 된 프로그램이라면 셋 중 중요도는 조금 낮다고 볼 수 있습니다. 따라서, 여기서는 tensorflow serving pod의 readiness probe를 설정하는 방법의 예제를 설명합니다.

 

Tensorflow Serving 예제

예제로 사용할 모델은 Tensorflow 가이드에 있는 ResNet 모델입니다. Tf serving 컨테이너를 실행하기 위한 간단한 deployment.yaml 리소스는 다음과 같습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tf-serving-deployment
  labels:
    app: tf-serving
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tf-serving
  template:
    metadata:
      labels:
        app: tf-serving
    spec:
      initContainers:
      - name: copy-model
        image: busybox
        command:
          - "/bin/sh"
          - "-c"
          - |
            mkdir /models/resnet
            wget http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NHWC_jpg.tar.gz
            tar -xvz -f resnet_v2_fp32_savedmodel_NHWC_jpg.tar.gz --strip-components=2 -C /models/resnet
        volumeMounts:
        - name: models-volume
          mountPath: /models
      containers:
      - name: tf-serving
        image: tensorflow/serving
        ports:
        - containerPort: 8500
        - containerPort: 8501
        env:
        - name: MODEL_NAME
          value: resnet
        - name: MODEL_BASE_PATH
          value: /models
        volumeMounts:
        - name: models-volume
          mountPath: /models
      volumes:
      - name: models-volume
        emptyDir: {}

편의상 initContainers를 이용하여 model을 다운로드하였습니다.

deployment를 생성한 모습

모델의 버전이 1538687457인 것을 확인할 수 있습니다.

 

모델 상태 API

Tensorflow serving은 RESTful API로 모델 상태 API를 제공합니다. 모델 상태 API를 통해 특정 버전의 모델, 혹은 모든 모델에 대해서 ModelVersionStatus 값을 얻을 수 있습니다. 가능한 status 값들은 START, LOADING, AVAILABLE, UNLOADING, END가 있습니다. 우리가 원하는 것은 pod의 readiness probe가 필요한 모델이 모두 AVAILABLE일때를 확인하는 것입니다.

 

먼저, 실행 중인 deployment에 API를 테스트 해봅니다. 테스트를 위해 deployment에 port forward를 합니다.

$ kubectl port-forward deployment/tf-serving-deployment 8501:8501
$ curl http://127.0.0.1:8501/v1/models/resnet
{
 "model_version_status": [
  {
   "version": "1538687457",
   "state": "AVAILABLE",
   "status": {
    "error_code": "OK",
    "error_message": ""
   }
  }
 ]
}

 

/versions/${VERSION} path를 추가해서 특정 버전만 테스트 할 수 있습니다.

$ curl http://127.0.0.1:8501/v1/models/resnet/versions/1538687457
...

 

Readiness Probe 설정

Pod의 readiness probe를 설정하는 방법은 세가지 종류가 있습니다. HTTP probe, TCP probe, 그리고 쉘 명령을 이용한 exec probe가 있습니다. 여기서는 exec probe를 사용합니다. HTTP probe를 사용하지 않는 것은 API가 state에 따라 HTTP response code를 보내주지 않기 때문입니다. 모델 상태 API는 curl, wget 과 같은 명령어로 요청할 수 있습니다. 하지만 tensorflow/serving 이미지에는 해당 명령어가 설치되어 있지 않아 바로 사용할 수 없습니다. 여기서는 busybox 툴 컨테이너를 하나 더 생성하여 probe를 설정합니다. Pod에 있는 컨테이너들은 localhost 네트워크를 공유하기 때문에 쉽게 tensorflow serving API를 호출 할 수 있습니다. 그리고 모든 컨테이너들의 readiness를 확인하기 때문에 busybox 컨테이너에 readiness probe를 설정하는 것은 tensorflow/serving에 설정하는 것과 같은 효과가 있을 것 입니다.

 

다음은 readiness probe를 위한 추가된 readiness-check 컨테이너입니다.

      containers:
      ...생략...
      - name: readiness-check
        image: busybox
        command:
          - "/bin/sh"
          - "-c"
          - |
            wget -O /tmp/jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64; chmod +x /tmp/jq
            while true; do sleep 30; done;
        readinessProbe:
          exec:
            command:
            - "/bin/sh"
            - "-c"
            - |
              /usr/local/bin/check_model_availability resnet 1538687457
          initialDelaySeconds: 5
          periodSeconds: 5
        volumeMounts:
        - name: bins-volume
          mountPath: /usr/local/bin
      volumes:
      - name: bins-volume
        configMap:
          name: bins
          defaultMode: 0755
      - name: models-volume
        emptyDir: {}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: bins
data:
  check_model_availability: |
    #!/bin/sh
    wget -O /tmp/readiness-check.json "http://localhost:8501/v1/models/$1/versions/$2"
    states=$(cat /tmp/readiness-check.json | /tmp/jq -j '.model_version_status[] | .version + "," + .state + " "')
    for s in $states; do
      i=0
      for t in `echo $s | tr ",", "\n"`; do
        if [ $i -eq 0 ]; then
          version=${t}
        else
          state=${t}
        fi
        i=$((i+1))
      done
      if [ "${version}" = "$2" ]; then
        if [ "${state}" = "AVAILABLE" ]; then
          echo "Success: models/$1/versions/$2 is available."
          exit 0
        fi
      fi
    done
    echo "Failed: models/$1/versions/$2 is unavailable."
    exit 1

 

편의 상 ConfigMap을 통해서 check_model_availability 스크립트를 컨테이너에 넣어 주었습니다. 어떤 식으로 작성되었는지 예제로써 보시고 각자의 목적에 맞는 스크립트를 작성하셔서 사용하시면 될 것 같습니다. 스크립트는 아래처럼 readinessProbe에서 사용합니다.

/usr/local/bin/check_model_availability $MODEL $VERSION

여러 모델의 여러 버전을 체크해야 해야하는 경우는 스크립트를 필요한 만큼 호출하셔서 사용하시면 될 것 같습니다. 완성된 버전으로 deployment를 생성하면 지정된 버전의 모델이 모두 AVAILABLE 상태로 될 때 pod가 서비스를 시작하게 됩니다.

 

 

참고

[1] Configure Liveness, Readiness and Startup Probes, https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

[2] Tensorflow Serving RESTful API, https://www.tensorflow.org/tfx/serving/api_rest

[3] Multi-container pods and container communication in Kubernetes (mirantis.com)