Loki — A Log Store That Does Not Index Logs
All the logs were there; I just did not know what to ask
한국어 원문으로 표시합니다.
목표
파드 안의 진짜 Loki 에 LogQL 을 직접 던져, 스트림 선택자와 줄 필터로 원하는 줄만 꺼내고 돌아온 JSON 의 모양까지 읽을 수 있게 됩니다.
왜 중요한가
Loki 에 질문을 던지는 일은 두 층으로 나뉜다. 먼저 선택자가 어느 스트림의 청크를 읽을지 정하고, 그 다음 줄 필터가 읽어 온 줄을 하나씩 맞춰 본다. 이 순서를 모르면 '왜 이 질의는 빠르고 저 질의는 느린가' 를 영원히 설명하지 못한다. 본문은 색인되어 있지 않으므로 줄 필터는 선택자가 고른 만큼을 전부 읽는다 — 그래서 사고 조사에서 가장 먼저 하는 일이 선택자와 시간 구간을 좁히는 일이다. limit 과 direction 도 사고 조사의 결과를 바꾼다. 방향을 모르고 limit 을 걸면 가장 중요한 첫 오류 대신 마지막 오류만 보게 된다.
단계
/root/lk-logql에서 Loki 를 띄우고,date +%s값을/root/lk-logql/anchor.txt에 한 줄로 적은 뒤python3 /opt/lab/d5/gen.py logql "$(cat anchor.txt)"로 자료를 넣으세요. 그리고 지금 Loki 에 스트림이 몇 개인지 세어/root/lk-logql/01-boot.txt에streams=<정수>한 줄로 적으세요.- 기준 시각에서 과거 한 시간 동안
app이web이고level이error인 줄이 몇 줄인지 구하세요. 쓴 질의를/root/lk-logql/02-selector.logql에, 줄 수를/root/lk-logql/02-selector.txt에lines=<정수>한 줄로 적습니다. - 같은 한 시간 동안 세 서비스(
web·api·worker) 전체에서 본문에timeout이 든 줄이 몇 줄인지 구하세요. 질의는/root/lk-logql/03-line.logql, 답은/root/lk-logql/03-line.txt에lines=<정수>로 적습니다. - 세 서비스 전체에서 본문에
refused가 들어 있고rpc는 들어 있지 않은 줄을 세세요. 질의는/root/lk-logql/04-chain.logql, 답은/root/lk-logql/04-chain.txt에lines=<정수>로 적습니다. - 세 서비스 전체에서 본문에 500 또는 503 이라는 세 자리 숫자가 든 줄을 정규식 줄 필터 하나로 세세요. 질의는
/root/lk-logql/05-regex.logql, 답은/root/lk-logql/05-regex.txt에lines=<정수>로 적습니다. 질의에는 정규식 줄 필터 연산자가 반드시 들어가야 합니다. {app="web",level="error"}를 같은 한 시간 구간에limit=5로 두 번 질의하세요. 한 번은direction=forward, 한 번은direction=backward입니다. 결과를/root/lk-logql/06-order.txt에 세 줄로 적습니다 —forward_first=<나노초 타임스탬프>,backward_first=<나노초 타임스탬프>,total=<구간 전체 줄 수>.- 아무 로그 질의나 한 번 던져 원본 JSON 을 보고
/root/lk-logql/07-shape.txt에 세 줄을 적으세요.result_type=뒤에는data.resultType의 값,ts_unit=뒤에는values의 첫 칸이 어떤 시간 단위인지(ns·us·ms·s중 하나),entry_len=뒤에는values의 원소 하나가 몇 칸짜리 배열인지 정수로 적습니다. web과api두 서비스에서 본문에timeout또는refused가 든 줄만 골라, 그 중 가장 이른 줄의 나노초 타임스탬프와 전체 줄 수를 구하세요. 질의는/root/lk-logql/08-triage.logql, 답은/root/lk-logql/08-triage.txt에lines=<정수>와first_ts=<나노초>두 줄로 적습니다. 질의에는 정규식 줄 필터가 들어가야 하고, worker 는 들어가면 안 됩니다.
참고
- 작업 디렉터리는
/root/lk-logql입니다. Loki 는 파드가 뜰 때 자동으로 켜지지 않습니다 — 1단계에서 직접 띄웁니다. - 자료 생성기는
/opt/lab/d5/gen.py입니다.logql자료를 기준 시각 인자와 함께 돌립니다. 채점기는 읽지 않으니 내용을 고칠 필요는 없습니다. - 질의는
logcli로도 던질 수 있습니다:LOKI_ADDR=http://localhost:3100 logcli query --limit=5 '{app="web"}'. 다만 이 실습의 답은 절대 구간으로 재야 하므로query_rangeAPI 를 직접 부르는 쪽이 정확합니다. - 흔한 실수:
since=1h로 재는 것. 시간이 흐르면 답이 달라져 재채점에서 떨어집니다.anchor.txt의 기준 시각으로start·end를 주세요. - 흔한 실수: 줄 필터의 문자열을 큰따옴표로 감싸는 것. LogQL 에서는 역따옴표가 안전합니다 — 역슬래시가 이스케이프로 해석되지 않습니다.
- LogQL 개요 · 로그 질의 · HTTP API · 라벨
Loki 를 띄우고 하루치 대신 한 시간치를 넣는다
/root/lk-logql 에서 Loki 를 띄우고, date +%s 값을 /root/lk-logql/anchor.txt 에 한 줄로 적은 뒤 python3 /opt/lab/d5/gen.py logql "$(cat anchor.txt)" 로 자료를 넣으세요. 그리고 지금 Loki 에 스트림이 몇 개인지 세어 /root/lk-logql/01-boot.txt 에 streams=<정수> 한 줄로 적으세요.
설정은 /opt/lab/loki/loki.yaml 을 복사해 씁니다. /ready 가 ready 를 돌려줄 때까지 20초쯤 걸리니 고정 sleep 대신 조건을 보는 반복문을 쓰세요. 스트림 수는 /opt/lab/loki/streams.sh 가 세어 줍니다 — 스트림은 서로 다른 라벨 조합의 개수입니다.
선택자만으로 볼 곳을 정한다
기준 시각에서 과거 한 시간 동안 app 이 web 이고 level 이 error 인 줄이 몇 줄인지 구하세요. 쓴 질의를 /root/lk-logql/02-selector.logql 에, 줄 수를 /root/lk-logql/02-selector.txt 에 lines=<정수> 한 줄로 적습니다.
스트림 선택자는 중괄호 안의 라벨 매처입니다. 조건을 쉼표로 이으면 둘 다 만족하는 스트림만 고릅니다. 구간은 start·end 를 나노초로 주세요 — anchor.txt 의 값에 1000000000 을 곱하면 나노초입니다.
줄 필터로 본문을 뒤진다
같은 한 시간 동안 세 서비스(web·api·worker) 전체에서 본문에 timeout 이 든 줄이 몇 줄인지 구하세요. 질의는 /root/lk-logql/03-line.logql, 답은 /root/lk-logql/03-line.txt 에 lines=<정수> 로 적습니다.
줄 필터는 선택자 뒤에 옵니다. 문자열을 그대로 찾는 연산자와 정규식으로 찾는 연산자가 다릅니다. 본문은 색인되어 있지 않아서, 줄 필터는 선택자가 고른 스트림의 줄을 전부 읽어 가며 맞춰 봅니다.
필터를 이어 붙여 걸러 낸다
세 서비스 전체에서 본문에 refused 가 들어 있고 rpc 는 들어 있지 않은 줄을 세세요. 질의는 /root/lk-logql/04-chain.logql, 답은 /root/lk-logql/04-chain.txt 에 lines=<정수> 로 적습니다.
줄 필터는 여러 개를 이어 쓸 수 있고 왼쪽부터 차례로 적용됩니다. '들어 있지 않다' 를 뜻하는 연산자가 따로 있습니다. 두 조건을 하나의 정규식으로 합치려 하지 마세요 — 부정은 이어 쓰는 쪽이 읽기 쉽습니다.
정규식 줄 필터
세 서비스 전체에서 본문에 500 또는 503 이라는 세 자리 숫자가 든 줄을 정규식 줄 필터 하나로 세세요. 질의는 /root/lk-logql/05-regex.logql, 답은 /root/lk-logql/05-regex.txt 에 lines=<정수> 로 적습니다. 질의에는 정규식 줄 필터 연산자가 반드시 들어가야 합니다.
정규식 줄 필터는 RE2 문법입니다. 문자 하나가 여러 값 중 하나일 수 있다는 것을 대괄호로 적습니다. 두 개의 |= 를 이어 쓰면 '둘 다 든 줄' 이 되어 답이 달라집니다.
limit 과 direction — 무엇이 잘려 나가는가
{app="web",level="error"} 를 같은 한 시간 구간에 limit=5 로 두 번 질의하세요. 한 번은 direction=forward, 한 번은 direction=backward 입니다. 결과를 /root/lk-logql/06-order.txt 에 세 줄로 적습니다 — forward_first=<나노초 타임스탬프>, backward_first=<나노초 타임스탬프>, total=<구간 전체 줄 수>.
limit 은 '앞에서부터 몇 줄' 이 아니라 '정렬 방향 기준 몇 줄' 입니다. 방향을 바꾸면 같은 limit 이 정반대의 줄을 돌려줍니다. 사고 조사에서 이걸 모르면 가장 중요한 첫 오류를 못 보고 마지막 오류만 보게 됩니다. 전체 줄 수는 limit 을 넉넉히 주고 셉니다.
돌아온 JSON 을 읽는다
아무 로그 질의나 한 번 던져 원본 JSON 을 보고 /root/lk-logql/07-shape.txt 에 세 줄을 적으세요. result_type= 뒤에는 data.resultType 의 값, ts_unit= 뒤에는 values 의 첫 칸이 어떤 시간 단위인지(ns·us·ms·s 중 하나), entry_len= 뒤에는 values 의 원소 하나가 몇 칸짜리 배열인지 정수로 적습니다.
curl ... | jq . 로 그대로 봅니다. 로그 질의와 지표 질의는 resultType 이 다릅니다. 타임스탬프의 자릿수를 세어 보면 단위를 알 수 있습니다 — epoch 초는 열 자리입니다. 이 모양을 알아야 자동화 스크립트를 쓸 수 있습니다.
응용 — 한 질의로 사고 구간을 좁힌다
web 과 api 두 서비스에서 본문에 timeout 또는 refused 가 든 줄만 골라, 그 중 가장 이른 줄의 나노초 타임스탬프와 전체 줄 수를 구하세요. 질의는 /root/lk-logql/08-triage.logql, 답은 /root/lk-logql/08-triage.txt 에 lines=<정수> 와 first_ts=<나노초> 두 줄로 적습니다. 질의에는 정규식 줄 필터가 들어가야 하고, worker 는 들어가면 안 됩니다.
선택자에서 두 서비스만 고르는 매처와, 둘 중 하나를 찾는 정규식을 함께 씁니다. 가장 이른 줄을 찾으려면 정렬 방향을 앞쪽으로 두거나, 받은 결과를 직접 정렬합니다. 이게 사고 조사의 첫 동작입니다 — 범위를 좁히고 시작 시각을 못박는 일.