AI 장애 분석 포스트모템 시리즈 3/9
Datadog scope를 보완하고 AI에게 첫 조사 요청을 넣었습니다. 이때 프롬프트에서 계속 붙잡고 있어야 했던 문장은 하나였습니다.
Do not assume /heartbeat is the root cause.
사람이 본 그래프를 알려주되, 그 그래프를 원인으로 확정하지 못하게 했습니다.
초기 단서를 원인으로 못박지 않게 만든 프롬프트
프롬프트 자체는 아래처럼 단정하지 않게 만들었습니다.
Investigate production incident.
Time:
2026-06-12 13:00~14:00 KST
Known clue:
- Datadog screenshot shows a slow GET /heartbeat trace.
- JVM Old Gen appears to increase around the same time.
Do not assume /heartbeat is the root cause.
Search Datadog first for broader correlated signals.
Collect:
- APM traces across suspicious resources
- logs, errors, warnings, monitor events
- JVM heap, old gen, new gen, survivor, non-heap, GC metrics
- request count and latency around affected services
Output:
1. Commands/API queries used
2. Timeline
3. Signals discovered
4. Confirmed facts
5. Root cause candidates
6. Evidence for and against each candidate
7. Missing evidence
8. Recommended next checks
Output 항목을 명시해두면 AI가 결론만 앞세우지 않고 근거를 같이 남깁니다.
첫 확인은 여전히 권한이었습니다
AI가 처음 한 것은 validate였습니다.
GET /api/v1/validate -> 200
그 뒤에는 실제 조회 가능 여부를 확인했습니다. 가능했던 것은 아래 항목이었습니다:
spans search
logs search
events search
monitors list
이 시점에도 아직 막혀 있던 것:
GET /api/v1/query -> 403
POST /api/v2/query/timeseries -> 403
GET /api/v1/hosts -> 403
1차 조사는 이 상태로 진행됐습니다. 아직 metric query가 열리기 전이었지만 APM, logs, events만으로도 방향은 바뀌었습니다.
/heartbeat보다 훨씬 긴 요청이 먼저 나왔습니다
AI가 span search를 넓게 돌리자 아래 계열이 나왔습니다.
AuthController.checkLogin
POST /v3/auth/token
AccountController.getAdInfo
AuthController.appRefreshToken
일부 span은 수백 초, 한 auth span은 약 20분까지 늘어져 있었습니다. 그 순간 /heartbeat의 위치가 바뀌었습니다. 원인 후보에서 증상 후보로 내려갔습니다.
/heartbeat가 느린 건 맞지만, 더 큰 지연은 business endpoint에서 나오고 있었습니다.
readiness cancellation 로그는 결과 쪽에 있었습니다
Datadog logs에서는 readiness error 로그가 반복됐습니다. 이때 root exception은 coroutine cancellation이었습니다.
MonoCoroutine was cancelled
요청이 오래 걸리고 클라이언트나 ALB가 포기하면서 coroutine이 취소되면 이런 로그가 남을 수 있었습니다. 이건 원인이라기보다 결과 쪽 신호에 가까웠습니다. 다만 눈에 띈 부분은 로그가 여러 Netty event-loop thread에서 관측됐다는 점이었습니다.
특정 thread 하나의 문제라기보다 서비스 전체가 같은 압력을 받고 있었습니다.
ECS task churn이 인프라 계층까지 이어져 있었습니다
Datadog events에서는 ECS task가 unhealthy로 판단되어 교체되는 흐름이 보였습니다. 처음에는 애플리케이션 지연으로만 보였는데 실제로는 인프라 계층까지 번지고 있었습니다.
business endpoint 지연
-> /heartbeat timeout
-> ECS task unhealthy
-> task replacement
-> 새 task가 다시 traffic 수신
-> 반복
이 패턴은 뒤의 AWS 조사에서 더 뚜렷해졌습니다.
Watchdog은 여러 계층을 같이 표시했습니다
Datadog Watchdog은 account-service만 열지 않았습니다. 같은 시점에 관련 web/service와 MySQL 계열의 story가 함께 열렸습니다. 단일 endpoint 문제가 아니라 여러 계층이 동시에 흔들렸다는 근거였습니다.
다만 여기서 조심해야 했던 것은 상관관계였습니다. Watchdog이 MySQL을 같이 표시했다고 해서 DB가 root cause라는 뜻은 아니었습니다. 원인 후보를 늘려줄 뿐이었습니다.
1차 조사 후 가설 정렬
1차 조사로 정리한 가설은 아래와 같았습니다.
A. auth/account 계열 reactive pipeline hang
B. 외부 provider response anomaly
C. MySQL contention
D. JVM Old Gen / GC pressure
E. ECS health check timeout amplification
가장 강해 보인 건 A와 E였습니다. 반면 D는 여전히 반박할 수 없는 상태였습니다. major GC count, GC time, heap max, CPU를 아직 조회할 수 없었기 때문입니다.
확인하지 못한 것을 남겨두는 편이 안전했습니다
1차 조사는 방향을 바꾸는 데는 충분했지만 결론을 내기에는 부족했습니다. 아직 조회하지 못한 것은 아래 항목이었습니다:
- JVM Old Gen timeseries
- heap max 대비 사용률
- major GC count
- major GC time
- minor GC overhead
- CPU
- request count timeseries
- host/task 분포
AI는 더 넓은 단서를 찾았지만 권한이 부족한 구간에서는 일부 가설을 끝까지 확인하지 못했습니다. 그래서 이 자리는 숨기지 않고 그대로 미확인 항목으로 두는 편이 뒤의 판단에 더 유리했습니다.
다음 조사부터 고정한 순서
이 방식이 유용했던 이유는 순서 때문이었습니다. 사람도 AI도 가장 눈에 띄는 그래프에서 시작하기 쉽습니다. 이번에는 아래 순서를 강제했습니다.
- 사람이 본 단서를 입력합니다.
- 단서를 원인으로 가정하지 않습니다.
- 같은 시간대의 span, log, event, monitor를 넓게 찾습니다.
- 더 설명력이 큰 단서를 다시 정렬합니다.
- 확인하지 못한 데이터를 명시합니다.
/heartbeat는 이 흐름을 거쳐 원인에서 증상으로 내려갔습니다. 대신 auth/account endpoint long span과 ECS task churn이 더 큰 후보로 올라왔습니다. 다음 편에서는 metric query 권한이 열린 뒤 Old Gen과 GC를 다시 본 흐름을 정리합니다.
'성장과 기술 > 개발과 자동화' 카테고리의 다른 글
| Datadog을 Claude Code에 연결하고 첫 증거자료를 수집하기 (0) | 2026.07.23 |
|---|---|
| AI 장애 분석은 접근 권한 설계부터 시작합니다 (0) | 2026.07.21 |
| 자동화가 실패했을 때 사람이 복구할 수 있게 만들기 (0) | 2026.06.15 |
| 공개 데이터와 내부 데이터를 분리하는 법 (0) | 2026.06.14 |
| 최신 데이터, 월별 스냅샷, 감사 백업을 나눠 저장하기 (0) | 2026.06.13 |
글에서 정리한 생각은 GitHub의 코드와 포트폴리오로 이어지고, 일부는 FamBlend 같은 제품 실험으로 확장됩니다.