Celery tasks를 디버깅 하는 몇가지 방법을 살펴 보겠습니다.
task_always_eager
설정을 True
로 하게 되면 큐로 보내지않고(비동기) 즉시(동기방식) task가 실행됩니다. 이러면 평소 동기적으로 코드를 디버깅하는 것처럼 편리하게 결과를 바로 피드백 받을 수 있기에 편리합니다. 이 모드는 테스트 시 사용해도 괜찮습니다.
그래서 CELERY_TASK_ALWAYS_EAGER: bool = True
를 FastAPI 컨피그에 추가하여 활성화 합니다.
이 방법을 통해서 빨리 개발에 대한 결과와 피드백을 받을 수 있는 장점이 있습니다.
코드를 디버그를 위해 woker, message broker 또는 result backend 프로세스를 실행할 필요가 없습니다. 즉, Uvicorn 서버 프로세스 내에서 직접 코드를 디버그할 수 있습니다. 디버깅과 테스트를 크게 단순화할 수 있습니다.
해당 모드의 경우 task.delay()
를 반환값이 AsyncResult
가 아닌 EagerResult
가 되는데요. 이 경우 비동기적 코드 복잡성이 커질 경우 오히려 예측한 결과대로 나오지 않을 우려가 있습니다.
rdb는 터미널에서 직접 Celery 작업을 디버그할 수 있는 강력한 도구입니다.
이 작업을 수행하려면 Telnet이 설치되어 있어야 합니다.
IDE 없이 효율적인 방법으로 Celery 작업을 디버그할 수 있습니다.
초보자에게는 어려울 수 있습니다.
task_always_eager
의 값이 False
가 되었는지 확인하세요.
telnet 설치를 위해서 Dockerfile 수정을 합니다.
...
RUN apt-get update \
# dependencies for building Python packages
&& apt-get install -y build-essential \
# psycopg2 dependencies
&& apt-get install -y libpq-dev \
# Additional dependencies
&& apt-get install -y telnet netcat \
# cleaning up unused files
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/*
...
project/users/tasks.py파일에 rdb를 Celery task코드에 추가 하도록 하겠습니다.
@shared_task
def divide(x, y):
from celery.contrib import rdb
rdb.set_trace()
import time
time.sleep(5)
return x / y
rdb.set_trace()
함수는 breakpoint와 같이 작동 하게 됩니다.
$ docker-compose up -d --build
$ docker-compose logs -f
새 터미널 창에서 프로젝트 디렉토리로 이동한 다음 Python 쉘을 통해서 Celery Worker에 task를 보냅니다.
$ docker-compose exec web bash
(container)$ python
>>> from main import app
>>> from project.users.tasks import divide
>>> divide.delay(1, 2)
로그가 실행중인 창에서 아래와 같은 유사한 내용이 표시 되어야 합니다.
celery_worker_1 | [2022-05-16 01:26:53,299: INFO/MainProcess] Received task: project.users.tasks.divide[3e04f265-b9c6-465d-8eb4-e8d21b42cbc7]
celery_worker_1 | [2022-05-16 01:26:53,329: WARNING/ForkPoolWorker-4] Remote Debugger:6903: Ready to connect: telnet 127.0.0.1 6903
celery_worker_1 |
celery_worker_1 | Type `exit` in session to continue.
celery_worker_1 |
celery_worker_1 | Remote Debugger:6903: Waiting for client...
Remote Debugger:6903: Waiting for client..
포트를 별도로 기록해 둡니다.
다른 터미널 창을 열고 프로젝트 디렉토리로 이동한 다음 telnet을 이용하여 debuger에 연결합니다.
$ docker-compose exec celery_woker bash
(container)$ telnet 127.0.0.1 6903
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
> /app/project/users/tasks.py(9)divide()
-> import time
(Pdb)
디버깅을 아래와 같이 시작 진행 할 수 있습니다.
(Pdb) args
x = 1
y = 2
(Pdb) help
Documented commands (type help <topic>):
========================================
EOF cl display j next run unalias where
a clear down jump p rv undisplay
alias commands enable l pp s unt
args condition h list r source until
b d help ll restart step up
break debug ignore longlist return tbreak w
bt disable interact n retval u whatis
Miscellaneous help topics:
==========================
exec pdb
Undocumented commands:
======================
c cont continue exit q quit
c(continue)를 입력하여 디버그 shell을 종료 할 수 있습니다.
Celery worker가 task를 마무리하여 종료하게 됩니다.
celery_worker_1 | [2022-05-16 01:27:24,337: WARNING/ForkPoolWorker-4] Remote Debugger:6903: Now in session with 127.0.0.1:55468.
celery_worker_1 | [2022-05-16 01:27:46,717: WARNING/ForkPoolWorker-4] Remote Debugger:6903: Session with 127.0.0.1:55468 ended.
celery_worker_1 | [2022-05-16 01:27:51,735: INFO/ForkPoolWorker-4] Task project.users.tasks.divide[3e04f265-b9c6-465d-8eb4-e8d21b42cbc7] succeeded in 58.501944299961906s: 0.5
완료되면 task에 rdb를 제거하거나 주석처리를 해두도록 합니다.
from celery import shared_task
@shared_task
def divide(x, y):
# from celery.contrib import rdb
# rdb.set_trace()
import time
time.sleep(5)
return x / y