Celery Task 디버깅

Hyeseong·2022년 5월 16일
0

개요

Celery tasks를 디버깅 하는 몇가지 방법을 살펴 보겠습니다.

목적

  1. 셀리리 테스크를 디버깅 하는 방법
  2. rdb를 사용하여 Celery 작업 디버깅

Eager Mode

task_always_eager 설정을 True로 하게 되면 큐로 보내지않고(비동기) 즉시(동기방식) task가 실행됩니다. 이러면 평소 동기적으로 코드를 디버깅하는 것처럼 편리하게 결과를 바로 피드백 받을 수 있기에 편리합니다. 이 모드는 테스트 시 사용해도 괜찮습니다.

그래서 CELERY_TASK_ALWAYS_EAGER: bool = True를 FastAPI 컨피그에 추가하여 활성화 합니다.

이 방법을 통해서 빨리 개발에 대한 결과와 피드백을 받을 수 있는 장점이 있습니다.

장점

코드를 디버그를 위해 woker, message broker 또는 result backend 프로세스를 실행할 필요가 없습니다. 즉, Uvicorn 서버 프로세스 내에서 직접 코드를 디버그할 수 있습니다. 디버깅과 테스트를 크게 단순화할 수 있습니다.

단점

해당 모드의 경우 task.delay()를 반환값이 AsyncResult가 아닌 EagerResult가 되는데요. 이 경우 비동기적 코드 복잡성이 커질 경우 오히려 예측한 결과대로 나오지 않을 우려가 있습니다.

rdb

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
profile
어제보다 오늘 그리고 오늘 보다 내일...

0개의 댓글