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

장애 지휘와 포스트모템

같은 5xx, 다른 범인 — 시계를 맞추고 타임라인 세우기

TT Lab 에서 이어서 보기

목표

시간대와 시계가 서로 다른 기록들(서버 로그 둘, 로드밸런서 표본, git 배포 기록, 플래그 기록, 경보·지표)을 하나의 기준 시계로 맞춰 타임라인을 세우고, 첫 오류·용의 변경·탐지 지연·완화 시각을 데이터로 정합니다. 마지막에는 같은 증상의 세 서비스를 원인별로 가르고 되돌릴 것만 되돌립니다.

왜 중요한가

포스트모템의 타임라인이 틀리면 그 위에 쌓은 원인과 조치가 모두 틀립니다. 그리고 타임라인을 틀리게 만드는 것은 대개 큰 실수가 아니라 시간대 표시 하나, 몇 분 어긋난 시계 하나입니다. 사고 중에는 같은 착시가 「전부 롤백하자」 는 결정으로 이어집니다 — 멀쩡한 변경을 되돌리는 것은 새 변경을 하나 더 하는 것이고, 진짜 원인은 그대로 남습니다.

단계

  1. python3 /opt/fixtures/ic/ickit.py open timeline 으로 재료를 펼치고 /root/ic/timeline/README.txt 를 읽으세요. web-1.log 와 web-2.log 에서 상태 코드가 5xx 인 줄 수를 합쳐 /root/ic/timeline/answers/errors.txt 에 숫자 하나로 적습니다.
  2. deploys.log(git log)의 배포와 flags.log 의 플래그 변경을 모두 UTC 로 바꿔 오래된 것부터 /root/ic/timeline/answers/changes.tsv 에 <utc-time>\t<change-id> 한 줄씩 적습니다. 시각은 2026-11-04T05:12:03Z 꼴, 변경 id 는 배포면 커밋 sha 앞 7자, 플래그면 flag:<name> 입니다.
  3. lb-sample.log(기준 시계)와 두 서버 로그에 같은 req id 로 찍힌 요청을 짝지어, 서버마다 server_time − reference_time 을 정수 초로 재서 /root/ic/timeline/answers/skew.txt 에 web-1=<seconds>, web-2=<seconds> 두 줄로 적습니다(앞선 시계는 +, 뒤처진 시계는 −).
  4. 앞 단계의 어긋남을 빼서 두 서버의 5xx 를 기준 시계로 맞추고, 가장 이른 것의 시각(UTC, 초까지)과 서버를 /root/ic/timeline/answers/first-error.txt 에 first_error_utc=<time>, host=<server> 두 줄로 적습니다.
  5. 기준 시계의 첫 오류 전 30분(첫 오류 시각 포함) 안에 들어간 변경의 id 를 /root/ic/timeline/answers/suspects.txt 에 한 줄에 하나씩 적습니다.
  6. 기준 시계의 첫 오류부터 이 사고의 경보(alerts.csv 의 api-5xx-rate fired)까지 걸린 초와, README 에 정의된 완화 시각(분의 시작, UTC)을 /root/ic/timeline/answers/phases.txt 에 detect_delay_sec=<seconds>, mitigated_utc=<time> 두 줄로 적습니다.
  7. incident ic-error-spike 를 실행하면 사건이 납니다. cart·search·pay 세 서비스가 오늘 차례로 배포됐고 셋 다 그 무렵부터 5xx 가 이어집니다(기록 /srv/spike/, README 먼저). 서비스마다 오류를 시작시킨 것을 찾아, 되돌릴 것만 /srv/spike/rollback.conf 에 <service> app=<previous-version> 또는 <service> config=<previous-revision> 으로 적습니다(되돌릴 필요가 없는 서비스는 적지 않습니다). /root/incident/ic-error-spike/report.txt 에 cart: <code>, search: <code>, pay: <code> 세 줄과, 서비스마다 그 원인을 보여 주는 재료의 한 줄을 evidence-cart: <line>, evidence-search: <line>, evidence-pay: <line> 로 적습니다(원인이 된 변경의 줄, 또는 의존 서비스 지표·시간 초과 줄). 그리고 서비스마다 기준 시계(로드밸런서)로 맞춘 첫 5xx 시각을 start-cart: <utc-time>, start-search: <utc-time>, start-pay: <utc-time> 로 적습니다(두 서버 중 더 이른 것, ±10초까지 받습니다). 원인은 서비스마다 따로 정해져 있어 같은 원인이 여럿일 수도 있습니다. 코드는 서비스마다 다음 중 하나입니다: bad-deploy(새 앱 판의 결함), clock-skew(시계 어긋남이 만든 착시), config-push(설정 배포), traffic-surge(요청 급증), upstream-timeout(하위 의존 서비스 지연).

참고

재료 펼치기 — 5xx 는 몇 줄인가

python3 /opt/fixtures/ic/ickit.py open timeline 으로 재료를 펼치고 /root/ic/timeline/README.txt 를 읽으세요. web-1.log 와 web-2.log 에서 상태 코드가 5xx 인 줄 수를 합쳐 /root/ic/timeline/answers/errors.txt 에 숫자 하나로 적습니다.

상태 코드는 줄의 여섯째 칸입니다. 4xx 는 서버 잘못이 아니라 세지 않습니다.

변경을 UTC 한 줄로

deploys.log(git log)의 배포와 flags.log 의 플래그 변경을 모두 UTC 로 바꿔 오래된 것부터 /root/ic/timeline/answers/changes.tsv 에 <utc-time>\t<change-id> 한 줄씩 적습니다. 시각은 2026-11-04T05:12:03Z 꼴, 변경 id 는 배포면 커밋 sha 앞 7자, 플래그면 flag:<name> 입니다.

git 의 Date 는 커밋한 사람의 시간대(+0900·-0800)로 찍히고 최신이 위입니다. 그 꼴은 GNU date 가 못 읽으니 python 의 strptime("%a %b %d %H:%M:%S %Y %z")를 쓰세요. 날짜 글자가 아니라 순간으로 정렬해야 합니다.

서버 시계는 몇 초 어긋났나

lb-sample.log(기준 시계)와 두 서버 로그에 같은 req id 로 찍힌 요청을 짝지어, 서버마다 server_time − reference_time 을 정수 초로 재서 /root/ic/timeline/answers/skew.txt 에 web-1=<seconds>, web-2=<seconds> 두 줄로 적습니다(앞선 시계는 +, 뒤처진 시계는 −).

시간대 표시(+09:00, Z)를 먼저 읽어 같은 순간(에포크 초)으로 바꾼 뒤 뺍니다. 요청은 로드밸런서를 지난 뒤 1초 안에 서버에 닿으므로 여러 쌍의 가운데 값(median)을 반올림하면 됩니다.

기준 시계의 첫 오류

앞 단계의 어긋남을 빼서 두 서버의 5xx 를 기준 시계로 맞추고, 가장 이른 것의 시각(UTC, 초까지)과 서버를 /root/ic/timeline/answers/first-error.txt 에 first_error_utc=<time>, host=<server> 두 줄로 적습니다.

맞춘 시각 = 서버가 찍은 시각 − 그 서버의 어긋남. 시계가 뒤처진 서버의 줄을 그대로 읽으면 배포보다 먼저 오류가 난 것처럼 보입니다.

용의 변경 좁히기

기준 시계의 첫 오류 전 30분(첫 오류 시각 포함) 안에 들어간 변경의 id 를 /root/ic/timeline/answers/suspects.txt 에 한 줄에 하나씩 적습니다.

앞의 두 산출물(changes.tsv, first-error.txt)을 맞대면 됩니다. 맞추지 않은 시계의 첫 오류로 창을 잡으면 촉발 변경이 창 밖으로 밀립니다.

탐지 지연과 완화 시각

기준 시계의 첫 오류부터 이 사고의 경보(alerts.csv 의 api-5xx-rate fired)까지 걸린 초와, README 에 정의된 완화 시각(분의 시작, UTC)을 /root/ic/timeline/answers/phases.txt 에 detect_delay_sec=<seconds>, mitigated_utc=<time> 두 줄로 적습니다.

완화 시각은 metrics.csv 의 분당 오류율로 정의대로 찾습니다 — 처음 5% 이상이 된 분 뒤에서, 1% 미만이 5분 연속 이어지는 첫 분입니다. 사고 전의 조용한 분을 고르지 않게 주의하세요.

사건 — 세 서비스 모두 배포 직후 오류, 무엇을 되돌리나

incident ic-error-spike 를 실행하면 사건이 납니다. cart·search·pay 세 서비스가 오늘 차례로 배포됐고 셋 다 그 무렵부터 5xx 가 이어집니다(기록 /srv/spike/, README 먼저). 서비스마다 오류를 시작시킨 것을 찾아, 되돌릴 것만 /srv/spike/rollback.conf 에 <service> app=<previous-version> 또는 <service> config=<previous-revision> 으로 적습니다(되돌릴 필요가 없는 서비스는 적지 않습니다). /root/incident/ic-error-spike/report.txt 에 cart: <code>, search: <code>, pay: <code> 세 줄과, 서비스마다 그 원인을 보여 주는 재료의 한 줄을 evidence-cart: <line>, evidence-search: <line>, evidence-pay: <line> 로 적습니다(원인이 된 변경의 줄, 또는 의존 서비스 지표·시간 초과 줄). 그리고 서비스마다 기준 시계(로드밸런서)로 맞춘 첫 5xx 시각을 start-cart: <utc-time>, start-search: <utc-time>, start-pay: <utc-time> 로 적습니다(두 서버 중 더 이른 것, ±10초까지 받습니다). 원인은 서비스마다 따로 정해져 있어 같은 원인이 여럿일 수도 있습니다. 코드는 서비스마다 다음 중 하나입니다: bad-deploy(새 앱 판의 결함), clock-skew(시계 어긋남이 만든 착시), config-push(설정 배포), traffic-surge(요청 급증), upstream-timeout(하위 의존 서비스 지연).

방향: 같은 증상이라도 시작 패턴이 다릅니다 — 새 판이 들어간 서버부터 차례로인가, 모든 서버가 동시에인가, 의존 서비스 지연이 먼저 솟았는가. 시계를 맞추기 전에는 순서를 믿지 마세요. 도구: lb-sample.log 로 서버마다 어긋남 재기, git log 의 Date 를 strptime 으로, metrics 의 dep_p99_ms, 오류 줄의 상태 코드와 응답 시간. 구체: 서비스마다 두 서버의 첫 5xx 를 기준 시계로 맞추고, 그 시각을 그 서비스의 앱 배포·설정 배포 시각과 나란히 놓으세요. 되돌린다면 오류를 시작시킨 변경의 바로 앞 판(리비전)입니다.