K8s 모니터링 기능 개발
Airflow 실행과 Kubernetes 서빙 리소스의 상태를 한 흐름에서 판정하기
Airflow 실행 상태와 Kubernetes 서빙 리소스·Revision·Pod 상태를 수집·통합해 대표 상태를 판정했습니다.
- Spring Boot
- Kubernetes
- Airflow
- Prometheus
- Grafana
- Loki
새벽에 실행되는 모델 학습이 실패하면 알림은 받았지만, Kubernetes Event는 약 한 시간 뒤 사라졌습니다. 상태 수집·통합과 판정 로직으로 운영 신호를 보존하고 판단 가능한 모니터링 시스템을 만들었습니다.
문제의 시작
모델 학습은 새벽에도 실행됐습니다. 실패 알림을 받으면 곧바로 원인을 확인해야 하지만, Kubernetes Event는 약 한 시간 뒤 사라졌습니다. 담당자가 알림을 확인하는 시점에는 어떤 Pod나 Revision에서 무슨 일이 있었는지 되짚을 기록이 남아 있지 않은 경우가 있었습니다.
알림은 실패가 있었다는 사실만 알려 줄 뿐, 그 실패가 파이프라인 실행·서빙 리소스·Revision·Pod 중 어디에서 시작됐는지는 보여 주지 않습니다. 관련 상태는 워크플로 시스템과 테넌트별 Kubernetes 환경에 흩어져 있어, 원인을 확인하려면 각 경로를 따로 열어 같은 시점의 신호를 다시 맞춰야 했습니다.
문제는 단순히 조사 시간이 길다는 데 있지 않았습니다. 상위 서빙 리소스가 준비 상태여도 일부 Revision이나 Pod는 실패할 수 있고, 이미 사라진 Event는 나중에 화면을 열어도 되살릴 수 없습니다. 수집 경로 하나가 응답하지 않는다고 전체 목록이 멈추거나, 확인할 수 없는 상태를 정상으로 표시해서도 안 됐습니다.
그래서 상태 수집·통합과 판정 로직을 분리해 운영 신호를 남기기로 했습니다. 각 신호의 관측 시각과 확인 불가 상태를 포함한 스냅샷을 만들고, 하위 단계 중 가장 심각한 상태를 대표 상태로 합성했습니다. 화면은 원격 환경을 매번 순회하지 않고, 이미 보존된 스냅샷을 읽습니다.
완료 기준은 색깔 있는 화면을 만드는 것이 아니었습니다. 운영자가 테넌트와 파이프라인을 특정하고 어느 단계에서 왜 멈췄는지 한 문장으로 설명할 수 있어야 했습니다. 한 환경의 수집 실패가 다른 목록을 막지 않는지도 확인했습니다. 모델 학습은 Saga의 단계별 상태를 DB에 기록하며 진행됩니다. DB에 남은 Saga 상태와 Kubernetes에서 실제 관측한 상태가 어긋날 때 이를 탐지하는지, 같은 정상 상태를 반복 저장하지 않고 의미 있는 상태 전환만 이력으로 남기는지를 검증했습니다.
단계별 상태를 분리해야 어디에서 실패했는지 한 문장으로 설명할 수 있습니다.
파이프라인 실행에서 서빙 리소스 설정, 배포 리비전, 파드 기동으로 이어지며 각 단계에는 서로 다른 실패 원인이 있습니다.
수집과 조회를 분리한 이유
화면을 열 때 원격 시스템을 순회하면 가장 느린 대상이 전체 응답을 결정합니다. 일부 수집 경로가 실패하면 목록 자체가 열리지 않는 문제도 생깁니다.
중앙 메타데이터는 주기 조회하고 실행 환경은 상시 감시하며 파이프라인 상태는 주기적으로 확인했습니다. 각 결과를 같은 스냅샷 형식으로 변환해 조회 경로에서는 외부 I/O를 없앴습니다.
수집은 비동기로 계속하고 화면은 완성된 스냅샷만 읽습니다.
중앙 메타데이터, 실행 환경 이벤트와 파이프라인 상태를 시점별 스냅샷에 모아 판정한 뒤 조회 API와 상태 화면에 제공합니다.
외부 시스템에 의존하지 않는 판정과 하위 상태 반영
판정 로직에서 네트워크와 저장소 접근을 제거하고 스냅샷과 규칙만 입력받게 했습니다. 덕분에 같은 입력은 항상 같은 결과를 내고 비운영 환경에서 발견한 조합을 단위 테스트로 고정할 수 있었습니다.
상위 상태에는 Airflow 실행, 서빙 리소스, Revision과 Pod 중 가장 심각한 상태를 반영하되 DB에 남은 Saga 상태와 직접 관측한 상태의 불일치는 별도 원인으로 남겼습니다. 오래되거나 누락된 신호도 정상으로 간주하지 않고 ‘정보가 오래됨’ 또는 ‘확인 불가’로 구분했습니다.
상위 상태와 불일치 원인을 섞지 않아 운영자가 다음 행동을 고를 수 있게 했습니다.
여러 실행 단계에서 가장 심각한 상태를 선택하고 DB에 남은 Saga 상태와 비교해 대표 상태 또는 불일치 원인을 만듭니다.
변화만 저장하는 상태 이력
매 수집 결과를 모두 저장하면 같은 정상 상태가 반복돼 중요한 전환이 묻힙니다. 현재 판정과 데이터베이스의 직전 판정을 비교해 값이 달라질 때만 이력을 한 행 추가했습니다.
데이터베이스를 이력의 단일 출처로 삼아 재기동과 다중 요청에서도 같은 전환을 중복 기록하지 않게 했습니다.
이력의 단위는 수집 주기가 아니라 의미 있는 상태 변화입니다.
현재 판정과 직전 판정을 비교해 같으면 기록하지 않고 다르면 변화 이력을 한 건 저장합니다.
메모리와 실행 인스턴스 수의 제약
상태 수집 서비스는 감시 대상을 메모리에 유지합니다. 비운영 환경의 실제 데이터를 세어 보니 전체 규모보다 한 대상에 몰린 리소스가 메모리 사용을 좌우했습니다.
실행 인스턴스 수를 늘리면 각 인스턴스가 모든 대상을 다시 감시해 캐시 사본과 연결 수가 함께 늘어납니다. 그래서 우선 한 인스턴스로 검증하고, 확장 검토 기준과 대상 분할 및 수집 범위 축소 순서를 미리 정했습니다.
요청마다 독립적으로 처리하는 일반 API처럼 실행 인스턴스 수만 늘리면 감시 연결과 메모리 사용량이 함께 늘어납니다.
관측 인스턴스를 두 대로 늘리면 각 인스턴스가 전체 대상을 감시해 스냅샷 사본과 연결 비용이 두 배가 됩니다.
빠른 조회와 상세 확인을 분리하기
관리 화면은 목록과 판정 결과만 빠르게 전달하도록 단순하게 구성했습니다. 원본 이벤트가 필요할 때는 상태 수집 서비스가 다시 전달하지 않고 권한이 있는 기존 경로에서 직접 상세 내용을 확인합니다.
화면 단위도 인프라 리소스가 아니라 운영자가 관리하는 앱을 중심으로 맞췄습니다. 문제만 보기, 상태 원인과 마지막 갱신 시각을 함께 보여 오래된 경고를 구분하게 했습니다.
빠른 상태 조회와 무거운 원본 이벤트 조사를 서로 다른 경로에 둡니다.
운영자는 운영 상태 화면에서 상태 조회 API의 스냅샷을 보고 필요할 때만 실행 환경의 원본 이벤트를 별도 경로로 확인합니다.
검증과 현재 상태
현재는 검증 환경의 실제 데이터로 확인하는 단계입니다. 실제 Airflow 실행과 Kubernetes 서빙 리소스·Revision·Pod 상태 조합, 지연, 부분 실패와 상태 전환을 주입해 스냅샷과 이력이 기대대로 변하는지 확인했습니다.
판정 규칙과 상태 합성 로직은 단위 테스트로 고정했고, 수집 경로 하나가 실패해도 다른 스냅샷과 조회 API가 유지되는지 점검했습니다. 출시 이후의 효과는 아직 측정하지 않았습니다.
배운 점
관측 시스템은 신호의 개수보다 각 신호가 언제의 사실인지, 충돌하면 무엇을 우선할지를 먼저 정의해야 합니다. 최신성과 ‘확인 불가’를 명시하자 화면의 색이 운영 행동으로 연결됐습니다.
또한 감시 앱은 요청 상태를 보관하지 않는 일반 웹 앱과 동작 방식이 다릅니다. 복제와 메모리 모델을 실제 대상 분포로 검증해야 안전한 확장 기준을 세울 수 있습니다.