분산 시스템에서의 작업 스케줄링

3 min read

기업의 데이터 분석 시스템을 운영한다고 가정해보자. 매일 자정에 전날의 판매 데이터를 집계하고, 매시간 재고 수준을 확인하며, 매주 월요일 오전 9시에 주간 보고서를 생성해야 한다. 또한 특정 이벤트가 발생하면 즉시 알림을 발송하고, 시스템 부하가 낮은 시간대에 데이터베이스 백업을 수행해야 한다. 이처럼 다양한 시간 조건과 이벤트에 따라 작업을 자동으로 실행하는 것이 바로 작업 스케줄링의 영역이다. 특히 여러 서버로 구성된 분산 시스템에서는 이러한 스케줄링이 훨씬 복잡한 과제가 된다.

작업 스케줄링은 크게 '시간 기반 스케줄링'과 '이벤트 기반 스케줄링'으로 구분된다. 시간 기반 스케줄링은 Unix의 cron과 같이 특정 시각이나 주기에 따라 작업을 실행하는 방식이다. "매일 오전 2시 30분", "매시간 15분마다", "매월 첫째 주 월요일" 등의 복잡한 시간 패턴을 표현할 수 있다. 반면 이벤트 기반 스케줄링은 "파일이 업로드되었을 때", "재고가 임계치 이하로 떨어졌을 때", "사용자가 특정 액션을 수행했을 때" 등 특정 조건이 충족되면 작업을 실행한다.

단일 서버 환경에서의 스케줄링은 상대적으로 단순하다. 운영체제의 스케줄러나 cron 데몬이 시간을 추적하고 정해진 시각에 프로그램을 실행하면 된다. 그러나 분산 시스템에서는 여러 가지 복잡한 문제가 발생한다. 첫째, 서버 간 시간 동기화 문제다. 각 서버의 시스템 시계가 조금씩 다를 수 있는데, 이로 인해 동일한 작업이 여러 번 실행되거나 아예 실행되지 않을 수 있다. 이를 해결하기 위해 NTP(Network Time Protocol)를 사용하여 모든 서버의 시간을 동기화하지만, 완벽한 동기화는 불가능하며 밀리초 단위의 차이는 여전히 존재한다.

둘째, 작업 중복 실행 방지 문제다. 예를 들어, 세 대의 서버가 모두 "매일 자정에 일일 집계 작업 실행"이라는 스케줄을 가지고 있다면, 자정이 되었을 때 세 서버가 모두 동일한 작업을 실행하려 할 것이다. 이는 데이터 정합성 문제를 일으킬 수 있으며, 시스템 자원도 낭비된다. 이를 방지하기 위해 분산 잠금(distributed lock) 메커니즘을 사용한다. 작업을 실행하기 전에 중앙 저장소(예: Redis, ZooKeeper)에서 잠금을 획득하고, 잠금을 획득한 서버만 작업을 실행하도록 한다.

셋째, 스케줄러 자체의 고가용성 문제다. 만약 스케줄러가 단일 서버에서만 실행된다면, 해당 서버에 장애가 발생했을 때 모든 스케줄링이 중단된다. 이는 시스템의 단일 장애점(single point of failure)이 된다. 이를 해결하기 위해 마스터-슬레이브 구조나 리더 선출(leader election) 알고리즘을 사용한다. 여러 서버가 스케줄러를 실행하되, 그중 하나만 실제로 작업을 스케줄링하는 '리더' 역할을 수행하고, 리더에 장애가 발생하면 다른 서버가 자동으로 리더 역할을 인수한다.

분산 스케줄링 시스템은 작업의 상태 정보도 관리해야 한다. 작업이 언제 마지막으로 실행되었는지, 성공했는지 실패했는지, 다음 실행 예정 시각은 언제인지 등의 정보를 중앙 저장소에 기록한다. 이를 통해 서버 재시작이나 장애 복구 후에도 스케줄을 정확히 유지할 수 있다. 또한 작업 실행 이력을 분석하여 실행 시간이 너무 오래 걸리는 작업을 식별하거나, 특정 시간대의 시스템 부하를 예측할 수 있다.

현대의 분산 스케줄링 시스템은 더욱 정교한 기능들을 제공한다. 작업 간 의존성을 정의하여 "A 작업이 완료된 후 B 작업 실행"과 같은 워크플로우를 구성할 수 있고, 작업 실행 시간대를 제한하여 업무 시간에만 특정 작업이 실행되도록 할 수 있다. 또한 동적 스케줄링을 통해 시스템 부하나 외부 조건에 따라 작업 실행 시각을 자동으로 조정하는 것도 가능하다.

Python의 Celery Beat, Apache Airflow, Kubernetes CronJob 등이 이러한 분산 스케줄링을 구현한 대표적인 시스템이다. 특히 Celery Beat는 Celery 작업 큐와 통합되어, 스케줄된 작업을 메시지 큐에 넣음으로써 실제 실행을 작업자들에게 위임한다. 이를 통해 스케줄링과 작업 실행을 분리하여 각각 독립적으로 확장할 수 있게 되었다. 이러한 분산 스케줄링 시스템은 현대의 데이터 파이프라인, 배치 처리, 시스템 유지보수 자동화에서 핵심적인 역할을 수행하고 있다.