Python APScheduler vs Celery Beat 작업 스케줄링
Olivia Novak
Dev Intern · Leapcell

소개
애플리케이션 개발 세계에서 작업은 종종 한 번으로 끝나는 것이 아닙니다. 특정 시간에, 정기적으로 또는 이벤트에 대한 응답으로 실행되어야 하는 특정 작업이 발생하는 시나리오에 자주 직면하게 됩니다. 일일 뉴스레터 발송, 주간 보고서 생성, 오래된 데이터 정리 또는 매시간 정보 동기화를 생각해 보세요. 이러한 작업을 수동으로 트리거하는 것은 비효율적이고 오류가 발생하기 쉽습니다. 바로 여기서 작업 스케줄러가 등장하여 이러한 필수 프로세스를 자동화하고 애플리케이션이 원활하고 효율적으로 실행되도록 보장합니다. Python은 풍부한 생태계로 이러한 목적을 위한 강력한 도구를 제공합니다. 가장 인기 있고 다재다능한 도구 중에는 APScheduler와 Celery Beat가 있습니다. 이 글에서는 이 두 가지 솔루션을 자세히 살펴보고, 해당 강점, 사용 사례 및 Python 프로젝트에서 효과적으로 구현하는 방법을 살펴봅니다.
작업 스케줄링의 핵심 개념
APScheduler와 Celery Beat의 특정 사항을 자세히 알아보기 전에 작업 스케줄링과 관련된 몇 가지 핵심 개념에 대한 공통적인 이해를 확립해 보겠습니다.
- 작업(Task): 실행되어야 하는 작업의 개별 단위.
- 스케줄러(Scheduler): 미리 정의된 규칙에 따라 작업을 시작하는 것을 책임지는 구성 요소.
- 작업(Job): 실행을 위해 예약된 작업의 인스턴스.
- 작업 저장소(Job Store): 작업 정의가 저장되는 곳(예: 메모리 내, 데이터베이스, Redis).
- 트리거(Trigger): 작업이 언제 실행되어야 하는지를 정의합니다. 일반적인 트리거는 다음과 같습니다.
- 날짜 트리거(Date Trigger): 특정 날짜와 시간에 작업을 한 번 실행합니다.
- 간격 트리거(Interval Trigger): 고정된 시간 간격으로 작업을 반복적으로 실행합니다.
- Cron 트리거(Cron Trigger): Unix와 유사한 cron 표현식(예: '매주 월요일 오전 9시')을 기반으로 작업을 실행합니다.
- 워커(Worker): 실제 작업 로직을 실행하는 프로세스 또는 스레드입니다. 분산 시스템에서 워커는 스케줄러와 구별될 수 있습니다.
- 브로커(Broker, 메시지 큐): 분산 스케줄링에서 메시지 큐(Redis 또는 RabbitMQ와 같은)는 스케줄러가 비동기적으로 워커에게 작업을 보낼 수 있도록 하는 중개자 역할을 합니다.
APScheduler: Python의 인프로세스 스케줄링
APScheduler(Advanced Python Scheduler)는 Python 함수를 나중에 실행하도록 예약할 수 있는 Python 라이브러리이며, 한 번이든 주기적이든 상관없습니다. 분산 작업 큐의 복잡성이 필요하지 않은 경우 특히 더 간단한 인프로세스 스케줄링 요구에 탁월한 선택입니다.
APScheduler 작동 방식
APScheduler는 애플리케이션 프로세스의 일부로 작동합니다. '작업 저장소'에 예약된 작업 목록을 유지하고 '스케줄러'를 사용하여 이러한 작업을 모니터링합니다. 작업의 트리거 조건이 충족되면 스케줄러는 동일한 프로세스 내 또는 관리하는 별도의 스레드/프로세스 풀에서 관련 Python 함수를 직접 실행합니다.
주요 기능 및 시나리오
- 유연한 트리거 시스템: 날짜, 간격 및 cron 트리거를 지원합니다.
- 다중 작업 저장소: 메모리 내, 데이터베이스(SQLAlchemy), Redis, MongoDB 또는 ZooKeeper에 작업을 저장하여 애플리케이션 재시작 후에도 지속성을 유지할 수 있습니다.
- 실행기(Executors): 작업을 스레드 풀(기본값) 또는 프로세스 풀, 심지어 비동기적으로 실행할 수 있습니다.
- 사용 편의성: 동적으로 작업을 추가, 수정 및 제거하기 위한 간단한 API.
APScheduler는 다음과 같은 경우에 이상적입니다.
- 로컬 인프로세스 백그라운드 작업이 필요한 중소 규모 애플리케이션.
- 전체 기능을 갖춘 분산 작업 큐가 과도할 때.
- 런타임에 예약된 작업을 동적으로 추가하거나 제거할 때.
- CPU 집약적이지 않고 애플리케이션 프로세스 또는 몇 개의 전용 스레드/프로세스 내에서 동시에 실행될 수 있는 작업을 가진 애플리케이션.
예제: APScheduler 사용
APScheduler를 사용하여 간단한 작업을 예약하는 방법을 살펴보겠습니다.
from datetime import datetime from apscheduler.schedulers.background import BackgroundScheduler import time def my_scheduled_task(message): """메시지와 현재 시간을 출력하는 간단한 작업.""" print(f"Task executed at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Message: {message}") if __name__ == "__main__": # 스케줄러 초기화 scheduler = BackgroundScheduler() # --- 다양한 유형의 작업 추가 --- # 1. 날짜 트리거: 특정 미래 시간에 한 번 실행 run_time = datetime.now().replace(second=0, microsecond=0) + timedelta(minutes=1) scheduler.add_job(my_scheduled_task, 'date', run_date=run_time, args=['One-time job']) # 2. 간격 트리거: 5초마다 실행 scheduler.add_job(my_scheduled_task, 'interval', seconds=5, args=['Interval job']) # 3. Cron 트리거: 매일 오전 10시 30분에 실행 # (데모를 위해 테스트를 위해 더 자주, 예를 들어 매분 실행하겠습니다) scheduler.add_job(my_scheduled_task, 'cron', minute='*/1', args=['Cron job (every minute)']) # 스케줄러 시작 scheduler.start() print("Scheduler started. Press Ctrl+C to exit.") try: # 스케줄러가 실행되도록 메인 스레드를 유지합니다. while True: time.sleep(2) except (KeyboardInterrupt, SystemExit): scheduler.shutdown() print("Scheduler shut down.")
이 예제에서는 별도의 스레드에서 실행되는 BackgroundScheduler를 사용합니다. date, interval 및 cron 트리거를 사용하여 작업을 추가하는 방법을 보여줍니다. args 매개변수를 사용하면 예약된 함수에 인수를 전달할 수 있습니다.
Celery Beat: Python의 분산 스케줄링
Celery는 Python을 위한 강력한 분산 작업 큐입니다. 비동기적으로 백그라운드 작업을 실행하기 위한 인프라를 제공합니다. Celery Beat는 Celery의 스케줄러 구성 요소로, 주기적인 작업을 실행하도록 설계되었습니다. APScheduler와 달리 Celery는 분산 환경을 위해 구축되었으며 여러 컴퓨터에서 확장성과 강력한 작업 관리를 가능하게 합니다.
Celery Beat 작동 방식
Celery는 세 가지 주요 구성 요소로 구성됩니다.
- 생산자(Producer, 클라이언트): 작업을 시작하는 애플리케이션 코드.
- 브로커(Broker, 메시지 큐): 워커가 무료해질 때까지 작업을 보관하는 메시지 전송 시스템(예: RabbitMQ, Redis, Amazon SQS).
- 워커(Worker): 브로커에서 작업을 지속적으로 가져와 실행하는 프로세스.
Celery Beat는 이 아키텍처와 함께 작동하는 별도의 프로세스입니다. Celery Beat는 주기적인 작업 정의(일반적으로 애플리케이션 설정에서)를 읽고, 작업이 실행될 시간이 되면 해당 작업을 Celery 브로커로 보냅니다. 거기서 Celery 워커가 작업을 가져와 실행합니다.
주요 기능 및 시나리오
- 분산 아키텍처: 작업을 다른 컴퓨터에서 실행할 수 있어 확장성과 내결함성을 제공합니다.
- 신뢰성: 작업이 메시지 큐에 추가되므로 워커가 충돌해도 작업이 유지됩니다. 재시도 및 오류 처리가 내장되어 있습니다.
- 풍부한 기능 세트: 작업 라우팅, 속도 제한, 작업 체인, 하위 작업, 워커 풀, 모니터링.
- 포괄적인 트리거링: Celery Beat는 주로 cron과 유사한 표현식과 간격 기반 스케줄링을 사용합니다.
Celery Beat는 다음과 같은 경우에 이상적입니다.
- 분산 백그라운드 작업 처리가 필요한 대규모 애플리케이션.
- 작업이 다른 서비스에서 처리될 수 있는 마이크로서비스 아키텍처.
- 높은 가용성과 내결함성이 중요할 때.
- 주 애플리케이션 스레드를 차단해서는 안 되는 장시간 실행 작업.
- 복잡한 작업 워크플로우 및 상호 의존성이 있는 애플리케이션.
예제: Celery Beat 사용
Celery Beat를 사용하려면 먼저 Celery를 설정해야 합니다.
1. Celery 및 브로커 설치:
pip install celery redis
2. celery_app.py 파일 생성:
# celery_app.py from celery import Celery from datetime import timedelta # Celery 초기화 # 'redis://localhost:6379/0'를 브로커 URL로 바꾸세요 app = Celery('my_app', broker='redis://localhost:6379/0', backend='redis://localhost:6379/1') # 선택 사항: beat를 위한 시간대 설정 app.conf.timezone = 'UTC' # 'beat_schedule'을 사용하여 주기적인 작업 정의 app.conf.beat_schedule = { 'add-every-10-seconds': { 'task': 'celery_app.my_periodic_task', 'schedule': timedelta(seconds=10), 'args': ('Periodic job (every 10 seconds)',) }, 'run-every-monday-morning': { 'task': 'celery_app.my_periodic_task', 'schedule': app.crontab(minute=0, hour=10, day_of_week=1), # 매주 월요일 오전 10시 'args': ('Weekly job (Monday 10 AM)',) }, } # 작업 정의 @app.task def my_periodic_task(message): """메시지와 현재 시간을 출력하는 간단한 작업.""" from datetime import datetime print(f"Celery Task executed at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - Message: {message}")
3. Celery Beat 및 Celery 워커 실행:
두 개의 별도 터미널 창을 엽니다.
- 터미널 1 (Celery Beat):
celery -A celery_app beat -l info - 터미널 2 (Celery Worker):
celery -A celery_app worker -l info
작업이 수신되고 실행됨을 나타내는 출력이 워커 터미널에 표시됩니다. Celery Beat는 작업을 브로커로 보낼 때 기록합니다.
이 Celery 예제에서는 beat_schedule이 Celery 앱 구성에 주기적인 작업을 직접 정의합니다. Celery Beat가 시작되면 이 스케줄을 읽고 적절한 시간에 브로커로 작업을 디스패치합니다. 그런 다음 해당 작업은 Celery 워커에 의해 가져와 실행됩니다.
결론
APScheduler와 Celery Beat 간의 선택은 주로 프로젝트의 규모, 복잡성 및 특정 요구 사항에 따라 달라집니다. APScheduler는 인프로세스, 비분산 스케줄링에 대한 단순성과 효율성을 뛰어나게 제공하며, 소규모 애플리케이션이나 외부 종속성 없이 동적 작업 관리가 필요한 경우에 적합합니다. 반대로, Celery Beat는 Celery 생태계의 일부로, 강력하고 확장 가능하며 분산된 주기적 작업 관리를 위한 솔루션이며, 높은 가용성과 내결함성이 필요한 대규모 애플리케이션에 이상적입니다. 두 도구 모두 해당 도메인에서 강력하며 Python 개발자에게 작업 실행을 자동화하고 간소화하는 효과적인 수단을 제공합니다.