본문 바로가기

개발 이야기/실무 Recipe

가장 빠른 머신러닝 앱 배포하기 | fastapi, gunicorn, nginx 로 lightsail에 배포하기

 

 

가볍고 빠르게 구축하거나 실행할 수 있는 모듈들로 머신러닝 앱을 배포해보자.

 


 

fast api, uvicorn, gunicorn, nginx, lightsail

fast api

fastapi는 가볍고 높은 퍼포먼스를 낼 수 있으며 ASGI 서버를 지원하는 모던 웹 프레임워크다. ML 프로젝트는 데이터, 패키지만 있으면 다른 디펜던시가 필요없으므로 이것저것 설정해야하는 django보다는 가벼운 fastapi를 사용하면 빠르고 쉽게 앱을 build할 수 있다. 마이크로소프트, 넷플릭스와 같은 대형 서비스에서도 ML 프로젝트를 fastapi로 빌드했다고 한다.

 

uvicorn

uvloop, httptools를 사용해 가볍고 빠른 ASGI 서버를 구현할 수 있다. gunicorn과 함께 사용하려면 uvicorn worker를 사용하면 된다.

 

gunicorn

gunicorn은 WSGI 서버이다. 빠르고 가벼운 서버를 구축할 수 있고 여러 프레임워크에 상관없이 앱을 실행할 수 있다. uvicorn과 함께 사용하여 WSGI 대신 ASGI를 실행할 수 있다.

 

nginx

http proxy server다. nginx에서 80포트에 request가 들어오면 8000번 포트(uvicorn default port)에 포워딩하여 uvicorn에서 실행한 앱에서 요청을 처리할 수 있다. uvicorn에서도 포트 설정이 가능하지만 1024번 이하는 previlleged ports이므로 80번 포트에서 실행해도 클라이언트에서 요청을 받을 수 없다.

 

lightsail

웹에서 셸로 접속해서 환경을 구축하고 실행할 수 있다. EC2보다 더 저렴하게 인스턴스를 사용할 수 있다. 사양이 낮거나 불편한 경우 스냅샷을 찍어서 EC2로 업그레이드할 수도 있다.

 

 

 

start fast api

- https://fastapi.tiangolo.com/#create-it

 

- 패키지 설치

pip install fastapi uvicorn

 

- main.py

from fastapi import FastAPI
from my_classifier import build_classifier, transformer

app = FastAPI()
clf = build_classifier()


@app.get("/")
async def read_root():
    return {"msg": "World"}


@app.get("/classes")
async def read_classes(data = None):
    result = transformer(clf.predict(data))
    return {"classes": result}

 

 

- 앱 실행: uvicorn main:app --reload

 

 

 

Deploy on lightsail

 

fast api 문서를 보면 deta라는 클라우드 서비스에서도 배포할 수 있다.

홍보하는 대로 무료로 배포할 수 있기도 하지만 실제로 배포하기엔 *무리가 있었다.

 

*코드와 설치된 패키지를 포함해 250MB를 넘으면 배포 자체가 안된다. 하지만 가벼운 프로젝트라 해도 konlpy나 sklearn 패키지, 데이터셋만 합쳐 200MB는 쉽게 넘는다. 또한 배포 실패 메시지(ex. Error: failed to deploy: Request entity too large)마저 정확히 어떤 원인에서 에러가 났는지 알기 어렵다. (deta 슬랙이 있어 help 채널에서 검색하면 되긴하지만 AWS를 이용하는 게 더 편할 것이다.)

 

 

lightsail에서 자유롭게 환경을 구축하기 위해 ubuntu를 선택했다.

 

 

 

 

install default dependencies

인스턴스 생성 후 셸에 접속해 필요한 패키지들을 설치하여 환경을 구축한다.

 

sudo apt-get update
sudo apt-get install -y --no-install-recommends tzdata g++ git curl

 

 

 

install java(konlpy를 사용하는 경우)

konlpy는 내부적으로 jvm을 사용하기때문에 자바를 설치하고 자바 경로를 설정해주어야 한다.

 

- 자바 설치

sudo add-apt-repository ppa:openjdk-r/ppa
sudo apt-get update
sudo apt-get install -y openjdk-8-jdk

 

- JAVA_HOME 환경변수 설정

JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk
source /etc/environment && export JAVA_HOME

# 확인
echo $JAVA_HOME

 

 

install python

- python 설치

sudo apt-get install -y python3-pip python3-dev

 

- symlink python3 → python, pip3 → pip

cd /usr/local/bin
sudo ls -s /usr/bin/python3 python
sudo ln -s /usr/bin/pip3 pip
pip3 install --upgrade pip

 

- clean packages

sudo apt-get clean
sudo rm -rf /var/lib/apt/lists/*

 

 

 

install nginx

- install nginx 

sudo apt-get update
sudo apt-get install nginx
sudo systemctl start nginx

 

- /etc/nginx/sites-available 내 file에 쓰기 권한이 없으므로 chmod를 쓰기 권한을 추가해준다.

sudo chmod 775 /etc/nginx/sites-available

 

- nginx config 추가

cd /etc/nginx/sites-available && vim <서버이름>

 

AWS route53에서 도메인을 추가한다. A record를 생성하기 위해서는 고정된 IP 주소가 필요하다. lightsail 인스턴스에서 고정된 public IP를 생성해 A record에 연결한다. 생성한 도메인은 nginx conf에 추가한다.

 

server{
       server_name <your-site-domain>;
       location / {
           include proxy_params;
           proxy_pass http://127.0.0.1:8000;
       }
}

 

 

- symlink: sites-available/<server-name> → sites-enabled

sudo ln -s /etc/nginx/sites-available/<your-server-name> /etc/nginx/sites-enabled/

 

 

run app

- git clone <your-server-repo>

cd /var/www
git clone <server-repo>
cd <server-repo>

 

- restart nginx

sudo systemctl restart nginx.service

 

- gunicorn으로 ASGI 서버 실행

 

-k 옵션으로 worker class를 설정한다. gunicorn은 WSGI 서버지만 uvicorn worker 클래스를 사용해서 ASGI 서버를 실행할 수 있다. --daemon 옵션으로 백그라운드에서 실행할 수 있으며, --access-log로 실행되는 동안의 로그를 저장할 파일을 지정할 수 있다.

 

python3 -m gunicorn -k uvicorn.workers.UvicornWorker main:app \
	--daemon --access-logfile ./gunicorn-access.log

 

 

- <domain>/docs 에서 자동으로 생성된 api document를 확인할 수 있다.

 

 

 


 

 

references