고민보단 실천을

분산 스케줄링 설계: Cron, Quartz, ShedLock 중 어떤 방식이 운영에 유리한가 본문

카테고리 없음

분산 스케줄링 설계: Cron, Quartz, ShedLock 중 어떤 방식이 운영에 유리한가

Just-Do-It 2026. 4. 9. 20:59

분산 스케줄링 설계: Cron, Quartz, ShedLock 중 어떤 방식이 운영에 유리한가

스케줄러는 단순해 보이지만, 인스턴스가 두 대만 넘어가도 '누가 실행할 것인가'가 운영 이슈가 된다.

중급 설계에서는 스케줄 실행 정확도보다 중복 실행 방지, 실패 재시도, 관측 가능성을 먼저 다뤄야 한다.

왜 지금 이 주제가 중요한가

  • 단일 인스턴스 cron은 간단하지만 장애 조치가 취약하다.
  • Quartz는 강력하지만 운영 복잡도와 상태 저장 비용이 따른다.
  • ShedLock은 간단한 분산 락 기반이지만 정밀한 스케줄 엔진을 대체하진 못한다.

핵심 설계 포인트

  • 단순 반복 작업이면 플랫폼 수준 cron(Kubernetes CronJob 포함)부터 검토한다.
  • 정확한 일정, misfire 처리, 달력 기반 스케줄이 필요하면 Quartz가 유리하다.
  • 기존 애플리케이션 내부 작업을 최소 변경으로 분산 환경에 올리려면 ShedLock이 현실적이다.
  • 어떤 방식을 택하든 실행 이력과 중복 실행 감지를 남겨야 한다.

예시 구성

Cron: 플랫폼이 주기적으로 컨테이너 실행
Quartz: trigger + job store + misfire policy
ShedLock: @Scheduled + distributed lock + max lock duration

적용 순서(실무 플로우)

설계 자체보다도 '작게 도입하고 관측하면서 확장하는 순서'가 운영 성공률을 좌우한다.

  1. 작업 종류를 단순 반복, 시간 정밀, 장시간 실행, 고가용성으로 분류한다.
  2. 중복 실행 허용 여부와 최대 실행 시간, 재시도 정책을 정한다.
  3. 락 저장소(DB/Redis) 또는 scheduler 저장소의 가용성을 확인한다.
  4. 실행 성공률, misfire, 지연을 모니터링하는 대시보드를 만든다.
  5. 수동 재실행 절차와 비상 중지 절차를 문서화한다.

운영 체크포인트

  • NTP와 timezone이 어긋나면 스케줄러 디버깅이 매우 어려워진다.
  • 장시간 실행 작업은 락 만료와 heartbeat 전략을 같이 설계한다.
  • 실패한 작업을 수동으로 다시 돌릴 수 있는 운영 도구가 필요하다.

운영 지표/알람 추천

  • 실행 성공률과 misfire 횟수
  • 중복 실행 감지 건수와 락 획득 실패율
  • 스케줄 지연 시간과 backlog 증가량
  • 실행 노드별 편중 여부

빠른 점검 명령/쿼리

# 최근 24시간 misfire/중복 실행 로그 확인
# 락 키별 획득 실패 건수와 재시도 간격 점검
# 노드 시계 오차(NTP)와 timezone 설정 확인

구조화 로그 필드 추천

  • traceId/requestId/eventId처럼 흐름을 이어주는 키를 남긴다.
  • endpoint/topic/flag/version 등 주제별 핵심 차원을 구조화한다.
  • 실패 이유(reasonCode)와 재시도 횟수(retryAttempt)를 분리한다.
  • 민감정보는 마스킹하고, payload는 샘플링 또는 요약 저장한다.
{
"level": "INFO",
"message": "job processed",
"traceId": "8d3f...",
"eventId": "evt_123",
"queue": "outbox-relay",
"status": "DONE",
"latencyMs": 184,
"retryAttempt": 0
}

테스트 케이스 샘플

테스트 케이스(최소 3종):
1) 정상: 기대한 성공 경로와 상태 전이가 유지되는지
2) 실패: 다운스트림 오류/잘못된 입력이 예측 가능한 에러로 떨어지는지
3) 동시성/재시도: 같은 요청 또는 이벤트가 반복돼도 부작용이 없는지

추가(가능하면):
- 장애 복구: 프로세스 재시작 후 중간 상태를 정상 회복하는지
- 부하: p95/p99와 queue/pool saturation이 임계값 안에 드는지

트레이드오프/대안

  • 운영 복잡도를 줄이면 기능 유연성이 떨어질 수 있고, 반대도 마찬가지다.
  • 기본값은 출발점일 뿐이다. 실제 트래픽과 실패 패턴을 보고 다시 조정해야 한다.
  • 관측 없이 최적화하면 체감 개선과 회귀를 구분하기 어렵다.
  • 팀 경계가 많은 시스템일수록 인터페이스 계약과 문서가 코드만큼 중요하다.

성공 기준(SLO) 예시

  • 핵심 경로 에러율: 0.1% 이하
  • 핵심 요청/이벤트 p95 지연: 서비스 목표 내 유지
  • 중복 실행 또는 데이터 유실: 0건
  • 장애 감지 후 임시 조치까지 걸리는 시간: 10분 이내

자주 터지는 실수/트러블슈팅

  1. 분산 환경에서 단순 `@Scheduled`만 쓴다: 인스턴스 수만큼 중복 실행된다.
  2. 락 만료 시간을 너무 짧게 잡는다: 작업 중복 실행 가능성이 커진다.
  3. 실행 이력을 저장하지 않는다: 실패 재현과 감사가 불가능하다.

바로 적용 템플릿

스케줄링 선택 템플릿:
중복 실행 허용 여부
최대 실행 시간 / 재시도 정책 / 수동 재실행 절차
락 또는 job store 종류
모니터링 지표(misfire, success, duration)

검증 방법

  • 인스턴스를 2대로 늘린 뒤 같은 시각에 작업이 한 번만 실행되는지 검증한다.
  • 작업 도중 프로세스 종료 시 락 만료와 재실행이 의도대로 동작하는지 확인한다.

장애 대응 Runbook(초안)

  • 현상: 어떤 사용자/서비스/플랫폼에서 무엇이 깨졌는지 한 문장으로 정리한다.
  • 범위: 언제부터 시작됐고, 영향받은 비율과 핵심 경로를 적는다.
  • 증거: 로그 3줄, 지표 1개, 최근 배포/설정 변경 1개를 먼저 모은다.
  • 임시 조치: 차단, 롤백, 스위치 전환, 재시도 제한 중 무엇을 할지 결정한다.
  • 근본 원인: 계약, 타임아웃, 락, 캐시, 버전, 운영 절차 중 어디가 깨졌는지 좁힌다.
  • 재발 방지: 테스트, 알람, 문서, 기본값을 함께 수정한다.

리뷰 체크리스트

  1. 실패 시나리오가 문서와 코드에서 같은 의미로 정의돼 있다.
  2. 타임아웃/재시도/락/캐시 같은 보호 장치가 상호 충돌하지 않는다.
  3. 관측 지표와 상관관계 키가 있어 운영 중 재현이 가능하다.
  4. 롤백 또는 비상 스위치가 준비돼 있다.
  5. 최소 1개 이상의 동시성/부하/중간 실패 테스트가 자동화돼 있다.
  6. 공식 문서 링크와 팀 의사결정 근거가 남아 있다.

팀 문서 템플릿

팀 문서 템플릿(복붙용):
1) 목표/배경: 어떤 운영 비용 또는 장애를 줄이려는가
2) 범위: API/잡/토픽/디바이스/리전 중 어디까지 적용하는가
3) 규칙: 키, 상태, 버전, TTL, timeout, retry 기본값
4) 예외: 허용하지 않는 상황과 에러 코드/조치 기준
5) 운영: 대시보드, 알람, 소유 팀, 점검 주기
6) 장애 대응: 임시 조치, 롤백, 후속 공지 절차
7) 변경 이력: 언제 누가 왜 기본값을 바꿨는가

FAQ(자주 묻는 질문)

Q. 처음부터 완벽하게 설계해야 하나요?
A. 아니다. 핵심 경로 1개부터 적용하고, 운영 지표를 보며 기본값을 보정하는 편이 실제로 더 안전하다.

Q. "분산 스케줄링 설계: Cron, Quartz, ShedLock 중 어떤 방식이 운영에 유리한가"를 도입했는데도 문제가 남아 있습니다. 어디부터 봐야 하나요?
A. 먼저 상관관계 키가 있는 로그와 지표로 실패 범위를 좁히고, 최근 배포/설정 차이를 확인한다. 대부분은 기본값보다 경계 조건에서 터진다.

Q. 팀 합의가 자꾸 흔들립니다. 무엇을 문서로 남겨야 하나요?
A. 상태 전이, 기본값, 예외 처리, 롤백 기준 네 가지는 반드시 남겨야 한다. 이 네 가지가 없으면 장애 때 판단이 흔들린다.

참고/출처

Comments