TT Lab
블로그 시작하기
배우기 러닝패스 코스

장애 지휘와 포스트모템

비난 없는 포스트모템 — 지금 실패하고 고치면 통과하는 조치

TT Lab 에서 이어서 보기

목표

결제 장애의 기록으로 촉발 배포·근본 원인·기여 요인을 실제 줄과 함께 가르고, 조치마다 「지금은 실패하고 고치면 통과하는」 검증 명령을 씁니다. 사람 이름 없는 포스트모템을 쓰고, 운영 설정을 실제로 고치고, 검증이 거짓말하던 지난 분기 조치를 바로잡습니다.

왜 중요한가

포스트모템이 약속하는 것은 「다시는 안 난다」 이고, 그 약속을 지키는 것은 글이 아니라 조치입니다. 조치가 「완료」 로 닫혀도 검증이 결함을 못 잡으면 완료된 적이 없는 것입니다. 이 실습의 채점기는 여러분의 검증 명령을 사고 당시 사본, 고친 사본 두 벌, 요인 하나만 남긴 사본에 실제로 돌려 봅니다 — 현장에서 검증이 거짓말하는 모든 방식(키만 보기, 종료 코드 덮기, 옛 사본·다른 키 보기, 값 하나 못박기)이 여기서 걸립니다. 이 실습은 60분으로 잡혀 있어 기본 세션 길이와 같으니, 시작할 때 시간을 미리 연장해 두세요.

단계

  1. python3 /opt/fixtures/ic/ickit.py open postmortem 으로 재료를 펼치고 /root/ic/postmortem/README.txt 를 읽으세요. app.log 의 첫 ERROR 바로 앞에 들어간 배포의 커밋 sha(앞 7자)와 그 배포가 바꾼 설정 파일을 /root/ic/postmortem/answers/trigger.txt 에 deploy=<sha>, changed=<etc/...file> 두 줄로 적습니다.
  2. /root/ic/postmortem/answers/evidence.txt 에 세 줄을 적습니다: trigger: <trigger-line>(촉발 배포를 가리키는 deploys.log 의 commit 줄이나 제목 줄), first_error: <first-error-line>(app.log 의 첫 ERROR 줄), recovery: <resolved-line>(이 사고 경보가 해제된 alerts.csv 의 줄). 각 줄은 재료에 있는 그대로여야 합니다.
  3. README 의 팀 기준과 /srv/shop/etc/ 의 설정, 배포 기록을 맞대어 근본 원인 하나(촉발 배포가 기준 밖으로 바꾼 것)와 기여 요인 둘(원래부터 기준 밖이던 것)을 /root/ic/postmortem/answers/factors.txt 에 적습니다. 원인 줄(root_cause: <code> 또는 contributing: <code>) 바로 아래에 evidence: <line> 을 붙입니다. 코드: alert-threshold-high(경보 기준이 높음), cache-ttl-zero(캐시 TTL 0), deploy-no-healthgate(배포 건강 검사 꺼짐), pool-too-small(커넥션 풀이 작음), retry-no-backoff(재시도에 대기 없음), timeout-too-long(게이트웨이 타임아웃이 김).
  4. 근본 원인과 기여 요인마다 조치 항목 하나씩을 /root/ic/postmortem/answers/actions.txt 에 씁니다. 조치마다 action: <what>, owner: <team>, factor: <factor-code>, verify: <shell-command> 네 줄입니다. verify 는 설정 트리를 환경 변수 $ROOT 로 받아(예: "$ROOT/etc/pool.conf") 그 요인이 팀 기준 밖이면 0 이 아닌 코드로, 기준 안이면 0 으로 끝나야 합니다. 채점기가 사고 당시 사본(실패해야 함), 고친 사본 두 벌(값이 달라도 통과해야 함), 그 요인만 남긴 사본(실패해야 함)에 실제로 돌려 봅니다.
  5. /root/ic/postmortem/answers/postmortem.md 에 포스트모템을 씁니다. ## Summary(요약), ## Impact(영향), ## Root cause(원인), ## Actions(조치) 네 절이 있어야 하고(절 이름은 한국어 ## 요약·## 영향·## 원인·## 조치 도 받습니다), 영향 절에는 duration: <N>m(첫 ERROR 부터 경보 해제까지의 분, 버림)과 failed_requests: <M>(app.log 의 ERROR 줄 수), 원인 절에는 근본 원인·기여 요인 코드, 조치 절에는 조치마다 verify: 줄을 적습니다. 어느 줄에도 사람 이름(채널 핸들)을 쓰지 않습니다 — 재료를 인용하는 줄만 evidence: 로 시작하면 예외입니다.
  6. 운영 설정 /srv/shop/etc/ 를 고쳐 세 결함을 팀 기준 안으로 들이세요(기준 안이던 설정은 깨뜨리지 않습니다). 그다음 04 단계의 verify 가 ROOT=/srv/shop 으로 모두 통과하는지 확인합니다.
  7. incident ic-unverifiable 를 실행하면 사건이 납니다. /srv/billing/postmortems/2026-q3.md 의 조치 A1·A2·A3 은 모두 done 이고 /srv/billing/bin/verify-actions 가 셋 다 PASS 를 내는데, /srv/billing/etc/ 의 설정은 셋 다 팀 기준(/srv/billing/README.txt) 밖입니다. (1) 조치마다 verify 를 고쳐 결함이 남아 있으면 실패하고 고쳐지면 통과하게 하고(factor 와 조치 순서는 그대로), (2) 설정을 실제로 고쳐 verify-actions 가 정직하게 PASS 를 내게 하세요. 채점기가 고친 verify 를 사고 당시 사본(옛 사본 etc/old/ 포함)·고친 사본 두 벌·그 요인만 남긴 사본에 돌려 봅니다. /root/incident/ic-unverifiable/report.txt 에 A1: <code>, A2: <code>, A3: <code> 세 줄과 evidence: <original-verify-line> 을 적습니다. 코드는 조치마다 다음 중 하나입니다: always-passes(값을 보지 않고 키 존재만 봄), exit-masked(실패한 종료 코드를 뒤에서 덮음), flaky-timing(시각에 따라 결과가 바뀜), wrong-file(서비스가 읽지 않는 파일을 봄), wrong-key(이름이 비슷한 다른 키를 봄).

참고

재료 펼치기 — 촉발 배포 찾기

python3 /opt/fixtures/ic/ickit.py open postmortem 으로 재료를 펼치고 /root/ic/postmortem/README.txt 를 읽으세요. app.log 의 첫 ERROR 바로 앞에 들어간 배포의 커밋 sha(앞 7자)와 그 배포가 바꾼 설정 파일을 /root/ic/postmortem/answers/trigger.txt 에 deploy=<sha>, changed=<etc/...file> 두 줄로 적습니다.

app.log 는 UTC, git 의 Date 는 커밋한 사람의 시간대입니다. 「가장 최근 배포」 가 아니라 「첫 오류 직전 배포」 입니다. 배포 커밋 본문의 change: 줄이 바꾼 파일을 알려 줍니다.

타임라인의 세 기둥을 실제 줄로

/root/ic/postmortem/answers/evidence.txt 에 세 줄을 적습니다: trigger: <trigger-line>(촉발 배포를 가리키는 deploys.log 의 commit 줄이나 제목 줄), first_error: <first-error-line>(app.log 의 첫 ERROR 줄), recovery: <resolved-line>(이 사고 경보가 해제된 alerts.csv 의 줄). 각 줄은 재료에 있는 그대로여야 합니다.

grep 으로 찾은 줄을 그대로 붙이세요(grep -n 의 줄 번호 접두는 붙어도 됩니다). 비슷한 다른 줄이나 손으로 고친 줄은 떨어집니다.

근본 원인과 기여 요인

README 의 팀 기준과 /srv/shop/etc/ 의 설정, 배포 기록을 맞대어 근본 원인 하나(촉발 배포가 기준 밖으로 바꾼 것)와 기여 요인 둘(원래부터 기준 밖이던 것)을 /root/ic/postmortem/answers/factors.txt 에 적습니다. 원인 줄(root_cause: <code> 또는 contributing: <code>) 바로 아래에 evidence: <line> 을 붙입니다. 코드: alert-threshold-high(경보 기준이 높음), cache-ttl-zero(캐시 TTL 0), deploy-no-healthgate(배포 건강 검사 꺼짐), pool-too-small(커넥션 풀이 작음), retry-no-backoff(재시도에 대기 없음), timeout-too-long(게이트웨이 타임아웃이 김).

기준 안에 있는 설정은 결함이 아닙니다(미끼). 증거는 그 요인을 보여 주는 로그·배포 기록·경보의 줄로 고르세요 — 설정 파일 한 줄은 너무 짧아 증거가 되지 않습니다.

검증할 수 있는 조치

근본 원인과 기여 요인마다 조치 항목 하나씩을 /root/ic/postmortem/answers/actions.txt 에 씁니다. 조치마다 action: <what>, owner: <team>, factor: <factor-code>, verify: <shell-command> 네 줄입니다. verify 는 설정 트리를 환경 변수 $ROOT 로 받아(예: "$ROOT/etc/pool.conf") 그 요인이 팀 기준 밖이면 0 이 아닌 코드로, 기준 안이면 0 으로 끝나야 합니다. 채점기가 사고 당시 사본(실패해야 함), 고친 사본 두 벌(값이 달라도 통과해야 함), 그 요인만 남긴 사본(실패해야 함)에 실제로 돌려 봅니다.

설정은 key=value 이고 같은 키가 또 나오면 아래 줄이 이깁니다. 키 존재만 보거나, 값 하나를 글자 그대로 못박거나, 종료 코드를 || 로 덮으면 떨어집니다. awk -F= 로 마지막 값을 들고 있다가 END 에서 기준과 비교해 exit 하는 꼴이 편합니다. 직접 ROOT=/srv/shop bash -c '<command>'; echo $? 로 돌려 보세요.

비난 없는 포스트모템 쓰기

/root/ic/postmortem/answers/postmortem.md 에 포스트모템을 씁니다. ## Summary(요약), ## Impact(영향), ## Root cause(원인), ## Actions(조치) 네 절이 있어야 하고(절 이름은 한국어 ## 요약·## 영향·## 원인·## 조치 도 받습니다), 영향 절에는 duration: <N>m(첫 ERROR 부터 경보 해제까지의 분, 버림)과 failed_requests: <M>(app.log 의 ERROR 줄 수), 원인 절에는 근본 원인·기여 요인 코드, 조치 절에는 조치마다 verify: 줄을 적습니다. 어느 줄에도 사람 이름(채널 핸들)을 쓰지 않습니다 — 재료를 인용하는 줄만 evidence: 로 시작하면 예외입니다.

원인은 「누가」 가 아니라 「무엇이」(설정·절차·검사 부재)로 씁니다. 숫자는 재료에서 다시 셉니다. 주인은 팀 이름으로 적습니다.

조치를 실제로 하기

운영 설정 /srv/shop/etc/ 를 고쳐 세 결함을 팀 기준 안으로 들이세요(기준 안이던 설정은 깨뜨리지 않습니다). 그다음 04 단계의 verify 가 ROOT=/srv/shop 으로 모두 통과하는지 확인합니다.

서비스는 같은 키가 여러 번 나오면 아래 줄을 씁니다 — 줄을 고치든 끝에 덧붙이든 마지막 값이 기준 안이면 됩니다. 고친 뒤 verify 를 직접 돌려 종료 코드를 보세요.

사건 — 「검증 통과」 인데 같은 장애가 또 났다

incident ic-unverifiable 를 실행하면 사건이 납니다. /srv/billing/postmortems/2026-q3.md 의 조치 A1·A2·A3 은 모두 done 이고 /srv/billing/bin/verify-actions 가 셋 다 PASS 를 내는데, /srv/billing/etc/ 의 설정은 셋 다 팀 기준(/srv/billing/README.txt) 밖입니다. (1) 조치마다 verify 를 고쳐 결함이 남아 있으면 실패하고 고쳐지면 통과하게 하고(factor 와 조치 순서는 그대로), (2) 설정을 실제로 고쳐 verify-actions 가 정직하게 PASS 를 내게 하세요. 채점기가 고친 verify 를 사고 당시 사본(옛 사본 etc/old/ 포함)·고친 사본 두 벌·그 요인만 남긴 사본에 돌려 봅니다. /root/incident/ic-unverifiable/report.txt 에 A1: <code>, A2: <code>, A3: <code> 세 줄과 evidence: <original-verify-line> 을 적습니다. 코드는 조치마다 다음 중 하나입니다: always-passes(값을 보지 않고 키 존재만 봄), exit-masked(실패한 종료 코드를 뒤에서 덮음), flaky-timing(시각에 따라 결과가 바뀜), wrong-file(서비스가 읽지 않는 파일을 봄), wrong-key(이름이 비슷한 다른 키를 봄).

방향: 검증이 거짓말하는 길은 여럿입니다 — 무엇을 읽는가(파일·키), 무엇과 비교하는가(값을 보긴 하나), 종료 코드가 끝까지 살아남는가. 조치마다 셋 중 어디가 어긋났는지 봅니다. 도구: 원래 verify 를 ROOT=/srv/billing bash -c '<command>'; echo $? 로 직접 돌리기, README 의 기준, grep -n. 구체: 먼저 verify 를 고쳐 지금 설정에서 실패하는 것을 눈으로 보고, 그다음 설정을 고쳐 통과하게 하세요. 순서를 거꾸로 하면 검증이 결함을 잡는지 알 수 없습니다.