Loki — A Log Store That Does Not Index Logs
Two queries with the same answer: one took four minutes, the other two seconds
한국어 원문으로 표시합니다.
목표
Loki 응답에 실려 오는 stats.summary 를 읽어 질의가 실제로 몇 줄·몇 바이트를 읽었는지 재고, 무엇이 그 숫자를 줄이고 무엇이 줄이지 않는지 직접 확인합니다.
왜 중요한가
Loki 의 색인에는 라벨 조합과 청크의 시간 범위만 들어 있다. 본문은 색인되지 않는다. 그래서 질의는 늘 같은 순서로 돈다 — 선택자가 열 청크를 고르고, 시간 구간이 그중 겹치는 것만 남기고, 남은 것을 전부 읽어 필터를 적용한다. 앞의 두 단계만 읽는 양을 정하고 뒤는 이미 읽은 것을 버리는 일이다. 이 순서를 모르면 줄 필터를 앞에 두거나 limit 을 줄이면서 질의가 빨라지기를 기다리게 된다. 비용이 바이트로 매겨지는 저장소에서는 이 차이가 곧 청구서다.
단계
/root/lk-filters에서 Loki 를 띄우고date +%s를/root/lk-filters/anchor.txt에 적은 뒤python3 /opt/lab/d5/gen.py filters "$(cat anchor.txt)"로 자료를 넣으세요. 그리고{app=~"edge|cart|ship|batch"}를 기준 시각에서 과거 한 시간 구간으로 질의해 응답의stats.summary에서 읽은 줄 수와 바이트를/root/lk-filters/01-base.txt에processed=<정수>와bytes=<정수>두 줄로 적으세요.- 같은 한 시간 구간에서 네 스트림 전체를 대상으로 본문에
SETTLE-LATE가 든 줄을 찾는 질의를/root/lk-filters/02-marker.logql에 쓰고, 결과 줄 수와 읽은 줄 수를/root/lk-filters/02-marker.txt에lines=<정수>와processed=<정수>두 줄로 적으세요. - 같은 답(같은 줄)을 내면서 스트림 선택자만 좁힌 질의를
/root/lk-filters/03-stream.logql에 쓰고, 결과 줄 수와 읽은 줄 수를/root/lk-filters/03-stream.txt에lines=<정수>·processed=<정수>로 적으세요. 그리고/root/lk-filters/03-ratio.txt에ratio=<소수 둘째 자리>한 줄로 2단계 대비 읽은 줄 수의 비율을 적습니다. - 3단계와 같은 질의를 구간만 30분(기준 시각에서 과거 1800초)으로 바꿔 던지세요. 결과 줄 수와 읽은 줄 수를
/root/lk-filters/04-window.txt에lines=<정수>·processed=<정수>로 적고,/root/lk-filters/04-note.txt에tradeoff=로 시작하는 한 문장(공백 뺀 40자 이상)을 적어 구간을 좁힐 때 무엇을 얻고 무엇을 잃는지 자기 말로 쓰세요. - 네 스트림 전체·한 시간 구간에서 두 질의를 던져 읽은 줄 수를 견주세요. 하나는 필터가 전혀 없는
{app=~"edge|cart|ship|batch"}, 다른 하나는 거기에 줄 필터 두 개를 더한 질의입니다. 결과를/root/lk-filters/05-linefilter.txt에 네 줄로 적습니다 —plain_processed=,plain_lines=,filtered_processed=,filtered_lines=. - 네 스트림 전체·한 시간 구간에
| logfmt | status="500"을 붙인 질의를 던져 세 숫자를/root/lk-filters/06-parser.txt에 적으세요 —processed=<읽은 줄 수>,post_filter=<필터를 통과해 남은 줄 수>,lines=<결과 줄 수>. 세 숫자를 1단계 기준선과 견주어, 무엇이 같고 무엇이 달라졌는지 보세요. - 지금까지 잰 것을
/root/lk-filters/cost.tsv로 정리하세요. 머리글 없이 네 줄이고 각 줄은 탭으로 나눈 세 칸<방법><탭><읽은줄수><탭><결과줄수>입니다. 방법 이름은 순서대로all(2단계),stream(3단계),window(4단계),parser(6단계)입니다. /root/lk-filters/08-budget.logql에 질의를 하나 쓰세요. 조건은 셋입니다 — (1) 기준 시각에서 과거 한 시간 구간으로 던졌을 때 2단계와 똑같은 줄을 돌려줄 것, (2) 읽은 줄 수가 1단계 기준선의 3분의 1 이하일 것, (3)limit에 기대지 말 것. 그리고/root/lk-filters/08-budget.txt에processed=<정수>한 줄로 그 질의가 읽은 줄 수를 적으세요.
참고
- 작업 디렉터리는
/root/lk-filters입니다. Loki 는 1단계에서 직접 띄웁니다. - 자료 생성기는
/opt/lab/d5/gen.py이고filters자료를 씁니다. 채점기는 이 파일을 읽지 않습니다. - 통계는
curl ... | jq '.data.stats.summary'로 봅니다. 이 실습이 보는 칸은totalLinesProcessed·totalBytesProcessed·totalPostFilterLines입니다.execTime은 같은 질의도 돌릴 때마다 달라지니 판단 근거로 쓰지 마세요. - 정답지가 만드는
cost.sh는 편의용 도우미입니다. 직접curl로 던져도 됩니다. - 흔한 실수:
since=1h로 재는 것. 시간이 흐르면 답이 달라져 재채점에서 떨어집니다. - 흔한 실수: 싸진 질의가 답까지 바꿨는데 모르고 넘어가는 것. 비용을 줄일 때마다 결과 줄 수를 함께 확인하세요.
- LogQL 개요 · 로그 질의 · 라벨 · 구조 · HTTP API
네 스트림을 넣고 기준선을 잰다
/root/lk-filters 에서 Loki 를 띄우고 date +%s 를 /root/lk-filters/anchor.txt 에 적은 뒤 python3 /opt/lab/d5/gen.py filters "$(cat anchor.txt)" 로 자료를 넣으세요. 그리고 {app=~"edge|cart|ship|batch"} 를 기준 시각에서 과거 한 시간 구간으로 질의해 응답의 stats.summary 에서 읽은 줄 수와 바이트를 /root/lk-filters/01-base.txt 에 processed=<정수> 와 bytes=<정수> 두 줄로 적으세요.
통계는 query_range 응답의 data.stats.summary 에 들어 있습니다 — jq '.data.stats.summary' 로 한 번 통째로 보세요. totalLinesProcessed 와 totalBytesProcessed 두 칸입니다. 이 숫자가 이 실습 내내 견줄 기준선입니다.
다섯 줄을 찾으려고 몇 줄을 읽었나
같은 한 시간 구간에서 네 스트림 전체를 대상으로 본문에 SETTLE-LATE 가 든 줄을 찾는 질의를 /root/lk-filters/02-marker.logql 에 쓰고, 결과 줄 수와 읽은 줄 수를 /root/lk-filters/02-marker.txt 에 lines=<정수> 와 processed=<정수> 두 줄로 적으세요.
줄 필터 하나면 됩니다. 중요한 것은 답이 아니라 답을 얻으려고 읽은 양 입니다 — 두 숫자를 나란히 놓고 보세요. 읽은 줄 수가 1단계의 기준선과 같은지도 확인하세요.
선택자를 좁히면 읽는 양이 준다
같은 답(같은 줄)을 내면서 스트림 선택자만 좁힌 질의를 /root/lk-filters/03-stream.logql 에 쓰고, 결과 줄 수와 읽은 줄 수를 /root/lk-filters/03-stream.txt 에 lines=<정수>·processed=<정수> 로 적으세요. 그리고 /root/lk-filters/03-ratio.txt 에 ratio=<소수 둘째 자리> 한 줄로 2단계 대비 읽은 줄 수의 비율을 적습니다.
표시가 어느 서비스에서 나오는지는 2단계 결과의 stream 라벨을 보면 압니다. 선택자를 그 하나로 줄이세요. 결과 줄 수는 그대로여야 합니다 — 답을 바꾸지 않고 비용만 줄이는 것이 이 단계의 요점입니다. 비율은 3단계 읽은 줄 수를 2단계 값으로 나눕니다.
구간을 좁히면 읽는 양은 줄고 답도 줄어든다
3단계와 같은 질의를 구간만 30분(기준 시각에서 과거 1800초)으로 바꿔 던지세요. 결과 줄 수와 읽은 줄 수를 /root/lk-filters/04-window.txt 에 lines=<정수>·processed=<정수> 로 적고, /root/lk-filters/04-note.txt 에 tradeoff= 로 시작하는 한 문장(공백 뺀 40자 이상)을 적어 구간을 좁힐 때 무엇을 얻고 무엇을 잃는지 자기 말로 쓰세요.
질의문은 그대로 두고 start 만 바꿉니다. 읽은 줄 수가 절반쯤으로 줄어드는데 결과 줄 수도 함께 줄어듭니다 — 구간 밖의 답은 아예 보이지 않습니다. 사고 시각을 모르는 채 구간부터 좁히면 무엇이 위험한지 생각해 보세요.
줄 필터를 더해도 읽는 양은 그대로다
네 스트림 전체·한 시간 구간에서 두 질의를 던져 읽은 줄 수를 견주세요. 하나는 필터가 전혀 없는 {app=~"edge|cart|ship|batch"}, 다른 하나는 거기에 줄 필터 두 개를 더한 질의입니다. 결과를 /root/lk-filters/05-linefilter.txt 에 네 줄로 적습니다 — plain_processed=, plain_lines=, filtered_processed=, filtered_lines=.
줄 필터는 무엇이든 좋습니다(예: |= "level=error" 와 != "route=/a"). 보아야 할 것은 네 숫자의 관계입니다 — 어느 쌍이 같고 어느 쌍이 다른가. 같은 쪽이 '읽은 양', 다른 쪽이 '남은 양' 입니다.
파서를 붙여도 읽는 양은 그대로다
네 스트림 전체·한 시간 구간에 | logfmt | status="500" 을 붙인 질의를 던져 세 숫자를 /root/lk-filters/06-parser.txt 에 적으세요 — processed=<읽은 줄 수>, post_filter=<필터를 통과해 남은 줄 수>, lines=<결과 줄 수>. 세 숫자를 1단계 기준선과 견주어, 무엇이 같고 무엇이 달라졌는지 보세요.
totalPostFilterLines 가 통계의 세 번째 칸입니다. cost.sh 는 이 칸을 안 내니 curl ... | jq '.data.stats.summary' 로 직접 보거나 도우미를 고쳐 쓰세요. 파서는 줄 필터보다 비싼 일을 하지만, 읽는 양 이라는 기준에서는 둘이 같은 자리에 있습니다.
응용 ① — 네 방법의 비용표
지금까지 잰 것을 /root/lk-filters/cost.tsv 로 정리하세요. 머리글 없이 네 줄이고 각 줄은 탭으로 나눈 세 칸 <방법><탭><읽은줄수><탭><결과줄수> 입니다. 방법 이름은 순서대로 all(2단계), stream(3단계), window(4단계), parser(6단계)입니다.
앞 단계에서 만든 파일들에서 값을 꺼내 모으면 됩니다. 표를 만들고 나면 한눈에 보입니다 — 읽은 줄 수가 실제로 줄어든 행은 몇 개인가.
응용 ② — 예산 안에서 같은 답을 내는 질의
/root/lk-filters/08-budget.logql 에 질의를 하나 쓰세요. 조건은 셋입니다 — (1) 기준 시각에서 과거 한 시간 구간으로 던졌을 때 2단계와 똑같은 줄을 돌려줄 것, (2) 읽은 줄 수가 1단계 기준선의 3분의 1 이하일 것, (3) limit 에 기대지 말 것. 그리고 /root/lk-filters/08-budget.txt 에 processed=<정수> 한 줄로 그 질의가 읽은 줄 수를 적으세요.
읽는 양을 줄이는 손잡이는 둘뿐입니다. 이 단계는 구간을 바꿀 수 없으니 남은 하나로 풀어야 합니다. 2단계 결과의 stream 라벨이 답을 알려 줍니다. 질의를 바꾸고 나서 결과 줄 수가 그대로인지 꼭 다시 재세요 — 싸졌는데 답이 달라지면 튜닝이 아니라 사고입니다.