Celery Task 디버깅

Hyeseong·2022년 5월 16일

개요

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개의 댓글