Split View: FDE 장애 진단 플레이북 — 접속 권한부터 보고서까지 6단계
FDE 장애 진단 플레이북 — 접속 권한부터 보고서까지 6단계
- 낯선 환경의 장애는 왜 다른가
- 1단계 — 접속과 권한을 확인한다
- 2단계 — 증상을 재현한다
- 3단계 — 계층을 분리한다
- 4단계 — 원인 가설을 세운다
- 5단계 — 가설을 검증한다
- 6단계 — 보고서를 쓴다
- 직접 연습하기
낯선 환경의 장애는 왜 다른가
자기 팀 서비스에 장애가 나면 어디를 볼지 몸이 압니다. 대시보드 주소, 로그 위치, 최근 배포 이력이 머릿속에 있습니다. 고객사 장애는 그 전제가 전부 사라진 상태에서 시작합니다. 모니터링이 무엇인지, 로그가 어디에 쌓이는지, 어제 무엇이 바뀌었는지 아무것도 모르는 채로, 고객은 옆에서 언제 고쳐지는지 묻습니다.
이 조건에서 필요한 것은 뛰어난 직감이 아니라 고정된 순서입니다. 이 글은 그 순서를 6단계로 적은 플레이북입니다. 관통하는 사례는 하나로 갑니다. 오후부터 결제 API가 간헐적으로 504를 반환한다는 신고 — 실제 사건이 아니라 이 글을 위해 구성한 예시이며, 등장하는 명령은 kubectl, curl, grep처럼 공개된 범용 도구만 씁니다.
1단계 — 접속과 권한을 확인한다
본능은 로그부터 열라고 하지만, 낯선 환경에서 첫 단계는 접속 확인입니다. 어떤 환경에 어떤 경로로 들어갈 수 있는지, 지금 가진 권한이 읽기인지 쓰기인지, 프로덕션인지 스테이징인지부터 확정합니다. 이유는 두 가지입니다. 첫째, 진단 도중에 권한이 없다는 것을 발견하면 그 요청과 승인에 걸리는 시간이 통째로 낭비됩니다. 둘째, 권한 밖의 행동은 그 자체로 사고입니다. 고객 프로덕션에서 권한을 넘는 명령 하나는 장애보다 큰 신뢰 손실을 만듭니다.
확인할 것은 짧습니다. 접속 경로가 살아 있는가. 계정의 권한 범위는 어디까지인가. 읽기 전용으로 진단을 끝낼 수 있는가. 쓰기가 필요해지면 누구에게 요청하는가. 이 네 줄을 장애 티켓 맨 위에 적어 두고 시작합니다.
2단계 — 증상을 재현한다
"가끔 느려요"는 진단할 수 없습니다. 진단 가능한 것은 측정값뿐입니다. 그래서 두 번째 단계는 신고된 증상을 명령 하나로 고정하는 것입니다.
# 증상을 명령 하나로 고정한다 (구성한 예시)
curl -sS -o /dev/null -w "%{http_code} %{time_total}s\n" \
https://api.customer.example/v1/payments/health
# 20회 반복해 실패율을 잰다
for i in $(seq 1 20); do
curl -s -o /dev/null -w "%{http_code}\n" https://api.customer.example/v1/payments/health
done | sort | uniq -c
이 반복 실행에서 20번 중 3번이 504라면, 장애는 전해 들은 이야기에서 수치로 바뀝니다. 재현이 안 되면 그것도 정보입니다. 특정 사용자, 특정 시간대, 특정 경로에서만 나는 문제라는 뜻이고, 질문이 그만큼 좁혀집니다. 재현 명령은 이후 모든 단계에서 수리 여부를 판정하는 잣대로 재사용됩니다.
3단계 — 계층을 분리한다
원인을 찾기 전에 원인이 사는 동네부터 찾습니다. 네트워크, 인증, 애플리케이션, 데이터의 네 계층에 각각 질문을 하나씩만 던집니다. DNS와 TLS는 정상인가. 401이나 403이 섞여 있는가. 파드는 재시작 없이 살아 있는가. 데이터 계층의 지연이 앱으로 전파되고 있는가.
# 계층별로 질문 하나씩 (구성한 예시)
kubectl -n payments get pods -o wide # 앱 계층: 상태와 재시작 횟수
kubectl -n payments describe deploy/api | grep -A3 Limits # 리소스 한도
kubectl -n payments get endpoints api # 서비스와 파드의 연결
kubectl -n payments logs deploy/api --since=30m | grep -ciE "timeout|refused|pool"
구성한 예시에서는 파드 재시작이 없고, 401도 없고, 로그에 타임아웃 계열 문자열이 몰려 있습니다. 그러면 네트워크와 인증 계층은 용의선상에서 빠지고, 앱과 데이터 사이 어딘가로 좁혀집니다. 계층 분리의 목적은 정답을 맞히는 것이 아니라 보지 않아도 되는 곳을 확정하는 것입니다.
4단계 — 원인 가설을 세운다
여기서부터는 로그를 무작정 뒤지고 싶은 유혹과 싸우는 구간입니다. 낯선 환경의 로그는 바다이고, 가설 없이 들어가면 시간만 사라집니다. 남은 용의 구역에서 가설을 두세 개로 명시합니다. 구성한 예시라면 이렇게 됩니다. 첫째, DB 커넥션 풀 고갈 — 로그의 pool 문자열과 간헐성이 부합합니다. 둘째, 특정 쿼리의 지연 — 데이터가 쌓이며 실행 계획이 바뀌었을 가능성. 셋째, 오후의 배포나 설정 변경 — 시각이 겹치는지 확인이 필요합니다.
좋은 가설의 조건은 하나입니다. 무엇을 보면 기각되는지가 명확할 것. 반증 방법이 안 떠오르는 가설은 진단이 아니라 짐작입니다. 가설 목록은 티켓에 그대로 적습니다. 나중에 보고서의 절반이 됩니다.
5단계 — 가설을 검증한다
가설마다 기각 또는 확증에 필요한 최소한의 증거만 찾으러 갑니다.
# 가설 1: 커넥션 풀 고갈 — 발생 빈도와 시간 분포 (구성한 예시)
kubectl -n payments logs deploy/api --since=2h \
| grep -c "connection pool exhausted"
kubectl -n payments logs deploy/api --since=2h \
| grep "connection pool exhausted" | cut -c1-16 | sort | uniq -c
# 14:05 이후에만 몰려 있다면 → 그 시각의 변경 이력을 고객에게 묻는다
구성한 예시의 결말은 이렇습니다. 오류가 14시 5분 이후에만 나타나고, 고객에게 물으니 그 시각에 설정 배포가 있었으며, 배포 diff에서 풀 크기가 줄어 있었습니다. 설정 원복 후 2단계의 재현 명령을 다시 돌려 20번 중 0번 실패를 확인합니다. 검증의 핵심은 두 방향입니다. 원인을 확증하는 증거, 그리고 수리 후 같은 잣대로 다시 잰 정상 수치. 후자가 없으면 고쳤다는 말을 할 수 없습니다.
6단계 — 보고서를 쓴다
FDE의 장애 대응은 시스템이 아니라 보고서에서 끝납니다. 기술적으로 완벽히 고쳐도 보고가 늦거나 모호하면 고객의 기억에는 불안만 남습니다. 첫 문단에는 영향과 현재 상태가 옵니다. 원인 분석은 그다음입니다.
[장애 보고 — 구성한 예시]
영향 : 결제 API 5xx 비율 평시 0.1% → 최대 7% (14:10–15:40)
현재 상태: 완화 조치 적용 완료, 오류율 평시 범위로 복귀 확인
잠정 원인: 14:05 설정 배포에서 DB 커넥션 풀 크기 축소
다음 단계: 원복 완료. 재발 방지로 배포 전 설정 diff 점검 절차를 제안 예정
사람을 지목하지 않는다는 원칙은 특히 고객사에서 무겁습니다. "고객사 담당자가 설정을 잘못 바꿔서"라고 쓰는 순간, 다음 장애에서 그 담당자는 정보를 숨깁니다. 시스템이 어떻게 실패했는지만 적습니다. 비난 없는 보고서는 도덕이 아니라 다음 진단의 속도를 사는 투자입니다.
직접 연습하기
이 플레이북은 읽는 것보다 겪는 쪽이 백 배 빠르게 몸에 붙습니다.
- FDE 엔지니어 키우기 RPG — "느려요", "접속이 안 돼요", "모니터링이 먼저 죽었어요" 같은 31개 미션이 전부 이 6단계의 변주입니다. 시간과 신뢰도가 깎이는 압박 속에서 순서를 지키는 연습을 할 수 있습니다.
- FDE 커리큘럼 로드맵 — 계층 분리에 필요한 도메인별 기술을 체크리스트로 점검합니다.
FDE 완전 가이드 시리즈
The FDE Incident Diagnosis Playbook — Six Steps from Access to Report
- Why Incidents in Unfamiliar Environments Are Different
- Step 1 — Confirm Access and Permissions
- Step 2 — Reproduce the Symptom
- Step 3 — Isolate the Layer
- Step 4 — Form Cause Hypotheses
- Step 5 — Verify the Hypotheses
- Step 6 — Write the Report
- Practice by Doing
Why Incidents in Unfamiliar Environments Are Different
When your own team's service breaks, your body knows where to look: the dashboard URL, the log locations, the recent deploy history are all in your head. A customer-site incident starts with all of those premises gone. You do not know what the monitoring is, where the logs pile up, or what changed yesterday — and the customer is beside you asking when it will be fixed.
What this condition demands is not superior intuition but a fixed order. This post is that order written down as a six-step playbook. One case runs through the whole piece: a report that the payments API has been intermittently returning 504 since the afternoon. It is not a real incident but an example constructed for this post, and every command shown uses only public, general-purpose tools such as kubectl, curl, and grep.
Step 1 — Confirm Access and Permissions
Instinct says open the logs first, but in an unfamiliar environment the first step is confirming access. Which environments can you enter, by which path? Are your current permissions read or write? Is this production or staging? Two reasons. First, discovering mid-diagnosis that you lack a permission wastes the entire round-trip of requesting and approving it. Second, acting beyond your permissions is itself an incident. One over-privileged command in a customer's production loses more trust than the outage did.
The checklist is short. Is the access path alive? What is the scope of this account's permissions? Can the diagnosis be completed read-only? If write access becomes necessary, who approves it? Write these four lines at the top of the incident ticket and then begin.
Step 2 — Reproduce the Symptom
"It is sometimes slow" cannot be diagnosed. Only measurements can. So the second step is pinning the reported symptom down into a single command.
# Pin the symptom into one command (constructed example)
curl -sS -o /dev/null -w "%{http_code} %{time_total}s\n" \
https://api.customer.example/v1/payments/health
# Repeat 20 times and measure the failure rate
for i in $(seq 1 20); do
curl -s -o /dev/null -w "%{http_code}\n" https://api.customer.example/v1/payments/health
done | sort | uniq -c
If 3 out of 20 runs return 504, the incident has turned from hearsay into a number. If it does not reproduce, that too is information: the problem lives with specific users, specific hours, or specific paths, and the question just narrowed accordingly. The reproduction command gets reused at every later step as the yardstick that decides whether the fix worked.
Step 3 — Isolate the Layer
Before hunting the cause, find the neighborhood it lives in. Ask exactly one question of each of four layers — network, auth, application, data. Are DNS and TLS healthy? Are 401s or 403s mixed into the errors? Are the pods alive without restarts? Is latency in the data layer propagating up into the app?
# One question per layer (constructed example)
kubectl -n payments get pods -o wide # app layer: status and restart counts
kubectl -n payments describe deploy/api | grep -A3 Limits # resource limits
kubectl -n payments get endpoints api # service-to-pod wiring
kubectl -n payments logs deploy/api --since=30m | grep -ciE "timeout|refused|pool"
In the constructed example, there are no pod restarts, no 401s, and the log grep shows timeout-family strings clustering. Network and auth drop off the suspect list, and the search narrows to somewhere between the app and the data layer. The purpose of layer isolation is not to guess the answer; it is to make certain which places you no longer need to look.
Step 4 — Form Cause Hypotheses
From here on you are fighting the temptation to rummage through logs at random. The logs of an unfamiliar environment are an ocean; enter without a hypothesis and only your time disappears. Within the remaining suspect zone, state two or three hypotheses explicitly. For the constructed example: first, DB connection pool exhaustion — it fits the pool strings in the log and the intermittency. Second, a specific query slowing down — data growth may have flipped an execution plan. Third, an afternoon deploy or config change — the timing overlap needs checking.
A good hypothesis has one requirement: it must be clear what evidence would kill it. A hypothesis with no imaginable refutation is not diagnosis, it is guessing. Write the hypothesis list straight into the ticket — later it becomes half of the report.
Step 5 — Verify the Hypotheses
For each hypothesis, go collect only the minimum evidence needed to reject or confirm it.
# Hypothesis 1: pool exhaustion — frequency and time distribution (constructed example)
kubectl -n payments logs deploy/api --since=2h \
| grep -c "connection pool exhausted"
kubectl -n payments logs deploy/api --since=2h \
| grep "connection pool exhausted" | cut -c1-16 | sort | uniq -c
# If it clusters only after 14:05 → ask the customer what changed at that time
The constructed example resolves like this: the errors appear only after 14:05; asked about it, the customer confirms a config deploy at that time; the deploy diff shows the pool size was reduced. After reverting, rerun the reproduction command from step 2 and confirm 0 failures out of 20. Verification points in two directions: evidence that confirms the cause, and the healthy number re-measured with the same yardstick after the fix. Without the latter, you cannot say it is fixed.
Step 6 — Write the Report
An FDE's incident response ends not in the system but in the report. Fix everything perfectly and still, if the report is late or vague, what remains in the customer's memory is anxiety. The first paragraph carries impact and current status; root-cause analysis comes after.
[Incident report — constructed example]
Impact : payments API 5xx rate 0.1% baseline → peak 7% (14:10–15:40)
Status : mitigation applied, error rate confirmed back in baseline range
Probable cause: 14:05 config deploy reduced the DB connection pool size
Next : revert complete; will propose a pre-deploy config diff check
The no-names principle weighs especially heavily at a customer site. The moment you write "the customer's admin changed the setting incorrectly," that admin hides information during the next incident. Describe only how the system failed. A blameless report is not a courtesy; it is an investment that buys speed for the next diagnosis.
Practice by Doing
This playbook sticks a hundred times faster when lived than when read.
- FDE Career RPG — the 31 missions, from "it is slow" to "we cannot connect" to "the monitoring died first," are all variations of these six steps. Practice keeping the order while time and trust drain under pressure.
- FDE Curriculum Roadmap — check the per-domain skills that layer isolation depends on.
FDE Complete Guide series