blog

Django + Celery 안전하게 운용하기위한 Tips

날짜: 2024-11-27

목록으로


Django + Celery 프로젝트에서 Celery Task가 먹통이 되는 상황을 방지하려면, Task Timeout 설정자동 종료 처리를 설정해야 합니다.

아래는 이러한 문제를 예방하기 위한 주요 설정과 방법입니다.

1. Celery Task Timeout 설정

1.1. Hard Time Limit

from celery import shared_task

@shared_task(time_limit=300)  # 5분 (300초)
def my_task():
    # Task Logic
    pass

1.2. Soft Time Limit

from celery import shared_task
from celery.exceptions import SoftTimeLimitExceeded

@shared_task(soft_time_limit=300)  # 5분
def my_task():
    try:
        # Task Logic
        pass
    except SoftTimeLimitExceeded:
        # Graceful 종료
        print("Task exceeded soft time limit and will terminate.")

2. Task 예외 처리

Task 내에서 예상치 못한 상황을 대비해 예외 처리를 구현하세요.

from celery import shared_task

@shared_task
def my_task():
    try:
        # Task Logic
        pass
    except Exception as e:
        # 예외 로깅
        import logging
        logging.error(f"Task failed: {e}")
        raise e  # 필요시 재시도를 위해 예외를 다시 던질 수 있음

3. Task 재시도 및 제한

Task 실패 시 재시도를 제한하거나 특정 조건에서만 재시도하도록 설정합니다.

from celery import shared_task

@shared_task(bind=True, max_retries=3, default_retry_delay=60)  # 1분 대기 후 최대 3회 재시도
def my_task(self):
    try:
        # Task Logic
        pass
    except Exception as e:
        # 예외가 발생하면 재시도
        raise self.retry(exc=e)

4. Worker Side 설정

4.1. Concurrency 제한

celery -A your_project worker --concurrency=4

4.2. Prefetch Limit

CELERY_WORKER_PREFETCH_MULTIPLIER = 1  # 한 번에 하나씩 가져오기

4.3. Task ACK 설정

CELERY_ACKS_LATE = True
CELERY_TASK_REJECT_ON_WORKER_LOST = True

5. Health Check 및 Monitoring

5.1. Flower

5.2. Dead Letter Queue


6. Worker 프로세스 자동 종료

Celery Worker가 메모리 누수 등의 이유로 불안정해질 수 있으므로, 일정 Task를 처리한 후 Worker를 자동으로 재시작하도록 설정합니다.

celery -A your_project worker --max-tasks-per-child=100

7. Retry 및 Deadlock 방지

Task 간 Deadlock을 방지하기 위해 Task 내부에서 Timeout이나 Lock을 구현하세요.

from celery.utils.log import get_task_logger
import redis

logger = get_task_logger(__name__)

@shared_task
def my_task():
    client = redis.StrictRedis()
    lock = client.lock("my_task_lock", timeout=300)  # 5분

    if not lock.acquire(blocking=False):
        logger.warning("Task is already running.")
        return

    try:
        # Task Logic
        pass
    finally:
        lock.release()

추천 설정

  1. Task 단위 Timeout:
    CELERY_TASK_TIME_LIMIT = 300
    CELERY_TASK_SOFT_TIME_LIMIT = 270
    
  2. Worker 안정성:
    CELERY_ACKS_LATE = True
    CELERY_TASK_REJECT_ON_WORKER_LOST = True
    CELERY_WORKER_PREFETCH_MULTIPLIER = 1
    
  3. Worker 자동 재시작:
    celery -A your_project worker --max-tasks-per-child=100
    

목록으로