LabHub
学习 学习路径 课程

Loki — 不索引日志的日志库

用日志算 p95,结果和 p50 是同一个数

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

LogQL 의 두 가지 지표 질의를 직접 던져 로그를 숫자로 바꾸고, 추출된 라벨이 계열을 쪼개 분위수를 무의미하게 만드는 현상을 재현한 뒤 고칩니다.

왜 중요한가

지표가 없는 서비스의 지연이나 오류율을 급히 알아야 할 때, 로그는 이미 거기 있다. LogQL 의 지표 질의는 그 로그를 숫자로 바꿔 주지만 한 가지 함정이 있다 — 계열의 정체성을 정하는 라벨에 그 질의에서 파서가 만든 라벨까지 들어간다는 것이다. 값이 줄마다 다른 필드가 하나라도 추출되면 계열이 줄 수만큼 쪼개지고, 계열마다 표본이 하나뿐이니 어느 분위수를 물어도 같은 값이 나온다. 오류도 경고도 없이 그래프만 멀쩡하게 그려지기 때문에 몇 달씩 안 들킨다. 그리고 로그 기반 지표는 질의할 때마다 원본을 읽으므로, 대시보드에 걸기 전에 읽는 양을 한 번 재 보는 습관이 필요하다.

단계

  1. /root/lk-metrics 에서 Loki 를 띄우고 date +%s/root/lk-metrics/anchor.txt 에 적은 뒤 python3 /opt/lab/d5/gen.py metrics "$(cat anchor.txt)" 로 자료를 넣으세요. 그리고 기준 시각을 time 으로 주고 sum by (app) (count_over_time({app=~"checkout|search"}[1h])) 를 던져, 두 서비스의 줄 수를 /root/lk-metrics/01-boot.txtcheckout=<정수>search=<정수> 두 줄로 적으세요.
  2. checkout 서비스에서 status 가 500 인 줄의 초당 발생률을 한 시간 구간으로 구하세요. 질의는 /root/lk-metrics/02-rate.logql, 값은 /root/lk-metrics/02-rate.txtrate=<소수 여섯째 자리> 한 줄로 적습니다. 결과가 한 계열로 나오게 바깥을 sum(...) 으로 묶으세요.
  3. checkout 의 한 시간 오류 비율(500 인 줄 ÷ 전체 줄)을 구해 /root/lk-metrics/03-ratio.txtratio=<소수 여섯째 자리> 한 줄로 적으세요. 질의는 /root/lk-metrics/03-ratio.logql 에 씁니다. 분자와 분모를 각각 sum(...) 으로 묶어 나누어야 계열이 맞아떨어집니다.
  4. quantile_over_time(0.50, ...)quantile_over_time(0.95, ...){app="checkout"} | logfmt | unwrap dur_ms [1h] 에 걸어 각각 던지세요. 두 질의가 돌려준 계열 수와 첫 계열의 값을 /root/lk-metrics/04-trap.txt 에 네 줄로 적습니다 — series=<정수>, p50_first=<숫자>, p95_first=<숫자>, lines=<그 구간의 전체 줄 수>.
  5. 같은 두 분위수를 한 계열로 나오게 고쳐 던지세요. 질의는 /root/lk-metrics/05-p50.logql/root/lk-metrics/05-p95.logql 에 각각 쓰고, 값은 /root/lk-metrics/05-fix.txtp50=<숫자>·p95=<숫자>·series=<정수> 세 줄로 적습니다. 두 값이 확실히 갈라져야 합니다.
  6. /root/lk-metrics/compare.tsv 를 만드세요. 머리글 없이 두 줄이고 각 줄은 탭으로 나눈 네 칸 <서비스><탭><줄수><탭><오류비율><탭><p95> 입니다. 서비스는 순서대로 checkout, search 이고 오류 비율은 소수 여섯째 자리, p95 는 소수 없이 반올림한 정수로 적습니다.
  7. 5단계의 p95 질의를 query_range 로 한 시간 구간에 던져 응답 통계의 읽은 바이트를 재고, /root/lk-metrics/07-cost.txt 에 세 줄로 적으세요 — bytes_per_query=<정수>, refresh_sec=30, bytes_per_day=<정수>. 하루치는 30초마다 한 번씩 돌린다고 보고 bytes_per_query × 2880 으로 계산합니다.
  8. /root/lk-metrics/08-decide.txt 에 세 줄을 적으세요. choice= 뒤에 log 또는 metric 중 하나, evidence= 뒤에 앞 단계에서 잰 숫자 두 개 이상을 담은 한 줄, reason= 뒤에 왜 그 선택인지 공백 뺀 60자 이상으로 적습니다. 정답은 하나가 아니지만 근거는 앞 단계의 실측값이어야 합니다.

참고

두 서비스의 로그를 넣고 줄부터 센다

/root/lk-metrics 에서 Loki 를 띄우고 date +%s/root/lk-metrics/anchor.txt 에 적은 뒤 python3 /opt/lab/d5/gen.py metrics "$(cat anchor.txt)" 로 자료를 넣으세요. 그리고 기준 시각을 time 으로 주고 sum by (app) (count_over_time({app=~"checkout|search"}[1h])) 를 던져, 두 서비스의 줄 수를 /root/lk-metrics/01-boot.txtcheckout=<정수>search=<정수> 두 줄로 적으세요.

지표 질의는 query_range 가 아니라 /loki/api/v1/query 로 던지고 time 을 나노초로 줍니다. 응답의 resultType 이 로그 질의와 다른 것도 한 번 보세요. sum by (app) 가 계열을 서비스별로 묶어 줍니다.

줄을 세는 질의 — 초당 오류

checkout 서비스에서 status 가 500 인 줄의 초당 발생률을 한 시간 구간으로 구하세요. 질의는 /root/lk-metrics/02-rate.logql, 값은 /root/lk-metrics/02-rate.txtrate=<소수 여섯째 자리> 한 줄로 적습니다. 결과가 한 계열로 나오게 바깥을 sum(...) 으로 묶으세요.

rate 는 구간 안의 줄 수를 구간의 초로 나눕니다. 500 만 세려면 파서로 상태 코드를 꺼내 라벨 필터를 걸어야 합니다. 값이 아주 작게 나오는 것이 정상입니다 — 한 시간에 몇 건이니까요.

비율은 두 지표 질의를 나눈다

checkout 의 한 시간 오류 비율(500 인 줄 ÷ 전체 줄)을 구해 /root/lk-metrics/03-ratio.txtratio=<소수 여섯째 자리> 한 줄로 적으세요. 질의는 /root/lk-metrics/03-ratio.logql 에 씁니다. 분자와 분모를 각각 sum(...) 으로 묶어 나누어야 계열이 맞아떨어집니다.

분모에는 파서가 필요 없습니다 — 전체 줄을 세면 됩니다. 분자와 분모의 라벨 집합이 다르면 나눗셈이 빈 결과를 내니, 양쪽을 sum(...) 으로 감싸 라벨을 모두 지우는 것이 가장 단순한 방법입니다.

p50 과 p95 가 똑같이 나온다

quantile_over_time(0.50, ...)quantile_over_time(0.95, ...){app="checkout"} | logfmt | unwrap dur_ms [1h] 에 걸어 각각 던지세요. 두 질의가 돌려준 계열 수와 첫 계열의 값을 /root/lk-metrics/04-trap.txt 에 네 줄로 적습니다 — series=<정수>, p50_first=<숫자>, p95_first=<숫자>, lines=<그 구간의 전체 줄 수>.

계열 수는 응답의 data.result 배열 길이입니다. 그 숫자를 1단계에서 센 줄 수와 견주어 보세요. 왜 두 분위수가 같은 값을 내는지는 그 비교에서 바로 보입니다. logfmt 가 이 로그에서 만든 라벨이 무엇무엇인지도 결과의 metric 에서 확인하세요.

라벨을 정리하면 분위수가 갈린다

같은 두 분위수를 한 계열로 나오게 고쳐 던지세요. 질의는 /root/lk-metrics/05-p50.logql/root/lk-metrics/05-p95.logql 에 각각 쓰고, 값은 /root/lk-metrics/05-fix.txtp50=<숫자>·p95=<숫자>·series=<정수> 세 줄로 적습니다. 두 값이 확실히 갈라져야 합니다.

값이 줄마다 다른 라벨을 떼어 내거나, 쓸 라벨만 남기거나, 바깥을 집계로 감싸면 됩니다. 세 방법 중 어느 것을 써도 좋습니다 — 다만 계열이 하나가 되어야 합니다. 어느 라벨이 범인인지는 4단계 결과의 metric 을 보면 압니다.

두 서비스를 한 표로 견준다

/root/lk-metrics/compare.tsv 를 만드세요. 머리글 없이 두 줄이고 각 줄은 탭으로 나눈 네 칸 <서비스><탭><줄수><탭><오류비율><탭><p95> 입니다. 서비스는 순서대로 checkout, search 이고 오류 비율은 소수 여섯째 자리, p95 는 소수 없이 반올림한 정수로 적습니다.

3단계와 5단계의 질의에서 서비스 이름만 바꾸면 됩니다. 같은 자료라도 두 서비스의 꼬리 모양이 다르다는 것을 표에서 확인하세요 — 평균이 아니라 p95 를 봐야 보이는 차이입니다.

응용 ① — 이 질의를 패널로 걸면 얼마를 읽나

5단계의 p95 질의를 query_range 로 한 시간 구간에 던져 응답 통계의 읽은 바이트를 재고, /root/lk-metrics/07-cost.txt 에 세 줄로 적으세요 — bytes_per_query=<정수>, refresh_sec=30, bytes_per_day=<정수>. 하루치는 30초마다 한 번씩 돌린다고 보고 bytes_per_query × 2880 으로 계산합니다.

지표 질의도 query_range 로 던질 수 있고, 그때 step 을 줍니다. 통계는 로그 질의와 같은 자리(data.stats.summary)에 있습니다. 하루 2880번은 30초 주기의 하루 횟수입니다 — 대시보드 패널 하나가 사람보다 훨씬 많이 질의한다는 뜻입니다.

응용 ② — 로그로 계속 잴 것인가, 지표로 내보낼 것인가

/root/lk-metrics/08-decide.txt 에 세 줄을 적으세요. choice= 뒤에 log 또는 metric 중 하나, evidence= 뒤에 앞 단계에서 잰 숫자 두 개 이상을 담은 한 줄, reason= 뒤에 왜 그 선택인지 공백 뺀 60자 이상으로 적습니다. 정답은 하나가 아니지만 근거는 앞 단계의 실측값이어야 합니다.

두 길의 값이 다릅니다. 로그 기반은 계측을 안 고쳐도 되고 과거를 되짚을 수 있지만 질의마다 원본을 읽습니다. 지표 기반은 싸고 빠르지만 미리 내보내야 하고 과거는 없습니다. 7단계의 하루 읽는 양과 4·5단계에서 겪은 함정을 근거로 쓰면 설득력이 생깁니다.