고문헌 번역 프로젝트(4) - 웹서버 개발 / 환경구축

업데이트:

고문헌 번역 프로젝트(2) - 첫번째 번역 모델

고문헌 번역 프로젝트(3) - Transformer:robot: 적용

GitHub Repository :octocat:

모델도 거의 완성되었고, 번역기를 돌려볼수 있는 웹 서비스를 개발해보자.

소개

이전에 딥러닝 모델을 돌려볼수 있게끔 웹서비스를 개발해 본 적이 있었다. (인터넷방송 유해도 산출을 통한 필터링 시스템)

모델이 크고 서버 성능이 좋지 않아 사용자 트래픽이 조금만 많아져도 서버가 꺼지는 문제가 많이 발생했었다. Flask로 개발했는데 당시에는 몰랐으나, Flask는 경량 프레임워크라 기본적으로 프로덕션 서버가 붙어있지 않다는 걸 모르고 그냥 개발용 서버로 배포해버려서 그랬던 것 같다..

아무튼! 이런 상황을 방지하기 위해서 이번에는 Flask에 프로덕션 서버 설치하고

Google Kubernetes Engine을 사용해서

  • 클라우드에서 Auto Scale-out 하고
  • Load Balancing 도 고려해서 개발해보려 한다.

웹 프레임워크로는 Flask 사용할 것이다.

  • 딥러닝 모델은 연산이 무겁다
    • 경량 프레임워크인 Flask 사용해서 필요한 것만 남기자
  • Flask는 Python 기반이다.
    • 텐서플로우 프레임워크와 호환이 비교적 자유롭다.

Flask Web Server

Flask를 이용해 웹서버를 구축해보자.

대부분의 Flask 코드는 책 “깔끔한 파이썬 탄탄한 백엔드 (송은우 저)” 을 참고하여 작성했습니다.

굉장히 불친절한 코드 죄송합니다. 책과 깃허브를 참고해주세요

  1. Flask 설치

  2. 사용자의 요청을 받는 API 코드 작성

    #app.py
    from flask import Flask, request, render_template
    import demo_run
       
    def create_app():
        app = Flask(__name__)
       
        @app.route('/', methods=['GET', 'POST'])
        def demo():
            if request.method == "POST":
                query = request.form.get('query')
                # model predict
                print(query)
                result = demo_run.translate(query)
                return render_template("demo.html", result=result, query=query)
            else:
                return render_template('demo.html')
               
        return app
    
    • 사용자의 입력을 input으로 하는 model predict 코드 작성
    자세한 코드는 [github 저장소](https://github.com/getChan/ADV/server/demo_run.py)를 참고
    
  3. 배포용 서버(Production Server)로 설정하여 배포

    • flask-twistedflask_script 라이브러리 설치
    #setup.py
    import sys
         
    from flask_script import Manager
    from app import create_app
    from flask_twisted import Twisted
    from twisted.python import log
         
    if __name__ == '__main__':
        app = create_app()
        twisted = Twisted(app)
        log.startLogging(sys.stdout)
         
        app.logger.info(f'Running the app...')
         
        manager = Manager(app)
        manager.run()
    
  4. Server Run

    # 80번 포트로 서버 run
    # `nohub`은 현재 ssh 세션이 종료되어도 해당 명령어를 계속 실행시켜 준다
    # `&`은 프로세스를 background에서 실행한다.
    $ nohub python setup.py runserver --host=0.0.0.0 --port=80 &
    

Docker Image 생성

kubernetes는 docker image를 가지고 container를 생성하여 동일한 서버 환경을 그대로 가져온다.

우선적으로, 배포할 Docker Image를 생성해보자

Docker 참고 포스팅 / Kubernetes 참고 포스팅

Base Image Pull

일단, 참고 문서를 따라서 텐서플로우 모델을 GPU상에서 실행할 수 있는 도커 이미지를 가져온다.

  • 텐서플로우에서 tensorflow model GPU 연산을 쉽게 할 수 있는 Docker 이미지를 배포해두었다.

  • host server에 nvidia gpu driver만 있으면 CUDA 등의 설치 없이 도커 이미지만을 가지고 GPU 연산을 수행할 수 있는 도커 이미지이다.

  • nvidia gpu driver는 GKE에서 자동으로 설치하는 Demon set을 제공한다. 따라서 드라이버를 따로 설치할 필요 없다.

$ docker pull tensorflow/tensorflow:latest-gpu-py3
# 가장 최신 이미지의 GPU연산과 python3를 지원하는 이미지 pull

nvidia-docker를 설치해야 컨테이너에서 nvidia-GPU를 사용할 수 있다고 한다. 설치 후 확인해보자

$ docker run --gpus all nvidia/cuda:9.0-base nvidia-smi

가져온 이미지를 컨테이너로 실행하고 컨테이너에 진입해보자. 80번 포트는 컨테이너에 바인딩한다.

$ docker run --gpus all --name adv-container -p 0.0.0.0:80:80 -it --rm tensorflow/tensorflow:latest-gpu-py3 bash

컨테이너가 잘 실행이 된다면, 만든 컨테이너를 새롭게 Image 파일로 만들어야 한다.

Make Dockerfile

FROM tensorflow/tensorflow:latest-gpu-py3 # base image pull
MAINTAINER 9511chn@gmail.com

COPY ./ADV/ /usr/src/app # 소스 복사

WORKDIR /usr/src/app/ # 실행 디렉토리 설정
RUN pip3 install -r requirements.txt # 패키지 설치
WORKDIR /usr/src/app/server

EXPOSE 80 # Listen port 
# CMD : 도커 컨테이너가 실행되었을 때 실행되는 명령어	
CMD python3 setup.py runserver --host=0.0.0.0 --port=80

Docker Build

$ docker build -t getchan/adv-image .
# -t(--tag) : 이미지 이름을 지정

성공적으로 설치되었다! 컨테이너를 실행해보자

$ docker run --gpus all --name adv-container -p 0.0.0.0:80:80 -it --rm getchan/adv-image

Docker push

Docker Hub에 이미지를 push해 보자.

일단 Docker Hub에 회원가입한 뒤

$ docker login
$ docker push docker push getchan/adv-image

여기까지는 성공적이다! 이제 쿠버네티스로 배포해야한다 :smile:

Google Kubernetes Engine

GCP에서 Kubernetes Engine을 이용하려고 한다. (GKE)

운이 좋게도 Google Study Jam 자격증반에 참여하면서 Qwiklab 크레딧을 주고 한달간 실습할수 있게끔 해줘서 GCP console사용법을 익힐 수 있었다.

또한 GKE GPU 가이드 문서가 아주 잘 설명되있어서 참고하여 구축하였다.

모델의 연산이 너무 커서 GPU가 설치된 서버 인스턴스가 필요했다. (GPU 제일 싼게 0.4$ / hour :sob:)

기본적으로 GPU 달린 인스턴스를 만들려면 제한된 할당량을 해제해줘야 한다.(디폴트로 GPU 0개 제한되어 있음)

본격적으로 시작하기 앞서서 쿠버네티스 엔진에서 순차적으로 해야 할 작업은 다음과 같다.

  • 쿠버네티스 엔진 생성
    • 최대 노드(with GPU) 4개, Auto Scale Out. Load Balancing
  • 각 노드(server)마다 Nvidia Driver 설치해야 함
    • GKE에서 자동으로 설치하는 Demon set을 제공한다.:heart_eyes: 따라서 드라이버를 따로 설치할 필요 없다.
  • 각 Pod(App) 마다 생성한 이미지 배포

클러스터 생성

일단 GKE 클러스터를 생성하고, 생성된 노드에 nvidia driver를 설치해주자.

$ kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/container-engine-accelerators/master/nvidia-driver-installer/cos/daemonset-preloaded.yaml

자세한 클러스터 생성법은 가이드 문서를 참고해주세요

애플리케이션 배포

docker hub에 올린 이미지를 받아서 배포 객체를 생성한다.

# deployments.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: adv-deployment
  labels:
    app:adv
spec:
  replicas: 4 # 노드당 1개의 GPU 가지고 있다. 4개 노드에 하나씩 pod 배포할 것이다.
  selector:
    matchLabels:
      app: adv
  template:
    metadata:
      labels:
        app: adv
    spec:
      containers:
      - image: docker.io/getchan/adv-image # 이미지 주소
        name: adv-container
        resources:
          limits:
            nvidia.com/gpu: 1 # GPU 사용
          ports:
          - containerPort: 80
$ kubectl apply -f deployment.yaml

외부에서 접근할 수 있도록 서비스 오브젝트를 생성한다.

$ kubectl expose deployment adv-deployment --type LoadBalancer --name=adv-service

서비스에 대한 자세한 정보를 확인해보자

$kubectl describe services adv-service
Name:                     adv-service
Namespace:                default
Labels:                   app=adv
Annotations:              <none>
Selector:                 app=adv
Type:                     LoadBalancer
IP:                       10.43.252.49
LoadBalancer Ingress:     35.184.165.210
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  32760/TCP
Endpoints:                10.40.2.16:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type    Reason                Age    From                Message
  ----    ------                ----   ----                -------
  Normal  EnsuringLoadBalancer  3m55s  service-controller  Ensuring load balancer
  Normal  EnsuredLoadBalancer   3m     service-controller  Ensured load balancer
  • LoadBalancer Ingress : 노출된 외부 IP의 주소
  • Port : 외부에서 접근하는 포트
  • NodePort : 각 노드에 접근하는 포트
  • Endpoint : app을 실행 중인 Pod의 내부 주소

외부 IP로 접속해서 잘 동작하는지 확인해보자.

또 app이 GPU에서 실행되고 있는지 로그를 확인해보자.

$ kubectl logs adv-deployment-7b9d4d945b-6cmwp
tf Version : 2.1.0
GPU Available : True
Model Load completed!!

:tada:

댓글남기기