작성 주식 뉴스 분석 프로젝트 <news crawl><4>

프로젝트 개요 및 각 글들의 링크: https://namth.tistory.com/18

 

핵심적인 로직들을 작성하였으므로 이제 http 요청에 응답 가능하게 웹 서버를 작성하고,
배포를 위해 dockerfile을 작성할 것이다.


 

웹 서버


웹 서버 프레임워크는 Flask를 사용하여 작성하였다.
구동은 gunicorn으로 구동할 예정이다.

POST body의 데이터에서 크롤링할 주제의 이름과 언어의 코드를 가져온다. (subject, source_lang)

라우팅 되는 함수의 데코레이터 중 abstract_request 함수가 있는데, 원래는 cors 및 jwt토큰 검사를 위해 request객체를 추출하는
함수였지만, 여기서는 제대로 사용하지 않고 있다. (modules/req_valid.py)


# https://github.com/mannamman/newsCrawl/blob/main/main.py
from modules.newsCrawler import HeaderCrawler
from modules.mongo_db import DBworker
from modules.file_worker import FileWorker
from modules.log_module import Logger
from finBERT.sentiment import FinBert
import pytz
import datetime
import functools
from math import ceil
import os
import traceback
import copy
from typing import Tuple, List, Dict
from uuid import uuid4
from flask import Response, Request
import json
# multi process
from multiprocessing import Pool
# server
from flask import request, Flask
app = Flask(__name__)
# 객체 초기화(공통으로 사용되는)
db_worker = DBworker()
file_worker = FileWorker()
sentiment_finbert = None
logger = Logger()
KST = pytz.timezone("Asia/Seoul")
def init_sentiment():
global sentiment_finbert
sentiment_finbert = FinBert()
def abstract_request(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(request)
return wrapper
@app.route("/sentiment", methods=["POST"])
@abstract_request
def index(req: Request):
global KST
global logger
global sentiment_finbert
global db_worker
# lazy loading
if(sentiment_finbert is None):
init_sentiment()
try:
# post 메시지 파싱
recevied_msg = json.loads(req.get_data().decode("utf-8"))
subject = recevied_msg["subject"]
source_lang = recevied_msg["source_lang"]
...
return Response(response="ok", status=200)
except Exception:
error = traceback.format_exc()
error = pretty_trackback(error)
logger.error_log(error)
return Response(response=error, status=400)
if(__name__ == "__main__"):
app.run(host="0.0.0.0", port=8080, debug=False)

 

Dokcerfile 작성


GCP cloud run으로 배포하기 위하여 웹서버를 docker image로 만들어야 했다.

GCP cloud run을 사용한 이유는 우선 회사에서 사용을 자주 해봐서 익숙하고,
사용한 만큼 요금이 부과되어서 요금이 적게 든다. 또한 요청이 줄거나 증가하면 인스턴스(서버)의 개수를 조정하여 부하를 맞춰준다.



FROM python:3.8.12-slim-buster
LABEL maintainer="wase894@gmail.com"
ENV TRANSFORMERS_OFFLINE 1
EXPOSE 8080
WORKDIR /app
COPY . /app
RUN apt update && apt install -y curl
RUN pip3 install -r requirements.txt
RUN python3 -m nltk.downloader punkt -d /usr/local/nltk_data
RUN python3 -m nltk.downloader stopwords -d /usr/local/nltk_data
# 개인 스토리지(public)
RUN curl https://storage.googleapis.com/public-model-bucket/pytorch_model_fin.bin --output /app/finBERT/models/sentiment/pytorch_model.bin
CMD exec gunicorn --bind 0.0.0.0:8080 --workers 2 --threads 1 --timeout 240 main:app
view raw blog_21_code_2 hosted with ❤ by GitHub




실행에 필요한 모듈 목록은 requirements.txt에 있으므로 해당 파일 읽어 모듈을 설치하고
nltk의 경우 따로 데이터가 더 필요하여 추가적으로 데이터를 다운로드하였다.

모델의 경우 다운로드를 위해 스토리지에 업로드 후 객체를 공개로 전환하여 다운로드가 가능하게 만들었다.

Flask와 gunicorn을 사용해야 성능이 제대로 나오므로 gunicorn을 이용하여서 서버를 실행시켰다.

 

개발 시 만나는 출력문

 

 

배포(gunicorn) 시 출력문

참고자료

알게 된 점 기록

# 2022.09.30 수정