[Python] Flask RESTful API를 gunicorn WSGI 이용 및 supervisor 활용 Develop Tip

지난번에 "Flask로 간단한 HTTPS REST API 제공 및 테스트"에 관한 블로깅을 한 적이 있습니다.

지난번에 만들어 보았던 Flask를 실제로 서비스에 활용하는 방법을 살펴봅니다.

플래스크와 같은 파이썬 마이크로 프레임워크를 이용하면 아주 간단하게 RestFul API를 제공할 수 있습니다.

하지만 문제는 해당 프로그램을 직접 수행하여야 하며,
해당 루틴은 Blocking 함수로서 대부분 동작하므로 실제 서비스에는 맞지 않습니다.

이에 플래스크 문서를 보면

와 같이 Gunicorn 또는 Tornado를 이용하라고 되어 있습니다.

우선 WSGI가 무엇인지 살펴보면, 
기존 웹서버 입장에서는 CGI GW 역할을, 프레임워크 입장에서는 웹서버 역할을 하는
일종의 미들웨어처럼 생각하면 된다고 합니다.

그냥 심플하게 플래스크로 RESTful API를 만들고 나면 이것을 실제로 N개의 인스턴스가 떠서
서비스를 제공하는 웹 서버로 동작한다고 간주하면 되겠습니다.

위의 참조 블로그에 있던 그림을 살펴보겠습니다.

일반 웹서비스 보다는 최신의 SPA 에 더 잘 어울리는 모습입니다.

다음과 같은 순서로 서비스를 보통 하게됩니다.

1) HTTP 요청
위부 웹브라우저에서 이 사이트로 요청을 하면 우선 정적 이미지, JavaScript, CSS 등을 클라이언트
웹 브라우져로 보내게 됩니다.
이런 일을 하는 정적 브라우징 역할을 담당하는 것이 NginX 입니다.
이 NginX는 과거 DDoS 공격이 빈번할 당시 Reverse Cache 엔진으로 아파치나 톰캣 서버 앞단에서
보호하는 웹캐시로 더 명성을 날렸습니다.
그만큼 정적 URI 리소스를 외부에 서비스하는데 NginX >> Apache >> Tomcat 등의 순으로
더 잘 하게 됩니다.

2) REST API URI 요청
정적 리소스와 달리 8001번 포트로 REST API를 제공한다고 하면
이를 WSGI로 단일 도메인(추측인데 틀리면 지적해 주십시오)으로 서비스를 제공합니다.

위와 같은 그림에서 오른편의 Python이 Flask를 이용한 파이썬 프로그램이고
둘을 연결해 주는 WSGI 역할을 담당하는 것이 바로 gunicorn 입니다.

gunicorn은 필요에 따라 지정한 데로 인스턴스를 N개 만들어 동시 서비스를 제공하는 worker 로서
동작을 합니다.

3) 실제 Flask 루틴
실제 REST API 제공 루틴입니다. 


위와 같은 내용을 보고는 우선 gunicorn 부터 돌려보기로 하였습니다.

sudo pip install gunicorn
으로 간단히 설치 완료됩니다.

which gunicorn
/usr/local/bin/gunicorn
이 설치 되었는지 확인합니다.

gunicorn -w6 --certfile=future.crt --keyfile=future.key rest_api:app -b 0.0.0.0:8443
명령어를 주면 

ps -ef | grep gunicorn
로 프로세스를 확인하니,
future    16298   2554  0 16:12 pts/2    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w6 --certfile=future.crt --keyfile=future.key restapi:app -b 0.0.0.0:8443
future    16303  16298  0 16:12 pts/2    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w6 --certfile=future.crt --keyfile=future.key restapi:app -b 0.0.0.0:8443
future    16308  16298  0 16:12 pts/2    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w6 --certfile=future.crt --keyfile=future.key restapi:app -b 0.0.0.0:8443
future    16313  16298  0 16:12 pts/2    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w6 --certfile=future.crt --keyfile=future.key restapi:app -b 0.0.0.0:8443
future    16318  16298  0 16:12 pts/2    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w6 --certfile=future.crt --keyfile=future.key restapi:app -b 0.0.0.0:8443
future    16323  16298  0 16:12 pts/2    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w6 --certfile=future.crt --keyfile=future.key restapi:app -b 0.0.0.0:8443
future    16328  16298  0 16:12 pts/2    00:00:00 /usr/bin/python /usr/local/bin/gunicorn -w6 --certfile=future.crt --keyfile=future.key restapi:app -b 0.0.0.0:8443

과 같이 6개의 인스턴스가 더 만들어져 서비스를 제공하고 있습니다.


해당 서비스의 rest_api.py 에 있는 app 가 6개 더 생성됩니다.
-w n 은 인스턴스 개수로서 보통 코어 개수 X 2 을 준다고 하는군요.


이제는 위의 gunicorn을 그위에 감독관 역할을 하는 데몬이 있어 시스템이 동작하면서 자동으로 기동하고
중간에 혹시 죽더라도 다시 재기동 시키는 동작을 하도록 해 보겠습니다.

node.js 에는 forever 모듈이 있어 유사한 기능을 제공하는데 파이썬에서는 
supervisor 라는 것이 그 역할을 합니다.

모두 특정 언어에 국한된 것은 아니니 다른 곳에서도 이용 가능합니다.

supervisor를 확인해 보면 pip로 설치를 해 보라고 되어 있으나
다른 설정 및 서비스파일 등과 최신 버전의 오류 때문에 
우분투 12.04에서는 동작을 하지 않아 (2015/01/12 현재)
pip 대신 apt-get 으로 설치 합니다.

sudo apt-get install supervisor

만약 네트워크가 없는 곳에 설치를 하려면,
/var/cache/apt/archives 에 있는

python-medusa_1%3a0.5.4-7build1_all.deb
python-support_1.0.14ubuntu2_all.deb
python-meld3_0.6.5-3.1_amd64.deb
supervisor_3.0a8-1.1_all.deb

의 4개 패키지를 복사하여

sudo dpkg -i python-medusa_1%3a0.5.4-7build1_all.deb
sudo dpkg -i python-support_1.0.14ubuntu2_all.deb
sudo dpkg -i python-meld3_0.6.5-3.1_amd64.deb
sudo dpkg -i supervisor_3.0a8-1.1_all.deb
를 순서대로 설치하면 됩니다.

위와 같이 설치되어 있는 상황에서

cat /etc/supervisor/conf.d/restapi.conf 
위의 파일에 다음과 같은 설정 파일을 만들고

[program:restapi1]
command = gunicorn -w6 --certfile=future.crt --keyfile=future.key restapi:app -b 0.0.0.0:8443
directory = /opt/tmus
user = root
numprocs=1
autostart=true
autorestart=true
redirect_stderr=false
stdout_logfile=/opt/tmus/stdout.log
stdout_logfile_maxbytes=1MB
stdout_logfile_backups=5
stdout_capture_maxbytes=1MB
stderr_logfile=/opt/tmus/stderr.log
stderr_logfile_maxbytes=1MB
stderr_logfile_backups=5
stderr_capture_maxbytes=1MB

[program:restapi2]
command = gunicorn -w6 --certfile=future.crt --keyfile=future.key restapi:app -b 0.0.0.0:8444
directory = /opt/tmus
user = root
numprocs=1
autostart=true
autorestart=true
redirect_stderr=false
stdout_logfile=/opt/tmus/stdout.log
stdout_logfile_maxbytes=1MB
stdout_logfile_backups=5
stdout_capture_maxbytes=1MB
stderr_logfile=/opt/tmus/stderr.log
stderr_logfile_maxbytes=1MB
stderr_logfile_backups=5
stderr_capture_maxbytes=1MB

[program:restapi3]
command = gunicorn -w6 --certfile=future.crt --keyfile=future.key restapi:app -b 0.0.0.0:8445
directory = /opt/tmus
user = root
numprocs=1
autostart=true
autorestart=true
redirect_stderr=false
stdout_logfile=/opt/tmus/stdout.log
stdout_logfile_maxbytes=1MB
stdout_logfile_backups=5
stdout_capture_maxbytes=1MB
stderr_logfile=/opt/tmus/stderr.log
stderr_logfile_maxbytes=1MB
stderr_logfile_backups=5
stderr_capture_maxbytes=1MB


sudo service supervisor stop
sudo service supervisor start
(restart 가 제대로 동작하지 않았습니다)

또는 시스템 reboot을 하여
정상으로 동작하는지 확인할 수 있습니다.

8443 말고도 8444, 8445 까지 주었던 이유는
하나의 포트 말고도 다른 포트로도 동일 REST API를 호출할 수 있음을
확인한 것입니다.

진작 알았더라면 일년전에 다르게 접근했을 텐데... 하는 마음이 드네요...


어느 분께는 도움이 되셨기를...


핑백

덧글

  • johan 2015/03/24 22:55 # 삭제 답글

    오 찾던 정보였는데 감사합니다 :)
  • 지훈현서아빠 2015/03/25 09:40 #

    도움이 되셨다니 저의 보람입니다~~ ^^
  • doky 2017/06/07 19:02 # 삭제 답글

    댓글을 누르니 블로그가 바뀌네요 ㅋㅋ
    gunicorn 정보 보러왔다가 개념적인 것들을 더 자세히 알게 되었어요~
    슈퍼바이저라는 데몬도....ㅎ 감사합니다.
  • 지훈현서아빠 2017/06/08 09:25 #

    도움이 되셨다니 저의 보람입니다~ ^^
  • ash84 2017/06/30 10:40 # 삭제 답글

    잘 보고 갑니다 ^^
  • 지훈현서아빠 2017/06/30 12:33 #

    도움이 되셨다니 저의 보람입니다~ ^^
댓글 입력 영역

구글애드텍스트