영향받은 사람은 몇 명이었나 — 규칙으로 세는 영향
목표
결제 장애의 접근 로그에서 사용자가 아닌 요청을 걸러 내고, 재시도를 묶고, 사람을 규칙대로 세서 요청·실패·오류율·영향받은 사용자를 구합니다. 그 규칙을 스크립트로 만들어 처음 보는 로그에도 맞게 하고, 마지막에는 서로 다른 숫자를 내는 세 팀의 스크립트를 고칩니다.
왜 중요한가
영향 숫자는 상황 공지·보상·규제 보고·SLO 가 그대로 가져다 쓰는 값입니다. 그런데 같은 로그에서도 무엇을 빼고 무엇을 묶느냐에 따라 숫자가 몇 배씩 달라집니다. 헬스체크를 넣으면 장애가 실제보다 커 보이고, 요청 수를 사람 수로 쓰면 영향이 부풀고, 구간의 시간대를 버리면 엉뚱한 시간을 셉니다. 「누가 맞나」 를 다투기 전에 규칙을 맞추고, 규칙을 코드로 남기는 것이 이 실습의 핵심입니다.
단계
python3 /opt/fixtures/ic/ickit.py open impact로 재료를 펼치고/root/ic/impact/README.txt의 세는 규칙을 읽으세요.access.log에서 헬스체크(경로가 정확히/healthz또는/readyz) 줄 수를/root/ic/impact/answers/health.txt에 숫자 하나로 적습니다.- 로그 전체에서 헬스체크·봇(User-Agent 에 bot·crawler·spider, 대소문자 무시)·사내 대역(클라이언트 IP 가 10. 으로 시작)을 뺀 줄 수를
/root/ic/impact/answers/external.txt에 숫자 하나로 적습니다. - 사고 구간(
window.txt, KST)에 첫 시도가 든 바깥 요청의 수와, 그중 마지막 시도의 상태가 500 이상인 요청의 수를/root/ic/impact/answers/requests.txt에requests=<n>,failed=<n>두 줄로 적습니다(같은req의 줄은 한 요청). - 같은 규칙으로 사고 구간의 오류율과, 로그 처음부터 구간 시작 전까지의 평소 오류율을 퍼센트(소수 둘째 자리)로 계산해
/root/ic/impact/answers/rate.txt에error_rate=<percent>,baseline_rate=<percent>두 줄로 적습니다. - 구간에 실패한 요청이 하나라도 있는 사람의 수와 구간에 요청을 보낸 사람의 수를
/root/ic/impact/answers/users.txt에affected_users=<n>,active_users=<n>두 줄로 적습니다. 사람은user=로, 로그인하지 않은 요청(user=-)은session=하나를 한 명으로 셉니다. /root/ic/impact/impact.sh <log> <start-iso> <end-iso>가 README 규칙대로requests=<n>,failed=<n>,affected_users=<n>세 줄을 내게 만드세요(실행 권한 필요, 언어는 자유 — 첫 줄 셔뱅으로 정합니다). 구간 인자는2026-11-04T14:10:00+09:00꼴입니다. 채점기가 이 실습의 로그와, 그 자리에서 새로 만든 다른 로그·구간에도 돌려 봅니다.incident ic-impact-dispute를 실행하면 사건이 납니다. 같은 로그(/srv/impact/access.log)·같은 구간(/srv/impact/window.txt)인데/srv/impact/bin/statuspage·support·sre세 스크립트의affected_users가 서로 다릅니다. 세 스크립트를 README 규칙대로 고쳐 셋 다 같은 참값을 내게 하세요 (부르는 법은 그대로, 숫자를 적어 넣으면 다른 로그에서 떨어집니다)./root/incident/ic-impact-dispute/report.txt에statuspage: <code>,support: <code>,sre: <code>세 줄과evidence: <defective-line>을 적습니다. 코드는 스크립트마다 다음 중 하나입니다:includes-probes(헬스체크·봇·사내 대역을 빼지 않음),requests-as-users(실패 요청 수를 사람 수로),retries-double-counted(재시도 중 한 번이라도 실패면 실패로),sampling-gap(로그 표본이 빠짐),sessions-as-users(로그인한 사람도 세션마다 따로),window-timezone(구간 시각의 시간대를 버림).
참고
- 재료 도구:
python3 /opt/fixtures/ic/ickit.py open <name>이 재료를/root/ic/<name>/에 펼칩니다. 재료는 사람마다 다른 무작위 씨앗으로 만들어지고, 채점기는 같은 씨앗으로 정답을 다시 계산합니다 — 남의 숫자는 맞지 않습니다. 다시 펼쳐도 같은 재료이고,--fresh를 붙이면 새 재료로 처음부터입니다. - 답은
/root/ic/<name>/answers/아래에 씁니다. 세션이 끝나면 사라지니 필요하면 따로 보관하세요. - 사건 도구:
incident list로 낼 수 있는 사건과 한 줄 증상을,incident show <name>으로 티켓을 다시 봅니다. 같은 사건을 다시 내면 새 무작위 재료로 처음 고장 상태가 됩니다(앞서 적은 답은 맞지 않게 됩니다). - 보고서는
/root/incident/<name>/report.txt입니다. 대상마다<target>: <cause-code>한 줄씩, 마지막에evidence: <line>를 그대로 붙입니다. 증거는 재료나 관찰 기록에 그대로 있는 한 줄(20자 이상)이어야 합니다. 채점기는 고친 뒤의 실제 상태를 먼저 보고, 보고서는 판단을 확인하는 데 씁니다. - 로그 한 줄:
<utc-time> <IP> "<method> <path> <protocol>" <status> <bytes> "<User-Agent>" user=<id|-> session=<id|-> req=<id> attempt=<n>. User-Agent 안에 공백이 있으니 공백으로만 나누면 칸이 밀립니다 — 정규식이나 따옴표 구분이 안전합니다. - 구간 비교는 시간대가 붙은 시각끼리 합니다. python 의
datetime.fromisoformat은+09:00을 읽고,Z는+00:00으로 바꿔 넣으면 읽습니다. - 흔한 실수 둘 — 줄 수를 요청 수로 세는 것(재시도 중복), 그리고 실패 요청 수를 영향받은 사용자 수로 쓰는 것.
재료 펼치기 — 헬스체크는 몇 줄인가
python3 /opt/fixtures/ic/ickit.py open impact 로 재료를 펼치고 /root/ic/impact/README.txt 의 세는 규칙을 읽으세요. access.log 에서 헬스체크(경로가 정확히 /healthz 또는 /readyz) 줄 수를 /root/ic/impact/answers/health.txt 에 숫자 하나로 적습니다.
요청 줄은 따옴표 안에 메서드·경로·프로토콜이 있습니다. 공백으로 나누면 경로가 넷째 칸입니다.
바깥 사용자의 줄만 남기기
로그 전체에서 헬스체크·봇(User-Agent 에 bot·crawler·spider, 대소문자 무시)·사내 대역(클라이언트 IP 가 10. 으로 시작)을 뺀 줄 수를 /root/ic/impact/answers/external.txt 에 숫자 하나로 적습니다.
User-Agent 는 따옴표로 묶인 둘째 칸입니다 — 따옴표를 구분자로 쓰면(awk -F'"') 넷째 칸이 됩니다. 줄 전체에서 bot 을 찾으면 다른 칸에 걸릴 수 있습니다.
구간의 요청과 실패 — 재시도는 하나로
사고 구간(window.txt, KST)에 첫 시도가 든 바깥 요청의 수와, 그중 마지막 시도의 상태가 500 이상인 요청의 수를 /root/ic/impact/answers/requests.txt 에 requests=<n>, failed=<n> 두 줄로 적습니다(같은 req 의 줄은 한 요청).
로그는 UTC, 구간은 KST 입니다. 요청마다 첫 시도의 시각과 마지막 시도의 상태를 따로 들고 있어야 합니다 — python 의 dict 두 개가 편합니다.
오류율과 평소 오류율
같은 규칙으로 사고 구간의 오류율과, 로그 처음부터 구간 시작 전까지의 평소 오류율을 퍼센트(소수 둘째 자리)로 계산해 /root/ic/impact/answers/rate.txt 에 error_rate=<percent>, baseline_rate=<percent> 두 줄로 적습니다.
오류율 = 실패 요청 ÷ 요청 × 100. 분모와 분자가 같은 규칙(바깥 요청, 재시도는 한 요청)이어야 합니다. 비율(0.09)이 아니라 퍼센트(9.33)입니다.
영향받은 사람 세기
구간에 실패한 요청이 하나라도 있는 사람의 수와 구간에 요청을 보낸 사람의 수를 /root/ic/impact/answers/users.txt 에 affected_users=<n>, active_users=<n> 두 줄로 적습니다. 사람은 user= 로, 로그인하지 않은 요청(user=-)은 session= 하나를 한 명으로 셉니다.
실패 요청 수와 사람 수는 다릅니다. 로그인한 사람의 세션이 여럿이어도 한 명입니다.
규칙을 스크립트로
/root/ic/impact/impact.sh <log> <start-iso> <end-iso> 가 README 규칙대로 requests=<n>, failed=<n>, affected_users=<n> 세 줄을 내게 만드세요(실행 권한 필요, 언어는 자유 — 첫 줄 셔뱅으로 정합니다). 구간 인자는 2026-11-04T14:10:00+09:00 꼴입니다. 채점기가 이 실습의 로그와, 그 자리에서 새로 만든 다른 로그·구간에도 돌려 봅니다.
앞 세 단계의 계산을 인자를 받는 한 파일로 모으면 됩니다. 구간 인자의 시간대 표시를 버리지 마세요.
사건 — 세 팀의 영향 숫자가 서로 다르다
incident ic-impact-dispute 를 실행하면 사건이 납니다. 같은 로그(/srv/impact/access.log)·같은 구간(/srv/impact/window.txt)인데 /srv/impact/bin/statuspage·support·sre 세 스크립트의 affected_users 가 서로 다릅니다. 세 스크립트를 README 규칙대로 고쳐 셋 다 같은 참값을 내게 하세요 (부르는 법은 그대로, 숫자를 적어 넣으면 다른 로그에서 떨어집니다). /root/incident/ic-impact-dispute/report.txt 에 statuspage: <code>, support: <code>, sre: <code> 세 줄과 evidence: <defective-line> 을 적습니다. 코드는 스크립트마다 다음 중 하나입니다: includes-probes(헬스체크·봇·사내 대역을 빼지 않음), requests-as-users(실패 요청 수를 사람 수로), retries-double-counted(재시도 중 한 번이라도 실패면 실패로), sampling-gap(로그 표본이 빠짐), sessions-as-users(로그인한 사람도 세션마다 따로), window-timezone(구간 시각의 시간대를 버림).
방향: 세 스크립트는 같은 틀에서 갈라졌습니다. README 의 다섯 규칙과 한 줄씩 맞대면 어긋난 자리가 스크립트마다 하나씩 나옵니다. 도구: diff(두 스크립트를 나란히), python3 -X 없이도 print 로 중간값 보기, 앞 단계의 impact.sh. 구체: 걸러 내는 조건(경로·User-Agent·IP), 마지막 시도를 고르는 줄, 사람을 모으는 자료형(집합인가 목록인가, 무엇을 넣는가), 구간 인자를 읽는 줄을 차례로 보세요. 고친 뒤 셋을 같은 인자로 돌려 숫자가 하나로 모이는지 확인합니다.