AI 장애 분석 포스트모템 시리즈 7/9
AI에게 장애 분석을 시키면 결과가 길게 나옵니다. 명령어, 로그, 표, 가설, 추천 조치가 한꺼번에 쏟아지기 때문입니다. 그런데 읽는 사람 입장에서 그건 아직 포스트모템이 아니었습니다.
읽는 사람은 아래를 빠르게 판단할 수 있어야 했습니다.
- 무엇이 일어났는가
- 영향은 무엇인가
- 확인된 사실은 무엇인가
- 아직 가설인 것은 무엇인가
- 무엇을 배제했는가
- 어떤 조치를 해야 하는가
그래서 AI에게 데이터를 조회시키기 전에 출력 형식부터 먼저 잡았습니다.
형식을 먼저 정하면 결과가 안 흔들렸습니다
같은 조사 조건이어도 형식을 안 정하면 결과가 매번 다르게 나왔습니다. 어떤 날은 timeline이 길고, 어떤 날은 root cause 후보가 먼저 나오고, 어떤 날은 action item이 빠졌습니다. 형식을 앞에 두자 이 흔들림이 줄었습니다.
이번 시리즈에서 쓴 템플릿은 아래와 같았습니다.
# Incident Postmortem
## Summary
## Impact
## Timeline
## Signals Discovered
## Confirmed Facts
## Root Cause Candidates
## Evidence
## What We Ruled Out
## Missing Evidence
## Corrective Actions
## Follow-up Checks
Summary는 확정된 것과 추정을 섞지 않았습니다
Summary는 3~5문장으로 줄였습니다. 이번 사례라면 이렇게 썼습니다.
처음 보인 단서는 /heartbeat 30초 지연과 Old Gen 증가였다.
Datadog/AWS/code 분석 결과, 실제 trigger는 모바일 유입 증가로 인한 GET /v3/auth traffic surge였다.
auth/session lookup이 R2DBC write pool을 고갈시켰고, pool acquire timeout이 없어 요청이 오래 suspend됐다.
/heartbeat도 같은 pool을 기다리며 timeout됐고, ECS health check가 task replacement loop를 만들었다.
Summary에서는 확정된 것과 아직 추정인 것을 섞지 않으려고 했습니다.
Impact는 기술과 사용자를 나눴습니다
기술 영향:
- auth endpoint latency
- health check timeout
- ECS task replacement
- DB pressure
사용자 영향:
- 로그인 확인 지연
- token refresh 지연
- account/profile API 지연
다만 영향을 모르면 모른다고 적었습니다.
User-visible failure rate is not yet confirmed.
이 자리를 매끈하게 채우면 관측 빈틈이 눈에 안 띄습니다.
Timeline에는 source와 confidence를 붙였습니다
Timeline은 포스트모템의 뼈대였습니다. 이번 사례에서는 시간대를 KST와 UTC로 같이 봐야 했습니다. AI에게도 timezone을 명확히 줬습니다.
Time:
2026-06-12 13:00~15:30 KST
각 이벤트 옆에는 source와 confidence를 붙였습니다.
| Time | Event | Source | Confidence |
|---|---|---|---|
| 13:30 | /v3/auth traffic surge begins |
Datadog metric | Fact |
| 13:38 | long auth span begins | Datadog span | Fact |
| 13:54 | ECS task replacement begins | ECS event | Fact |
| 14:03 | blue/green deploy starts | CodeDeploy | Fact |
| 14:12 | deploy fails and rollback starts | CodeDeploy | Fact |
source가 없으면 timeline이 아니라 기억이었습니다.
Signals Discovered는 사람 단서와 AI 단서를 분리했습니다
이 섹션은 AI 포스트모템에 특히 중요했습니다. 그래서 사람이 처음 준 단서와 AI가 추가로 찾은 관측 항목을 분리했습니다.
Initial clues:
/heartbeat30초- Old Gen 증가
Additional signals:
GET /v3/auth6~10배 증가- long auth spans
- R2DBC pool acquire wait 가능성
- ECS health check replacement loop
- MySQL pressure but not root cause
- mobile WebView traffic concentration
이렇게 분리하면 AI가 처음 단서에 끌려가지 않았다는 게 보였습니다.
Confirmed Facts에는 확인된 것만 넣었습니다
확인된 것:
GET /v3/authtraffic increased 6-10x.- Major GC count was zero.
- Target group health check path was
/heartbeat. - Target group slow start was 0 seconds.
- R2DBC pool had no acquire timeout.
추정은 넣지 않았습니다.
Push notification probably caused the surge.
이건 fact가 아니라 hypothesis 항목으로 옮겼습니다.
Root Cause Candidates는 표로 관리했습니다
Root cause는 하나만 던지지 않았습니다. 후보를 표로 관리했습니다.
| Candidate | Evidence For | Evidence Against | Status |
|---|---|---|---|
| R2DBC pool acquire starvation | traffic surge, no acquire timeout, no child SQL span | needs runtime pool metric | likely |
| GC pause | Old Gen rise | major GC zero, heap 15% | ruled out |
| MySQL root cause | row lock spike | span duration mismatch | contributor |
| bot traffic | request surge | broad IP/mobile users | ruled out |
이 표가 있어야 후보가 왜 올라왔고 왜 내려갔는지 추적할 수 있었습니다.
What We Ruled Out에는 배제 근거까지 남겼습니다
맞힌 것만 쓰지 않았습니다. 배제한 것도 썼습니다. 이번 사례에서 배제한 것은 아래 항목이었습니다:
- Full GC pause
- CPU exhaustion
- Redis bottleneck
- single-IP bot traffic
- fresh API deploy regression
- MySQL as sole root cause
왜 배제했는지까지 붙였습니다.
Major GC count was zero.
CPU was not saturated.
Control database was flat.
Traffic was broadly distributed across mobile clients.
Missing Evidence를 남기는 편이 유리했습니다
Missing Evidence는 AI가 모르는 것을 숨기지 않게 만듭니다. 이번 사례의 missing evidence는 아래 항목이었습니다:
- push/marketing log
- RUM session flow
- ALB access log full aggregation
- exact unique user count
- production pool acquire wait metric
- full stack trace for cancellation log
여기를 빈 채로 두면 다음 조사 때 다시 부딪히지 않았습니다.
Corrective Actions는 유형별로 나눴습니다
Action item을 섞지 않았습니다. 대신 유형을 나눴습니다.
| Type | Example |
|---|---|
| Code | R2DBC acquire timeout 추가 |
| Config | read-only transaction routing 수정 |
| Infra | target group slow start 설정 |
| Observability | pool wait metric 추가 |
| Process | incident 중 deploy alarm gate 추가 |
이번 사례에서 나온 action item:
- R2DBC pool acquire timeout 추가
- read-only auth lookup을 read pool로 라우팅
/heartbeat에서 DB 의존 제거 또는 1초 timeout- auth flow request timeout 추가
- ECS slow start 설정
- CodeDeploy alarm gate 추가
민감정보 제거는 템플릿에 넣어뒀습니다
공유 전에 제거할 항목도 템플릿에 넣었습니다.
- API key
- account id
- task id
- internal URL
- private repository URL
- employee name
다음 조사에서 실제로 바꾼 것
이 템플릿을 먼저 만들자 AI가 결론만 앞세우지 않고 필요한 항목을 놓치지 않았습니다. 그래서 다음 조사부터는 프롬프트를 짜기 전에 이 템플릿 파일을 먼저 열어두고 시작합니다. 빠뜨린 섹션이 있으면 프롬프트를 다시 손보는 신호로 삼습니다.
다음 편에서는 이 템플릿에 채운 초안을 사실, 가설, 액션아이템으로 나누는 편집 흐름을 정리합니다.
'성장과 기술 > 개발과 자동화' 카테고리의 다른 글
| 내부 공유 채널에 RCA 초안을 올리는 워크플로우 (0) | 2026.07.24 |
|---|---|
| RCA 초안을 사실, 가설, 액션아이템으로 나누기 (0) | 2026.07.24 |
| GitLab 배포 이력과 장애 시점을 연결하기 (0) | 2026.07.24 |
| AWS 지표로 Datadog 가설을 검증하기 (0) | 2026.07.24 |
| APM Trace, 로그, JVM 지표를 하나의 증거로 묶기 (0) | 2026.07.24 |
글에서 정리한 생각은 GitHub의 코드와 포트폴리오로 이어지고, 일부는 FamBlend 같은 제품 실험으로 확장됩니다.