Devin.KR

SLO와 오류 예산

75분 안팎

학습 목표

측정 결과와 목표·허용 실패량을 구분합니다.

개념

목표는 측정값과 역할이 다릅니다

SLI가 지난 구간을 설명한다면 SLO는 정한 기간에 도달하려는 서비스 목표입니다. 안내판 조회의 28일 성공률을 99% 이상으로 유지한다는 문장은 기간·작업·목표를 함께 담습니다. 이번 99%는 계산을 연습하기 위한 교육 값이며 실제 서비스 권장치가 아닙니다. 제품 담당자와 사용자 기대·운영 능력을 확인하고 합의하는 과정이 필요합니다. 이미 측정한 값에 맞춰 목표를 낮추면 위험 판단 기준이 사라집니다. 이번 레슨의 산출물은 목표 문서와 그 목표에 대한 건수 계산기입니다.

기간을 붙여서 읽습니다

같은 99%라도 하루와 28일은 다른 목표입니다. 짧은 구간의 장애가 긴 기간에 희석되거나 최근 악화가 평균에 가려질 수 있습니다. 구간 끝이 움직이는 롤링 기간과 달력상 고정 기간 중 무엇을 쓰는지 문서에 남깁니다. 이번 정책은 28일 롤링 기간을 가정하지만 실습 입력은 이미 선택된 구간의 집계 건수입니다. 프로그램이 달력을 읽거나 28일 자료를 자동 수집하지 않습니다. m08의 5건 표본을 넣어 나온 결과는 그 표본에 대한 계산이고 기간 전체 목표 준수의 증거는 아닙니다.

SLO와 SLA를 구별합니다

내부 운영 목표를 정하는 SLO와 외부 약속·보상 조건 등이 포함될 수 있는 SLA는 역할이 다릅니다. SLO를 숫자 하나 적는 것으로 SLA 계약을 만든 것으로 보지 않습니다. 이 트랙에서는 유료 계정이나 계약을 다루지 않고 팀이 사용할 교육 정책을 작성합니다. 목표를 넘지 못했을 때 배포 위험을 줄이고 관측을 강화하는 합의가 필요합니다. 목표에 맞는 실제 사용자 경험인지 확인하는 책임도 남습니다. 성공률만 좋고 응답이 너무 늦다면 별도 지연 지표와 목표를 검토합니다.

오류 예산을 요청 건수로 계산합니다

목표 비율을 s, 유효 요청 수를 N이라고 하면 이 구간에서 허용하는 실패의 실수량은 N 곱하기 (1-s)입니다. 실패는 정수 건수이므로 충족 가능한 최대 실패 수는 이 값의 바닥값입니다. 99% 목표에 100건이면 1건까지 허용하고 101건이면 여전히 1건입니다. 2건을 허용하려면 그 비율이 목표를 만족하는지 다시 계산해야 합니다. 요청 기반 예산은 사용 시간의 분 수와 단위가 다릅니다. 요청량이 불균일하면 한 번의 장애가 소비하는 건수도 달라지므로 시간 예산으로 자동 바꾸지 않습니다.

목표를 정수 단위로 표현합니다

브라우저 입력 target_bps는 1부터 10000까지의 정수입니다. 10000은 100%, 9900은 99%, 9990은 99.9%입니다. bps 한 단위는 비율 0.0001이며 퍼센트 표기로 0.01%포인트입니다. 이름이 비슷한 네트워크 bit per second와 다른 의미이므로 정책 문서에 단위를 적습니다. 목표를 float으로 읽어 곱한 뒤 내리면 표현 오차가 정수 경계에 영향을 줄 수 있습니다. 여기서는 N 곱하기 (10000-target_bps)를 10000으로 정수 나눗셈하여 허용 건수를 정확하게 얻습니다.

사용량과 잔량을 구분합니다

사용한 오류 예산은 bad라는 실패 건수이며 남은 값 remaining은 allowed에서 bad를 뺀 값입니다. 허용 1건에 실패 2건이면 잔량은 -1입니다. 음수는 목표 초과 정도를 보여 주므로 0으로 잘라 숨기지 않습니다. 0이면 예산이 경계까지 사용되었지만 이 건수 기준 목표는 충족합니다. 잔량이 양수여도 다음 배포가 안전하다는 보증은 아닙니다. 앞으로의 요청량과 실패율을 알 수 없기 때문입니다. 화면에는 정책·기간·분모·실패·허용량·잔량을 함께 보여 해석의 근거를 남깁니다.

관측 구간의 총량은 미래 예산이 아닙니다

28일 기간이 아직 끝나지 않았다면 현재까지의 N으로 산출한 허용량은 지금까지의 자료에 대한 값입니다. 요청이 더 들어오면 총량과 허용량이 함께 바뀝니다. 이 값만으로 앞으로 몇 건 더 실패해도 된다고 보장하지 않습니다. 이전 기간의 트래픽을 바탕으로 전망할 수는 있지만 전망과 측정을 구분해야 합니다. 롤링 창에서는 오래된 실패가 창 밖으로 빠져 잔량이 변하기도 합니다. 과제는 주어진 집계의 산술을 구현하며 다음 달 요청 수를 예측하거나 목표 정책을 자동으로 바꾸지 않습니다.

목표 충족 판정은 정수로 합니다

bad가 allowed 이하이면 MET, 더 크면 MISSED입니다. 분모 0은 NO_DATA이며 성공률을 100%로 넣지 않습니다. 100% 목표에서는 허용 실패가 0건이므로 유효 실패 한 건만 있어도 MISSED입니다. 표시 반올림으로 99.00%가 보여도 실제 비율이 조금 낮다면 목표를 넘지 못할 수 있습니다. 목표 충족의 비교는 표시값 대신 정수 건수로 수행합니다. 예를 들어 목표 9999에 10001건 중 2건 실패라면 허용량은 1건입니다. 반올림된 성공률 숫자가 목표와 비슷하게 보이는 것에 속지 않습니다.

입력 거부도 계산기의 책임입니다

total과 bad는 0 이상 정수이며 bad가 total보다 클 수 없습니다. target_bps가 0이거나 10000을 넘거나 문자열이면 INVALID입니다. 음수 건수를 절댓값으로 바꾸거나 실패를 total에 맞춰 줄이지 않습니다. 원천 자료의 모순은 계산기 밖에서 수정하고 새 증거를 남깁니다. 빈 표준 입력은 목표도 없으므로 INVALID입니다. 앞 레슨의 빈 이벤트 NO_DATA와 달리 이 레슨은 정책이 없는 입력을 계산할 수 없습니다. 오류 메시지를 읽을 때 자료 없음과 정책 누락을 구별하는 연습입니다.

예산 정책을 행동으로 연결합니다

잔량이 줄면 어떤 결정을 할지 합의해야 숫자가 실제 운영에 쓰입니다. 교육 정책은 MISSED일 때 기능 변경의 위험을 검토하고 사용자 영향·복구 우선순위를 확인합니다. 긴급 복구 변경까지 일괄 금지하는 정책을 만들면 오히려 장애가 길어질 수 있습니다. 개선 작업과 위험한 기능 배포를 구분하여 담당자 판단을 남깁니다. 예산을 전부 소모해도 괜찮다는 뜻이나 실패를 할당받았다는 뜻으로 읽지 않습니다. 경보는 이 정책에 따라 신속한 조사가 필요한 상황을 다음 레슨에서 탐지합니다.

따라하기의 숫자를 검산합니다

먼저 100건·99%·실패 1건의 경계를 계산합니다. 다음으로 101건에서 내림이 유지되는지 보고 실패 2건의 음수 잔량을 확인합니다. 끝으로 100% 목표와 분모 0을 시험합니다. ZeroDivisionError는 비율 계산 전에 분모를 검사하지 않은 징후입니다. TypeError는 문자열과 정수의 연산을 섞었을 가능성을 확인합니다. 과제에서는 JSON 스키마 검사 후 계산하여 INVALID와 NO_DATA를 구분합니다. print에 allowed remaining state를 한 칸씩 띄워 출력하고 퍼센트 문자열은 채점 결과에 덧붙이지 않습니다.

교육 표본으로 과장하지 않습니다

미션 보고서에는 sample_only와 educational samples, not 28-day compliance라는 범위 설명을 포함합니다. 정상 표본 5건은 성공이지만 긴 기간의 모든 사용자가 성공했다고 말할 수 없습니다. 오류 표본 5건은 해당 주입 창이 실패했다는 사실이며 실제 운영 장애 통계가 아닙니다. 정책 버전과 관측 범위를 남겨 동료가 무엇을 계산했는지 재현하게 합니다. 숫자의 타입과 정수 나눗셈에 익숙하지 않다면 더 읽기의 자료형 장을 확인합니다. 이번 목표는 정의된 기간·목표·건수로 허용량과 초과량을 스스로 설명하는 것입니다.

목표와 오류 예산 정책의 팀 합의에 관한 배경은 Google SRE의 SLO 구현 안내에서 확인할 수 있습니다. 본문의 99%와 경보 임계값은 이 트랙의 교육 예시입니다.

따라하기

목표 단위 확인

정수 목표를 퍼센트 표시로 바꾸어 단위를 확인합니다.

for target in [9900,9990,10000]:
    print(target,f'{target/100:.2f}%')

실행 결과

9900 99.00%
9990 99.90%
10000 100.00%

허용량 내림 확인

99% 목표의 요청 건수별 최대 허용 실패를 비교합니다.

for total in [100,101,199,200]:
    print(total,total*(10000-9900)//10000)

실행 결과

100 1
101 1
199 1
200 2

경계와 초과량 계산

표시 반올림 대신 정수 건수로 판단합니다.

allowed=100*(10000-9900)//10000
for bad in [0,1,2]:
    print(allowed,allowed-bad,'MET' if bad<=allowed else 'MISSED')

실행 결과

1 1 MET
1 0 MET
1 -1 MISSED

분모 0과 100% 목표

100% 목표에서도 분모가 없으면 준수 여부를 판정하지 않습니다.

for total,bad in [(0,0),(5,0),(5,1)]:
    allowed=total*(10000-10000)//10000
    state='NO_DATA' if total==0 else ('MET' if bad<=allowed else 'MISSED')
    print(allowed,allowed-bad,state)

실행 결과

0 0 NO_DATA
0 0 MET
0 -1 MISSED

확인 문제

실습

target_bps(1~10000 정수), total(0 이상 정수), bad(0~total 정수)를 JSON 객체로 입력받습니다. target_bps=9900은 99%입니다. allowed=total*(10000-target_bps)//10000, remaining=allowed-bad입니다. allowed remaining state를 출력합니다. total=0이면 NO_DATA, bad가 allowed 이하이면 MET, 넘으면 MISSED입니다. 음수 잔량을 유지합니다. 형식 오류·범위 위반·빈 표준 입력은 INVALID입니다. 기간 집계가 이미 끝난 입력이며 프로그램이 기간을 수집하지 않습니다.

모범 답안
import sys,json
try:
    d=json.load(sys.stdin)
    if not isinstance(d,dict) or any(type(d.get(k)) is not int for k in ['target_bps','total','bad']):raise ValueError()
    t,n,b=d['target_bps'],d['total'],d['bad']
    if not 1<=t<=10000 or n<0 or not 0<=b<=n:raise ValueError()
    allowed=n*(10000-t)//10000
    state='NO_DATA' if n==0 else ('MET' if b<=allowed else 'MISSED')
    print(allowed,allowed-b,state)
except (ValueError,TypeError,KeyError):print('INVALID')

더 읽기

면접 질문

  • SLI 측정값과 SLO 목표의 차이를 요청 건수 예제로 설명하나요?
  • 오류 예산을 모두 쓴 경우 어떤 운영 결정을 합의하나요?