HLS 스트림 모니터링 대시보드
개요
HLS 스트림을 주기적으로 점검하고 영상 이상을 감지하는 Python 기반 모니터링 서비스입니다. 방송이나 VOD 스트림은 HTTP 요청이 성공하더라도 실제 화면이 검거나 멈춰 있을 수 있습니다. 단순 헬스체크만으로는 이런 장애를 잡기 어려워서, ffmpeg 분석 결과를 기준으로 스트림 상태를 판단하도록 만들었습니다.
| 기능 | 내용 |
|---|---|
| 주기 점검 | 설정된 HLS 소스를 30초마다 분석 |
| 영상 이상 감지 | Black Screen, Frozen Frame 감지 |
| 장애 판정 | warning, suspect, signal_loss, check_failed 단계로 상태 판단 |
| 이력 저장 | SQLite에 점검 결과와 상태 변경 이력 저장 |
| 알림 | JSON 웹훅, Slack, Microsoft Teams 지원 |
| 대시보드 | 최신 상태와 프리뷰 프레임 확인 |
기술 스택
| 영역 | 기술 |
|---|---|
| 백엔드 | Python, Flask |
| 영상 분석 | ffmpeg, blackdetect, freezedetect |
| 데이터베이스 | SQLite |
| 인증 | Keycloak OIDC SSO |
| 알림 | Webhook, Slack, Microsoft Teams |
모니터링 방식
각 스트림은 독립 스레드에서 주기적으로 점검합니다. ffmpeg로 짧은 구간을 분석하고, 출력 로그에서 검은 화면과 화면 정지 구간을 파싱해 ProbeResult로 정규화합니다.
분석과 프리뷰 생성은 한 번의 ffmpeg 실행 안에서 처리합니다. 입력 영상을 split=2로 나눠 한쪽은 blackdetect/freezedetect 분석에 쓰고, 다른 한쪽은 대시보드에 보여줄 썸네일로 저장합니다.
flowchart TD
Config[config/streams.json]
Service[MonitorService]
Thread[Per-stream Thread]
FFMPEG[ffmpeg probe]
Parser[black/freeze result parser]
DB[(SQLite)]
Preview[Preview frame]
Notifier[Webhook notifier]
Dashboard[Flask Dashboard]
Config --> Service
Service --> Thread
Thread --> FFMPEG
FFMPEG --> Parser
Parser --> DB
Thread --> Preview
Parser --> Notifier
DB --> Dashboard
Preview --> Dashboard
주요 구현 포인트
Black Screen과 Frozen Frame 감지
ffmpeg의 blackdetect, freezedetect 필터를 사용해 사람이 화면을 직접 보지 않아도 이상 징후를 수치로 판단합니다.
ffmpeg -i <hls_url> \
-vf "blackdetect=d=3:pic_th=0.98:pix_th=0.1,freezedetect=n=-50dB:d=5" \
-f null -
| 감지 항목 | 기준 |
|---|---|
| Black Screen | 지정 시간 이상 검은 화면 지속 |
| Frozen Frame | 지정 시간 이상 프레임 변화 없음 |
| Warning | Black 또는 Freeze 중 하나만 감지 |
| Suspect | Black과 Freeze가 함께 감지됐지만 연속 실패 기준 미달 |
| Signal Loss | Black/Freeze 조합이 연속 실패 기준 충족 |
| Check Failed | ffmpeg 실행 실패, timeout, DNS/TLS/HTTP 오류 |
연속 실패 기반 장애 판정
일시적인 네트워크 흔들림이나 짧은 인코딩 지연을 바로 장애로 보지 않도록 연속 실패 기준을 둡니다. Black과 Freeze가 함께 감지되면 먼저 suspect로 보고, 같은 패턴이 설정 횟수 이상 반복될 때 signal_loss로 올립니다. check_failed도 2회 이상 연속 발생해야 장애로 봅니다.
실패 원인은 그대로 로그를 보여주기보다 운영자가 바로 읽을 수 있는 문장으로 정리합니다. HTTP 4xx/5xx, DNS 실패, timeout, TLS 인증서 문제를 구분해 reason에 남기고, 필요한 경우 stderr 일부를 같이 저장합니다.
Recovery 알림
장애 발생 알림만 보내면 운영자가 복구 여부를 다시 확인해야 합니다. 상태가 healthy로 돌아오면 recovery 이벤트를 별도로 발송해 장애 시작과 종료를 한 흐름으로 추적할 수 있게 했습니다.
점검 이력은 SQLite에 저장하되, 정상/경고 이력은 30일이 지나면 정리합니다. 장애 분석에 필요한 최근 이력은 남기고, 장기 운영 중 DB가 불필요하게 커지지 않도록 했습니다.
설정 파일 기반 소스 관리
모니터링 대상과 판정 기준은 config/streams.json에서 관리합니다. 운영 중 소스별 기준값을 조정할 수 있도록 분석 시간, black/freeze 임계값, 연속 실패 기준을 설정으로 분리했습니다.
{
"check_interval_sec": 30,
"probe_duration_sec": 10,
"black_duration_sec": 3,
"black_pic_ratio": 0.98,
"black_pixel_threshold": 0.1,
"freeze_duration_sec": 5,
"freeze_noise_db": -50,
"consecutive_failures_for_down": 2
}
프로젝트 구조
src/cms_monitor/
├── __main__.py
├── config.py
├── monitor.py
├── notifier.py
├── storage.py
├── timeutil.py
├── auth.py
├── web.py
└── static/
└── dashboard.js
실행
export CMS_SESSION_SECRET='your-secret'
export CMS_KEYCLOAK_CLIENT_SECRET='keycloak-secret'
uv run python -m cms_monitor --host 0.0.0.0 --port 8080
활용 사례
- 방송 스트림 품질 모니터링
- VOD 서비스 가용성 확인
- CDN 장애 빠른 감지
- 인코더 장애와 소스 장애의 초기 탐지
향후 개선점
- 다중 CDN 비교 점검
- QoE 지표 추가
- 스트림별 알림 정책 분리
- 대시보드 차트와 장애 타임라인 개선