수요일

Celery Worker 에러 해결 — 태스크가 처리되지 않을 때 완벽 가이드

🔍 검색 키워드: celery worker 에러 해결, celery worker not processing tasks, kombu connection refused, celery django redis 에러, celery worker 작업 안됨, celery OperationalError 해결

증상: 태스크를 보냈는데 Worker가 반응이 없다

Celery 써본 사람이라면 한 번씩 겪어봤을 상황이다. .delay() 또는 .apply_async()로 태스크를 넣었는데 처리가 안 된다. 워커 로그를 보면 아예 조용하거나, 아래 같은 에러가 떠 있다.

kombu.exceptions.OperationalError: [Errno 111] Connection refused

또는 워커 프로세스는 살아 있는데 태스크 큐에 메시지가 쌓이기만 하고 소비가 안 된다. Celery + Django + Redis 조합에서 특히 자주 터진다.

원인 분류

Celery 워커가 태스크를 처리하지 못하는 원인은 크게 세 가지다.

1. 브로커(Redis/RabbitMQ)에 연결이 안 됨

가장 흔한 원인. Redis가 아예 안 떠 있거나, 포트나 URL이 틀렸다.

# Redis 실행 여부 확인 redis-cli ping # PONG 이 나와야 정상 # 포트 확인 netstat -an | grep 6379

Django settings에서 CELERY_BROKER_URL 확인:

# settings.py CELERY_BROKER_URL = 'redis://localhost:6379/0' # 로컬 # Docker 환경이면 CELERY_BROKER_URL = 'redis://redis:6379/0' # 서비스명 주의

Docker Compose 쓸 때 실수가 잦다. 컨테이너 안에서 localhost는 자기 자신을 가리키기 때문에, Redis 컨테이너 이름(서비스명)을 써야 한다.

2. Celery 5.6.x 버전 버그 — Redis Reconnection 후 멈춤

이건 좀 억울한 케이스다. Celery 5.5.0에서 Kombu reconnection 버그를 고쳤는데, 5.6.x에서 같은 문제가 다시 들어왔다. 증상은 이렇다.

  • 워커가 처음엔 잘 돌다가 Redis 재연결(failover, 재시작) 이후 멈춤
  • 태스크가 큐에 들어오는 건 보이는데 워커가 pick-up 안 함
  • 워커 프로세스는 살아 있음 (CPU 거의 0%)
# 현재 celery 버전 확인 pip show celery | grep Version pip show kombu | grep Version

5.6.x라면 다운그레이드하거나 패치 버전을 기다려야 한다.

pip install "celery==5.5.0" "kombu==5.4.0"

3. Worker Concurrency 부족 — 큐는 차는데 처리 속도가 안 따라감

에러는 없는데 태스크가 밀리는 경우다. 기본 concurrency는 CPU 코어 수인데, I/O bound 작업이 많으면 이걸 올려야 한다.

# 현재 워커 상태 확인 celery -A myproject inspect active celery -A myproject inspect reserved celery -A myproject inspect stats
# concurrency 늘려서 실행 celery -A myproject worker --concurrency=16 --loglevel=info # 여러 워커 프로세스 (celery multi) celery multi start 4 -A myproject -Q default,priority --concurrency=8

디버깅 순서: 이 순서대로 확인해라

Step 1. 브로커 연결 직접 확인

# Django shell에서 from celery_app import app # 또는 본인 celery app app.connection().ensure_connection(max_retries=3)

에러 없이 통과하면 브로커 연결은 OK다.

Step 2. 간단한 태스크로 테스트

# tasks.py from celery import shared_task @shared_task def debug_task(): print("task executed!") return "ok" # Django shell에서 from myapp.tasks import debug_task result = debug_task.delay() print(result.get(timeout=10)) # "ok" 나오면 정상

Step 3. 워커 로그 레벨 올려서 확인

celery -A myproject worker --loglevel=DEBUG

[DEBUG/MainProcess] Received task: 라인이 안 뜨면 태스크가 워커까지 도달 자체를 못 하는 것. Received task:는 뜨는데 처리가 안 되면 concurrency 문제이거나 태스크 내부에서 죽는 것.

Step 4. Flower로 실시간 모니터링

pip install flower celery -A myproject flower --port=5555

http://localhost:5555에서 워커 상태, 태스크 큐, 처리 속도를 한눈에 볼 수 있다.

상황별 체크리스트

증상체크 항목해결 방법
kombu connection refusedRedis 실행 여부redis-cli ping → Redis 시작
Docker에서 Redis 연결 실패BROKER_URL 호스트명localhost → 서비스명 변경
워커 재시작 후 멈춤Celery 버전5.5.0으로 다운그레이드
큐 적체, 에러 없음concurrency 설정--concurrency 값 증가
태스크 timeout작업 시간 초과soft_time_limit, time_limit 설정
특정 큐만 처리 안 됨워커 큐 설정-Q 파라미터로 큐 명시

실무 설정 예시

Django settings.py — 권장 Celery 설정

# Celery 설정 CELERY_BROKER_URL = os.environ.get('CELERY_BROKER_URL', 'redis://localhost:6379/0') CELERY_RESULT_BACKEND = os.environ.get('CELERY_RESULT_BACKEND', 'redis://localhost:6379/1') # 작업 직렬화 CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' CELERY_ACCEPT_CONTENT = ['json'] # 타임존 CELERY_TIMEZONE = 'Asia/Seoul' CELERY_ENABLE_UTC = True # 재시도 설정 CELERY_TASK_ACKS_LATE = True # 처리 완료 후 ack CELERY_WORKER_PREFETCH_MULTIPLIER = 1 # 한 번에 하나씩 # 태스크 타임아웃 CELERY_TASK_SOFT_TIME_LIMIT = 300 # 5분 경고 CELERY_TASK_TIME_LIMIT = 360 # 6분 강제 종료

Docker Compose 설정 예시

version: '3.8' services: redis: image: redis:7-alpine ports: - "6379:6379" healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 5 web: build: . depends_on: redis: condition: service_healthy environment: - CELERY_BROKER_URL=redis://redis:6379/0 celery_worker: build: . command: celery -A myproject worker --concurrency=4 --loglevel=info depends_on: redis: condition: service_healthy environment: - CELERY_BROKER_URL=redis://redis:6379/0

Docker Compose에서 depends_on: condition: service_healthy를 안 쓰면, Redis가 완전히 뜨기 전에 Celery worker가 연결 시도해서 connection refused가 뜬다.

Celery Beat (스케줄러) 별도로 떠야 한다

가끔 보면 periodic task가 실행이 안 된다는 이슈가 있는데, celery beat를 별도 프로세스로 안 띄워서 그런 경우가 있다.

# beat는 반드시 별도로 실행 celery -A myproject beat --loglevel=info --scheduler django_celery_beat.schedulers:DatabaseScheduler

beat와 worker를 같은 프로세스로 돌리는(-B 옵션) 건 개발환경에서만 써라. 프로덕션에서는 반드시 분리해야 한다.

마무리

Celery 문제는 대부분 브로커 연결 문제 아니면 버전 버그다. 위 순서대로 하나씩 확인하면 대부분 잡힌다. Docker 환경에서는 서비스명 vs localhost 혼동이 제일 많으니 그것부터 봐라.

실무에서 직접 겪은 내용 기반으로 작성했습니다. 틀린 부분 있으면 댓글로 알려주세요.

댓글 없음:

댓글 쓰기