CNPE — 클라우드 네이티브 플랫폼 엔지니어 (전문가) · 지표는 정상인데 사용자는 실패하는 날 · 이론
성공률 100%라는 거짓말의 분모
한 줄 요약
SLI의 첫 질문은 수식이 아니라 무엇을 한 건으로 세고, 어떤 실패를 놓치고 있는가입니다. 잘못 계측한 데이터를 정확히 나누어도 올바른 사용자 경험 지표가 되지는 않습니다.
왜 이게 필요했나
가상의 셀프서비스 플랫폼이 데이터베이스 발급 API를 제공합니다. 성공하면 201을 반환하고, 의존 서비스 장애면 503을 반환합니다. 담당자는 성공 핸들러 끝에서만 카운터를 늘렸습니다. 정상 시도 2건과 실패 시도 2건을 보내도 지표에는 성공 2건과 전체 2건만 남습니다. 대시보드의 성공률은 100%지만 실제 응답 기록은 50%입니다. 이때 임계값을 99.9%에서 99.99%로 올리는 것으로는 문제를 발견할 수 없습니다.
이어질 실습은 이 사건을 재현합니다. 가짜 이미지 캡처가 아니라 파드 안에서 Python HTTP 서버를 실행하고 201·503·504를 실제로 받습니다. 장애는 X-Lab-Scenario라는 실습 전용 헤더로 고릅니다. 실제 외부 데이터베이스를 중단시키지는 않습니다. 이처럼 진짜로 실행하는 층과 가상으로 설정한 원인을 나누어 설명해야 증거의 범위를 알 수 있습니다.
어떻게 동작하나
한 건의 단위를 먼저 결정한다
[Google SRE의 SLO 구현 안내](https://sre.google/workbook/implementing-slos/)는 사용자가 중요하게 여기는 행위를 기준으로 측정 가능한 지표와 목표를 설계하는 출발점을 제공합니다. 여기서는 발급 API의 HTTP 시도 한 건을 단위로 정합니다. 실패한 시도 a 뒤에 성공한 재시도 b가 있으면 두 건입니다. “최종적으로 발급됐으니 성공 한 건”으로 바꾸면 시도 기반 지표와 사용자 작업 기반 지표가 섞입니다. 둘 중 하나가 언제나 옳은 것이 아니라 서로 다른 질문에 답합니다.
실습의 scope.json은 /provision의 2xx·5xx만 유효 시도로 삼고 4xx와 상태 점검 경로는 제외하는 가상 계약입니다. 운영에서는 이 분류를 그대로 복사하지 마세요. 예를 들어 서비스의 용량 부족 때문에 429가 발생한다면 사용자의 잘못이라고 제외하는 것은 부당할 수 있습니다. 요청의 유효성과 서비스 책임을 합의하고, 제외 비율도 관측해야 지표를 좋게 보이도록 바꾸는 일을 막을 수 있습니다.
좋은 가용성과 좋은 지연을 나눈다
실습에서 eligible은 분모에 들어갈 시도, available은 그중 2xx, fast는 그중 성공하면서 서버 처리 시간이 1000ms 이하인 응답입니다. 1000ms는 포함하고 1000.1ms는 제외합니다. 빠른 503은 fast가 아닙니다. 느린 201은 available이지만 fast가 아닙니다. 두 SLI는 같은 분모를 사용하지만 분자가 다릅니다.
| 실습 사건 | eligible | available | fast |
| --- | --- | --- | --- |
| 정상 201, 10ms | 참 | 참 | 참 |
| 느린 201, 1100ms | 참 | 참 | 거짓 |
| 실패 503, 10ms | 참 | 거짓 | 거짓 |
| 입력 오류 400 | 거짓 | 거짓 | 거짓 |
측정 위치도 명시합니다. 서버 처리 시간에는 클라이언트 네트워크 왕복과 연결 대기가 모두 포함되지는 않습니다. 이 실습이 서버 시간을 재었다는 사실을 사용자 전체 응답 시간의 증명으로 확대하지 마세요. 실제 목표가 브라우저에서 보이는 지연이라면 그 위치의 관측도 필요합니다.
실패를 직접 보내고 두 기록을 대조한다
[Prometheus 계측 원칙](https://prometheus.io/docs/practices/instrumentation/)은 카운터·실패·레이블 설계를 함께 생각할 근거입니다. 실습 서버는 지표에 사용자 ID나 요청 ID를 넣지 않고 observed·eligible·available·fast 네 종류만 노출합니다. 요청 ID는 고유 사건을 추적하는 로그에서 다루고, 지표 레이블은 제한된 집합으로 유지합니다. 사용자가 늘어날 때 사용자별 시계열도 끝없이 늘어나는 설계를 피하기 위해서입니다.
HTTP 재연은 응답 일곱 건과 observed=7을 맞추고, 그중 분모 4건·가용 성공 2건·빠른 성공 1건을 대조합니다. /metrics 수집 자체를 업무 요청에 다시 세지 않습니다. 지표가 100%라는 출력만 확인하는 것이 아니라 실패를 보낸 사실과 실패가 분모에 들어간 사실을 연결합니다. 현재 코드로 다시 실행하므로 오래된 정상 결과 파일만 제출해서는 충분하지 않습니다.
현장에서 만나는 모습
또 다른 가상 사건에서는 수집기가 같은 로그를 두 번 보냈습니다. attempt_id도 내용도 같다면 수집 중복입니다. 한 번만 분류하고 중복 개수는 별도로 남깁니다. 같은 ID인데 상태 코드가 다르다면 어느 쪽을 믿을지 모르므로 ValueError로 충돌을 드러냅니다. 임의로 마지막 값을 고르면 실제 실패가 성공으로 덮일 수 있습니다. 반면 다른 ID로 실행한 재시도는 별개의 시도이므로 둘 다 셉니다.
이 규칙은 작은 실습용 JSON 사건에 대한 것입니다. 운영의 여러 서비스가 ID를 발급한다면 충돌 방지 범위와 보존 기간도 필요합니다. 이번 과제가 분산 환경에서의 정확히 한 번 처리나 영구 중복 제거를 구현했다고 주장하지 않습니다.
다음에 읽을 것
이제 분모가 검증된 데이터로 얼마의 실패를 허용할지, 데이터가 비었을 때 무엇을 결정할 수 없는지 읽습니다. 이어질 실습에서는 분류 함수와 실제 HTTP, 중복 처리, 오류 예산, 직접 설계한 반례를 연결합니다.