Django 를 개발할 때, background job(Async Task) 을 처리하면서 처음으로 Rabbitmq + Celery 를 처음 접했다. 웹에서 긴 동작 처리를 하거나 주기적인 반복동작, 정해진 시간에 일을 처리하기 위해서 여러 방법이 있다.
웹에서 긴작업이 문제가 되는 경우는 사용자가 인내력 없이 다른 페이지도 이동하는 경우도 있겠지만 인내력이 있다고 해도 Nginx 나 Apache 그리고 uwsgi 같은 시스템들이 Flask 가 Djaong 의 특정 request 의 동작이 길면 뭔가 잘못되었다고 생각해서 client 에 timeout 를 발생시키고 해당 프로세스를 죽이기 때문에 문제가 발생할 수 있다.
정해진 시간에 처리하기 위한 가장 쉬운 방법은 Cron(ko.wikipedia.org/wiki/Cron) 을 이용하는 것이다. 일반적으로 정해진 시간에 특정동작은 DB 접근도 필요한 경우가 많아 Flask 에서 이용한 DB 모델(일반적으로 sqlalchemy 로 정의된 모델)구조를 이용할 수 있어야 한다. 이 때 Flask Cli Command 를 이용해서 마치 하나의 console 프로그램 처럼 실행할 수 있다. 그리고 일반적으로 virtualenv 환경에서 동작해야 하고, 여러 환경변수로 전달해야 하고, 로그도 남겨야 한다. 그러다 보면 아래 형태가 된다.
bash -c 'FLASK_APP=/usr/share/nginx/flask/main.py FLASK_CONFIG=testing SERVER_NAME="test.labstoo.com" /opt/py_envs/web/bin/python -m flask dailyjob >> /var/log/crontab/dailyjob.log 2>&1 '
Background job 을 하기 위해서는 일반적으로 Dajngo 쪽에서는 Rabbitmq + Celery , Redis + Celery 를 선택하는 것 같다. 이 job 을 동작하기 위해서는 중요한 것이 Background Job 만 처리하는 woker 라는 프로그램을 따로 만들어야 하는 것이다. 일반적으로 이 프로그램도 Django 구조를 사용하게 할 수는 있다.
Flask 계열에서는 일반적으로 RQ(python-rq.org/) 계열의 종류를 많이 사용하는 것 같다. 이 것도 Redis + Worker 가 필요하다.
Worker 구조를 갖는 경우 모두 한 서버에서 구현한다고 하면 Nginx(또는 Apache) + uwsgi(또는 gunicorn ) + Postgresql (또는 Mysql, MongoDB 같은 DB) + Redis (또는 Rabbitmq ) + Worker(Celery 를 이용하거나 RQ를 이용하거나) 의 선택을 하게 된다. 이 경우 Redis 의 경우 Cache 서비스 때문에 이용한다고 치더라도 Worker 까지 사용하는 건은 웬지 배보다 배꼽이 큰 것 같이 느껴진다.
물론 넉넉한 회사의 경우 이런 서비스 들에 대해서 모두 이중화를 하겠지만 스타트업에 다니면서 단 하나의 서버만을 쓰는 입장에서는 뭔가 리소스 낭비로 생각되기도 하다. (물론 이런 경우 서비스가 죽는다면 다시 서버를 켜면 된다. 결제 시스템 같은 것이 붙은 것도 아니기 이런 선택을 할 수 있다.)
이런 때 사용할 수 있는 시스템이 apscheduler( github.com/agronholm/apscheduler flask 의 경우 github.com/viniciuschiele/flask-apscheduler, django 의 경우 github.com/jcass77/django-apscheduler ) 이다. 따로 Thread 를 만들어 동작하는 방식이다. 이렇기 때문에 Django 나 Flask 가 죽으면 동작하지 않는다. 그리고 프로세스마다 Thread 가 생성되기 때문에 잘 처리해줘야 한다. (uwsgi 의 경우 lazy 옵션을 사용하는 경우가 특히 이렇다. 내 경우 가 이런 형태라 사용을 포기했다. ) 그리고 기본적으로 이중화 처리가 좀 어렵다. 그래서 사용하더라도 하나의 서버만 사용하는 경우에 적합하다.
그리고 오늘 발견한 시스템이 있는데, uwsgi 를 이용하는 경우 uwsgi 자체의 background-job 을 지원하는 경우를 사용해서 처리하는 방법이 있다. 이를 조금 편하게 지원하기 위해 uwsgi_tasks (github.com/Bahus/uwsgi_tasks) 라는 라이브러리를 사용할 수 있다. 결과적으로 uwsgi 가 책임지고 task 를 돌려주는 방식이다. 그리고 서버가 죽었다가 다시 실행하더라도 자동으로 다시 task 를 동작한다. 아직 회사에서 테스트 줄인데, uwsgi 설정파일(ini 파일) 에 설정한 환경변수도 같이 전달되는지는 확인 하지 못했다. 그거 까지 된다면 단일 웹서버 시스템의 경우 Celery 의 대안이 될 수 있을 것 같다.