[DRE] 연속 실패 Job 자동 일시 중지를 통한 운영 효율화 및 비용 절감

안녕하세요, 데이터 엔지니어 주형권입니다.
이번 글에서는 Databricks 운영 환경의 안정성과 비용 효율을 개선하기 위해 구현한 연속 실패 Job 자동 일시중지(Pause) 파이프라인의 기술적인 구현 과정과 이를 통해 얻은 효과에 대해 공유하고자 합니다.
최근에 DRE적인 업무를 계속해서 고민하다 보니 솔직히 말해서 업무의 영역이라고 할 게 없다시피 할 정도로 너무나도 방대하고 코에 걸면 코걸이 귀에 걸면 귀걸이 느낌입니다. 챙기고자 한다면 얼마든지 챙길 수 있고, 신경 쓰지 않고자 한다면 아무런 신경도 쓰지 않아도 되는 것이 DRE의 업무가 아닐까? 생각이 듭니다.
오늘은 어쩌면 간단(?)하지만 어쩌면 굉장히 많이 도움이 될거 같은 기능을 개발한 내용에 대해서 적어보고자 합니다. 이 작업도 역시 Gemini & Databricks Genie를 이용해서 작업을 하였으며, 기획과 설계는 역시 제가 담당하였습니다. 물론 디테일한 개발도 담당하였으며, AI를 적극 활용해서 프로젝트를 진행하였습니다.
1. 구상: 왜 만들었는가?

아마도 대부분의 회사들이 DX시대를 거치며 데이터 민주화를 통한 Self 서비스를 하고 있는 것으로 알고 있습니다. AX 시대를 맞이하면서 이 역시 변함없을 것이라고 생각합니다. DX건 AX건 데이터를 가공하거나 데이터를 적재하는 Job은 필수입니다. 하지만 그만큼 불특정 다수의 사용자가 수많은 파이프라인을 만들면 관리자는 그것을 감당하기 어려운 것이 사실입니다.
또한 생성하는 사람 또한 Agent AI를 통하거나 여러 개를 만들어두고, 딱히 관심을 기울이지 않으면 계속해서 실패해도 그 Job은 주기적으로 적재를 시도하거나 데이터 가공을 시도하거나 어떠한 행위를 항상 시도하게 됩니다. Job이 실행과 동시에 구문 오류나 여러 설정 오류로 시작도 못하면 모르겠지만 다른 이유로 우선 실행을 하고 실행 중에 실패를 하면 그것은 모두 비용입니다.
관리자가 이러한 오류를 모두 통제하고 실패하면 원인을 파악해서 담당자에게 알리고 수정을 하면 좋겠으나 수많은 내가 알지 못하는 Job을 모두 분석하는 것은 여간 어려운 일이 아닙니다. 그래서 이러한 비용의 누수를 방지하고자 연속으로 실패하는 Job을 일시중지 하고 해당 담당자에게 알림을 보내는 시스템을 만들어봤습니다.
2. 문제 정의: 연속 실패 Job으로 인한 운영 비효율
대규모 데이터 플랫폼 환경에서는 수백 개의 Databricks Job이 매일 실행됩니다. 이때 소스 데이터의 스키마 변경, API 키 만료, 혹은 일시적인 인프라 문제 등 다양한 원인으로 특정 Job이 연속적으로 실패하는 상황이 발생할 수 있습니다. 이러한 상황은 두 가지 주요한 문제점을 야기합니다.
- 첫째, 불필요한 클러스터 비용 발생: Job이 실패하더라도 스케줄러는 정해진 주기에 따라 실행을 재시도합니다. 이 과정에서 클러스터가 생성되고 즉시 종료되는 과정이 반복되며 상당한 DBU(Databricks Unit) 비용이 불필요하게 소모됩니다.
- 둘째, 운영 담당자의 알림 피로도 증가: 동일한 오류에 대한 경고 알림이 반복적으로 발생하여 운영 채널의 가독성을 저해하고, 담당자가 다른 중요한 경고를 놓치게 될 위험을 높입니다.
이러한 비효율을 제거하기 위해, 소프트웨어 공학의 '서킷 브레이커(Circuit Breaker)' 패턴에 착안하여 특정 임계치 이상의 연속 실패가 감지된 Job의 스케줄을 자동으로 중지시키는 시스템을 구축하기로 결정했습니다.
3. 시스템 설계: Airflow 기반의 자동화 파이프라인
매일 정해진 시간에 모든 운영 워크스페이스를 점검하고, 연속 실패 Job을 선별하여 스케줄을 PAUSED 상태로 변경하는 자동화 파이프라인을 설계했습니다. 전체적인 아키텍처는 Airflow를 기반으로 구성되었습니다. 저희 시스템은 대부분이 일/주/월 배치이므로 주 1회로 검사하도록 하였습니다.

여기 순서도에 보면 기존에 제가 만들었던 모든 로직과 다르게 알림이 Teams가 아니고 E-mail입니다. 이렇게 한 이유는 Teams는 Slack과 달리 채널에 속해있지 않으면 알림을 받을 수 없습니다. 기본적으로 채널에 속해진 사용자만 알림을 받을 수 있고 멘션을 걸 수 있습니다. 물론 멘션은 걸수 있지만 멘션으로 호출해도 실제 멘션 당한 사람은 아무런 알림이 없습니다. (실제로 해봤습니다.)
그래서 로직의 통일성이 깨지지만 E-mail을 선택하였습니다.
3. 핵심 구현 상세 (Technical Deep Dive)
이 파이프라인이 정확하고 안전하게 동작하기 위한 몇 가지 주요 기술적 고려사항과 구현 내용은 다음과 같습니다.
3.1. 점검 대상의 정확한 식별: schedule과 trigger
첫 번째 단계는 점검 대상을 누락 없이 선정하는 것입니다. Databricks의 Job 스케줄은 두 가지 방식이 있습니다.
- Cron 기반 스케줄 (schedule): UI의 "Advanced" 옵션을 통해 설정하는 정적 스케줄
- 트리거 기반 스케줄 (trigger): "Simple" 스케줄(매시간/매일등)이나, 파일 도착, 테이블 업데이트와 같은 이벤트 기반 동적 스케줄
초기 구현에서는 schedule 필드만 검사하여 trigger 기반의 Job들이 점검 대상에서 누락되는 문제가 있었습니다. 따라서, 두 필드를 모두 확인하여 pause_status가 PAUSED가 아닌 모든 Job을 탐색하도록 로직을 수정했습니다.
# settings.schedule (cron) 과 settings.trigger (periodic, file_arrival 등)
# 두 가지 케이스를 모두 탐지하도록 로직 구성
# Case 1: 전통적 cron 스케줄 (settings.schedule)
if schedule and schedule.get("pause_status", "UNPAUSED") != "PAUSED":
# ... 활성 Job 목록에 추가 ...
# Case 2: 트리거 기반 스케줄 (settings.trigger)
elif trigger and trigger.get("pause_status", "UNPAUSED") != "PAUSED":
# ... 활성 Job 목록에 추가 ...
3.2. 실패 및 완료 사앹의 명확한 정의
연속 실패 여부를 정확히 판단하기 위해서는 Job 실행 상태(Run State)에 대한 명확한 정의가 필요합니다. Databricks Jobs API v2.1은 Job의 상태를 life_cycle_state와 result_state 두 가지 필드로 제공합니다.
- life_cycle_state: Job 실행의 생명주기를 나타냅니다. (PENDING, RUNNING, TERMINATED, SKIPPED, INTERNAL_ERROR 등)
- result_state: TERMINATED 된 Job의 최종 결과를 나타냅니다. (SUCCESS, FAILED, TIMEDOUT, CANCELED)
저는 오탐과 누락을 최소화하기 위해 실패와 완료의 정의를 다음과 같이 확장했습니다.
- 실패(Failure)의 정의:
- result_state = FAILED: 일반적인 태스크 코드 레벨의 실패.
- result_tate = ERROR: 인증 실패, 클러스터 시작 실패 등 인프라 수준의 오류로, 이 경우 life_cycle_state는 INTERNAL_ERROR가 됩니다.
- 완료(Completed)의 정의:
- life_cycle_state = TERMINATED: 정상적으로 실행이 완료된 경우(성공/실패 포함).
- life_cycle_state = INTERNAL_ERROR: 실행 전 인프라 단계에서 즉시 종료된 경우.
이러한 정의를 바탕으로, API 호출 시 completed_only=true 파라미터를 사용하여 실행 중인(RUNNING) Job은 제외하고 오직 '완료'된 Job들의 이력만을 바탕으로 연속 실패를 카운트하도록 구현하여 정확도를 확보했습니다.

추가적으로 수동으로 최근 3회 중에 단 1번이라도 성공을 하였다면, 이 부분도 일시중지에서 제외하였습니다. (사용자가 장애를 인지하고 처리하였다고 가정함)
3.3. Job 소유자에 대한 피드백 메커니즘
자동화 시스템이 사용자 모르게 특정 기능을 중지시키는 것은 잠재적인 혼란을 야기할 수 있습니다. 따라서 시스템의 조치 내용을 Job 소유자에게 명확히 알리는 것이 매우 중요합니다. 서킷 브레이커가 특정 Job의 스케줄을 PAUSED 시키면, 시스템에서 alert_user 태그를 확인하여 1차 발송을 시도하고 없을 경우, 시스템은 해당 Job의 생성자(creator_user_name 필드)에게 조치 내역과 확인이 필요하다는 내용의 안내 메일을 즉시 발송합니다. (Teams로 하지 않은 이유는 2번 내용에 나와 있습니다.)

기본적으로 creator_user_name으로 하지 않은 이유는 담당자가 변경되어도 수동으로 생성자까지는 변경하기 어렵다고 판단하여 태그를 통한 alert_user를 기본으로 하였습니다. alert_user의 경우 Monitoring에서 Observability로: Databricks 워크플로우 알림 체계화 (Teams 연동) 글에서 확인하실 수 있습니다.


이 이메일에는 어떤 환경의 어떤 Job이 왜 중지되었는지 명확히 명시하고, 클릭 한 번으로 해당 Job 페이지로 이동할 수 있는 바로가기 링크를 포함하여 사용자가 즉시 문제를 인지하고 조치할 수 있도록 설계되었습니다.
4. 도입 효과 및 결론
이 서킷 브레이커 파이프라인을 도입한 이후, 다음과 같은 정량적/정성적 효과를 얻을 수 있었습니다.
- 비용 절감: 불필요하게 실패를 반복하는 Job으로 인한 DBU 비용 낭비가 원천 차단되었습니다.
- 운영 효율성 증대: 반복적인 오류 알림이 대폭 감소하여 운영팀이 보다 근본적인 문제 해결에 집중할 수 있는 환경이 조성되었습니다.
- 사용자 책임감 강화: Job 소유자에게 직접적인 피드백이 전달됨으로써, 자신이 생성한 파이프라인의 안정성에 대해 더 높은 책임감을 갖게 되는 문화적 변화를 유도했습니다.
아직 도입 초기라서 엄청나게 많은 이점과 효과를 봤다고 보기는 어렵습니다. 다만 이 자동화 파이프라인은 최소한의 리소스를 투입하여 Databricks 플랫폼의 안정성과 비용 효율성을 동시에 개선하는 데 성공적인 사례가 되었습니다. 향후에는 단순히 Job을 중지시키는 것을 넘어, 실패 원인을 자동으로 분류하고 해결 가이드를 제시하는 방향으로 시스템을 고도화해 나갈 계획입니다.
긴 글 읽어주셔서 감사합니다.