200 OK 뒤에 숨겨진 API 이상 징후 탐지 — AI가 아닌 통계로
요약
단순한 업/다운 체크를 넘어, 200 OK 응답 속에서도 발생하는 API 이상 징후를 통계적 방법으로 탐지하는 기술을 소개합니다. 머신러닝 대신 이동 평균과 표준편차(3σ)를 활용하여 비용 효율적이고 설명 가능한 모니터링 시스템을 구축하는 방법을 다룹니다.
핵심 포인트
- 단순 상태 코드 체크의 한계를 극복하기 위해 응답 크기와 응답 시간을 추적
- ML 대신 결정론적이고 설명 가능한 통계적 방식(3σ) 채택
- 엔드포인트별 개별 베이스라인을 설정하여 정확도 향상
- SQL 집계 함수를 활용해 최소한의 데이터로 효율적인 계산 구현
- 오탐 방지를 위한 하한선(Floors) 설정의 중요성
대부분의 업타임 모니터(uptime monitors)는 한 가지 질문에 답합니다: 서비스가 작동 중인가, 아니면 다운되었는가? 하지만 제가 겪었던 최악의 장애 중 일부는 완벽하게 정상적인 200 OK를 반환했습니다:
-
캐시된 에러 페이지를 제공하기 시작한 엔드포인트 (endpoint)
-
상태 코드 200과 함께 {"error": ...}를 반환하는 JSON API
-
응답 속도가 조용히 10배 느려진 경우
-
백엔드가 빈 결과를 반환하기 시작하면서 페이로드(payload)가 14 KB에서 800 바이트로 급감한 경우
단순한 업/다운(up/down) 체크는 이 모든 상황을 그대로 통과해 버립니다. 저는 제 모니터가 "작동은 하지만, 잘못되었다"는 것을 알아차리기를 원했습니다. 제가 이를 어떻게 구축했는지, 그리고 왜 의도적으로 머신러닝 (machine learning, 또는 "AI"라는 단어)을 선택하지 않았는지 소개합니다.
유혹, 그리고 이를 건너뛴 이유
유행하는 방식은 "AI 기반 이상 탐지 (AI-powered anomaly detection)"입니다. 하지만 엔드포인트별 지표(per-endpoint metrics)의 경우, 머신러닝 (ML)은 대부분 과잉 대응(overkill)입니다. 학습 데이터가 필요하고, 모델은 불투명하며, 왜 무언가가 감지되었는지 설명하기 어렵습니다. 단순한 통계 (statistics)는 더 간단하고, 저렴하며, 결정론적(deterministic)이고, 무엇보다 설명 가능(explainable)합니다. 그래서 저는 통계를 사용했습니다.
엔드포인트당 하나의 베이스라인 (baseline)
핵심 결정 사항: 모든 엔드포인트는 각자의 베이스라인을 가집니다. CDN에 캐시된 2 KB JSON 응답과 500 KB HTML 페이지는 공통점이 없으므로, 전역 임계값(global threshold)은 의미가 없습니다. 저는 엔드포인트당 두 가지 신호(signal)를 추적합니다:
-
응답 크기 (bytes)
-
응답 시간 (ms)
각 신호에 대해 저는 이동 베이스라인(rolling baseline)을 유지하며 다음과 같이 질문합니다: "최신 값이 이 엔드포인트에 있어 이상한가?"
수학: 이동 평균 (rolling mean), 표준편차 (std), 그리고 3σ
표준적인 방식입니다. 값이 평균에서 표준편차의 3배를 벗어나면 플래그를 지정합니다:
|value − mean| > 3 · σ
비결은 이를 저렴하게 계산하는 것입니다. 매번 체크할 때마다 엔드포인트의 전체 이력을 불러오고 싶지 않습니다. 평균(mean)과 분산(variance)은 단 세 가지의 실행 집계(running aggregates) — 개수(count), 합계(sum), 제곱의 합(sum of squares) — 만 있으면 되며, 이는 단일 SQL 쿼리로 가능합니다:
SELECT COUNT(*) AS n,
SUM(value) AS s,
SUM(value * value) AS q
FROM signal
WHERE endpoint_id = ?
AND created_at >= ?; -- 이동 창 (rolling window)
그 다음:
mean = s / n
모집단 분산 (population variance) = E[x²] − (E[x])²
variance = max(0.0, q / n - mean * mean)
std = variance ** 0.5
이력 전송도, 모델도 필요 없습니다. 단 세 개의 숫자면 충분합니다.
가드레일 (실질적인 작업이 이루어지는 곳)
가공되지 않은 3σ (3-sigma) 값은 노이즈가 많습니다. 흥미로운 부분은 오탐 (false positives)을 방지하는 것입니다.
- 안정적인 엔드포인트를 위한 하한선 (Floors). 매우 안정적인 엔드포인트는 σ ≈ 0이므로, 아주 미세한 흔들림도 "> 3σ"가 되어 끊임없이 알람을 받게 됩니다. 따라서 임계값 (threshold)은 다음 세 가지 값 중 최댓값으로 설정합니다:
threshold = max(3 * std, # 통계적 임계값
rel_floor * mean, # 예: 크기의 경우 +50%
abs_floor) # 예: +500 ms, 절대적 최소값
변화는 통계적으로나 실질적으로 유의미해야 합니다.
- 방향이 중요합니다. 크기 (size)의 경우, 양방향 모두 플래그를 지정합니다. 응답이 비대해지는 것도, 잘리거나 비어 있는 것도 모두 나쁜 신호이기 때문입니다. 지연 시간 (latency)의 경우에는 느려지는 경우만 카운트합니다.
def is_anomalous(value, mean, std, *, rel_floor, abs_floor, both_directions):
threshold = max(3 * std, rel_floor * mean, abs_floor)
delta = value - mean
flagged = abs(delta) > threshold if both_directions else delta > threshold
return flagged, ("up" if delta > 0 else "down")
-
플래핑 (Flapping) 방지. 한 번의 이상한 체크는 장애가 아닙니다. 알람을 보내기 전, 동일한 방향으로 연속 두 번의 이상 징후 체크가 발생해야 합니다.
-
웜업 (Warm-up) 가드. 최소 샘플 수(저는 약 50개를 사용합니다) 미만일 때는 알람을 전혀 보내지 않습니다. 아직 신뢰할 수 있는 기준선 (baseline)이 없기 때문입니다.
이러한 요소들이 결합되어 노이즈가 많은 3σ 트리거를, 엔드포인트가 정말로 평소와 다르게 동작할 때만 작동하는 시스템으로 탈바꿈시킵니다.
그렇다면 "AI"는 어디에 쓰일까요?
제가 중요하게 생각하는 경계선은 이렇습니다: 탐지 (detection)는 통계가 하고, AI는 설명만 합니다.
수학적 계산이 무언가를 플래그(flag)하면, 저는 그 숫자들을 작은 LLM 호출로 전달하여 다음과 같이 변환합니다:
[입력]: payload size dropped from ~14 KB to ~800 B (−94%), 2 checks in a row
[출력]: "이 엔드포인트는 평소 응답 대신 에러 또는 빈 페이로드 (empty payload)를 반환하고 있을 가능성이 높습니다. 200 OK 응답을 유지하면서도 본문 크기가 약 94% 감소했습니다."
모델은 인간의 문장을 작성합니다. 무엇이 이상 징후인지 결정하는 것이 아닙니다. 저는 3σ (3-sigma) 임계값을 머신러닝 (Machine Learning)이라고 마케팅하는 것을 거부합니다. 탐지 (Detection) = 수학, 설명 (Explanation) = 언어입니다. 이 전체 과정을 "AI 이상 징후 탐지"라고 부르는 것은 어떤 부분이 무엇인지에 대해 거짓을 말하는 것입니다.
핵심 요약 (TAKEAWAY)
"서비스는 작동 중이지만 고장 난 상태"를 잡아내기 위해 머신러닝 (ML)이 꼭 필요한 것은 아닙니다. 엔드포인트 (Endpoint)별 이동 평균 기준선 (Rolling baseline), max(3σ, 상대적 하한선, 절대적 하한선) 임계값, 방향 규칙 (Direction rule), 그리고 연속 2회 발생 시 알림 규칙 (Two-in-a-row guard)만으로도 놀라울 정도로 충분한 성과를 낼 수 있습니다. 또한 모든 알림이 완전히 설명 가능 (Explainable)하므로, 새벽 3시에 문제를 마주하고 있을 때는 블랙박스 (Black box) 모델보다 훨씬 유리합니다.
이 기능은 제가 구축 중인 업타임 모니터 (Uptime monitor)인 PingMon (pingmon.de)에서 실행되지만, 이 기술은 범용적입니다. 메트릭 (Metric) 이력이 있는 무엇에든 결합할 수 있습니다. 윈도우잉 (Windowing)이나 계층별 비용 제어 (Per-tier cost controls)에 대해 더 자세히 알고 싶으시다면 댓글로 알려주세요.
— Dario
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기