본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] Celery 워커 메모리 누수 방지 해결 방법 3가지와 설정 값 차이 분석

by Papa Martino V 2026. 4. 3.
728x90

Celery 워커 메모리 누수 방지
Celery 워커 메모리 누수 (Memory Leak) 방지

 

파이썬 기반의 비동기 작업 큐 시스템인 Celery는 대규모 서비스의 백엔드에서 필수적인 역할을 수행합니다. 하지만 많은 개발자가 운영 환경에서 겪는 가장 골치 아픈 문제 중 하나는 바로 워커(Worker) 프로세스의 메모리 점유율이 끝없이 상승하는 메모리 누수(Memory Leak) 현상입니다. 파이썬의 가비지 컬렉션(GC) 메커니즘과 외부 라이브러리의 C 확장 모듈 특성이 결합되어 발생하는 이 문제는 단순한 코드 수정만으로는 해결하기 어렵습니다. 오늘은 시스템 안정성을 확보하기 위한 Celery 워커 최적화 설정 방법을 심층적으로 다루어 보겠습니다.


1. Celery 워커에서 메모리 누수가 발생하는 근본 원인

파이썬은 기본적으로 참조 횟수 계산(Reference Counting) 방식을 사용하지만, Celery 워커처럼 장시간 실행되는 프로세스에서는 다음과 같은 이유로 메모리 팽창이 일어납니다.

  • C 확장 모듈의 잔류 메모리: Pandas, NumPy, 텐서플로우 등 C로 작성된 라이브러리는 파이썬 GC가 관리하지 못하는 영역에서 메모리를 점유한 뒤 해제하지 않는 경우가 많습니다.
  • 프래그멘테이션(Fragmentation): 메모리를 할당하고 해제하는 과정에서 빈 공간이 조각나, 실제 사용량보다 더 많은 시스템 메모리를 OS로부터 요청하게 됩니다.
  • 전역 변수 및 캐시 남용: 워커 프로세스가 살아있는 동안 유지되는 전역 객체나 캐시가 작업이 반복될수록 비대해집니다.

2. 메모리 누수 해결을 위한 3가지 핵심 설정 방법

가장 효과적인 해결책은 워커 프로세스가 일정량의 작업을 수행한 뒤 스스로 죽고 다시 살아나게 만드는 '재생생(Recycling)' 전략입니다.

해결 1: worker_max_tasks_per_child

하나의 자식 프로세스(Child Process)가 처리할 수 있는 최대 작업 수를 제한합니다. 설정된 수치에 도달하면 워커는 종료되고 새로운 깨끗한 프로세스가 생성됩니다.

해결 2: worker_max_memory_per_child

프로세스가 사용할 수 있는 최대 메모리 임계치를 설정합니다. 작업 수와 관계없이 메모리가 특정 수준(예: 1GB)을 넘어서면 프로세스를 교체하여 메모리 폭발을 방지합니다.

해결 3: Prefetch Multiplier 최적화

메모리 점유율을 낮추기 위해 워커가 한꺼번에 가져오는 메시지 수를 조절합니다. 메모리 집약적인 작업일수록 이 값을 1로 낮추는 것이 유리합니다.


3. 주요 설정값 비교 및 권장 가이드

운영 환경의 리소스 상황에 따라 적용해야 할 설정값의 차이를 아래 표로 정리하였습니다.

설정 항목 기본값 (Default) 권장 최적화 값 기대 효과
worker_max_tasks_per_child None (무제한) 100 ~ 1000 주기적 프로세스 교체로 누수 차단
worker_max_memory_per_child None (무제한) 200000 ~ 500000 (KB) 예상치 못한 메모리 폭주 방지
worker_prefetch_multiplier 4 1 (무거운 작업 시) 불필요한 메시지 대기 및 메모리 점유 감소
task_acks_late False True 워커 급작 종료 시 작업 손실 방지

4. [Sample Example] 실전 최적화 Celery Config

파이썬 프로젝트의 celery.py 또는 설정 파일에 바로 적용할 수 있는 해결 코드 예시입니다. 이 설정은 2026년 현재 대규모 데이터 처리 환경에서 검증된 방법입니다.


from celery import Celery

app = Celery('my_project')

# [해결책] 메모리 누수 방지를 위한 핵심 설정
app.conf.update(
    # 1. 프로세스당 처리 작업 제한 (500개 처리 후 프로세스 재시작)
    worker_max_tasks_per_child=500,
    
    # 2. 프로세스당 최대 메모리 제한 (400MB 초과 시 재시작)
    # 단위: KB (kilobytes)
    worker_max_memory_per_child=400000,
    
    # 3. 프리페치 조절 (메모리 효율을 위해 1개씩 가져옴)
    worker_prefetch_multiplier=1,
    
    # 4. 작업 완료 후 ACK 전송 (안정성 확보)
    task_acks_late=True,
    
    # 5. 불필요한 로그 데이터 축적 방지
    worker_disable_rate_limits=True
)

5. 전문적인 추가 진단: 가비지 컬렉션 수동 제어

설정 변경만으로 부족하다면, 파이썬의 gc 모듈을 작업 완료 시점에 직접 호출하는 방법도 있습니다. 특히 대용량 리스트나 객체를 다룬 직후에 gc.collect()를 명시적으로 실행하면 OS로의 메모리 반환 속도를 높이는 데 도움이 됩니다. 하지만 이는 오버헤드가 발생하므로 worker_max_tasks_per_child 설정을 먼저 우선시하는 것이 정석적인 해결 방법입니다.


6. 결론: 안정적인 배포를 위한 제언

Celery 워커의 메모리 관리는 '누수를 완벽히 없애는 것'보다 '누수가 시스템 전체에 영향을 미치기 전에 프로세스를 신선하게 유지하는 것'에 초점을 맞춰야 합니다. 위에서 제시한 3가지 설정 방법을 통해 인프라 비용 절감과 서비스 가용성 향상이라는 두 마리 토끼를 잡으시길 바랍니다.


내용 출처 및 참고 문헌

  • Celery Official Documentation: Worker Optimization Guide (v5.x+)
  • Python Garbage Collection Design: Understanding Reference Counting and Cyclical GC
  • Real Python: Managing Memory in Long-running Python Processes
728x90