Loki — A Log Store That Does Not Index Logs
We replayed two hours of logs and the screen showed nothing
한국어 원문으로 표시합니다.
목표
과거 타임스탬프로 넣은 줄이 접수는 되는데 보이지 않는 현상을 파드의 진짜 Loki 에서 재현하고, 그 경계를 정하는 설정을 서버에서 직접 확인한 뒤 플러시로 되살립니다.
왜 중요한가
Loki 의 쓰기와 읽기는 서로 다른 길을 간다. 들어온 줄은 인제스터의 열린 청크에 쌓이고, 청크가 닫혀야 저장소로 올라간다. 질의는 저장소에 묻되 최근 구간이면 인제스터에도 묻는데, 그 '최근' 의 범위가 query_ingesters_within(기본 3시간)이다. 그래서 복구 작업으로 뒤늦게 밀어 넣은 과거 자료는 두 길 사이의 틈에 빠진다 — 저장소에는 아직 없고 인제스터에는 있지만 아무도 묻지 않는다. 이 구조를 모르면 'push 가 거짓말을 한다' 로 결론 내리고 엉뚱한 곳을 고치게 된다.
단계
/root/lk-chunks에서 Loki 를 띄우고date +%s를/root/lk-chunks/anchor.txt에 적은 뒤python3 /opt/lab/d5/gen.py chunks "$(cat anchor.txt)"로 자료를 넣으세요. 이 자료는 기준 시각에서 30분 안쪽에 있습니다.{app="fresh"}를 한 시간 구간으로 질의해 줄 수를 세고, 저장소 디렉터리/tmp/lokidata/chunks아래의 파일 수도 세어/root/lk-chunks/01-boot.txt에lines=<정수>와chunk_files=<정수>두 줄로 적으세요.python3 /opt/lab/d5/gen.py chunks-old "$(cat anchor.txt)"로 기준 시각에서 다섯 시간 전 자료를 넣으세요. 그리고 그 시각 앞뒤 한 시간 구간으로{app="aged"}를 질의해 줄 수를 세고,/root/lk-chunks/02-aged.txt에 두 줄로 적으세요 —push_code=<HTTP 상태 코드>와lines=<정수>.- 새 스트림 네 개에 한 줄씩 넣어 보이는지 확인하세요 — 기준 시각에서 1·2·4·5시간 전이고 라벨은
{"app":"b1h"}·b2h·b4h·b5h입니다. 결과를/root/lk-chunks/boundary.tsv에 머리글 없이 네 줄, 탭으로 나눈 두 칸<시간><탭><yes|no>로 적습니다(시간은1·2·4·5). 그리고 이 경계를 정하는 설정을 서버의/config에서 찾아/root/lk-chunks/03-param.txt에param=<설정 이름>과value=<기본값 그대로>두 줄로 적으세요. - 플러시 전후를 한 파일에 기록하세요. 먼저
{app="aged"}줄 수와/tmp/lokidata/chunks의 파일 수를 재고,curl -XPOST http://localhost:3100/flush를 부른 뒤 같은 두 숫자를 다시 잽니다./root/lk-chunks/04-flush.txt에 네 줄로 적으세요 —aged_before=,chunks_before=,aged_after=,chunks_after=. - 서버의
/config에서 열린 청크가 닫히는 조건을 정하는 세 설정을 찾아/root/lk-chunks/chunkparams.tsv에 머리글 없이 세 줄, 탭으로 나눈 두 칸<설정이름><탭><값>으로 적으세요. 순서는chunk_idle_period,chunk_target_size,max_chunk_age이고 값은 서버가 찍어 준 그대로입니다. - 플러시가 끝난 지금 색인에 어떤 스트림이 있는지 series API 로 확인해
/root/lk-chunks/06-series.txt에 두 줄로 적으세요 —streams=<정수>와apps=<app 라벨 값들을 쉼표로, 사전순>. 조회 구간은 기준 시각에서 과거 일곱 시간으로 주세요. 8단계에서 만들revive스트림은 세지 않습니다(이 단계에서는 아직 없습니다). /root/lk-chunks/runbook.txt에 네 줄을 적으세요. 각 줄은1.부터4.로 시작하고, 각 줄에는 실제로 칠 수 있는 확인 명령이나 확인할 값이 하나씩 들어가야 합니다. 주제는 "과거 구간의 로그가 비어 보인다" 는 신고를 받았을 때의 점검 순서입니다. 네 줄을 합쳐 공백을 뺀 글자가 120자 이상이어야 합니다.- 본문에
revive-check라는 낱말이 든 줄 하나를 라벨{"app":"revive"}로 기준 시각에서 여섯 시간 전에 넣고, 질의로 보이게 만든 뒤 그 줄의 본문을/root/lk-chunks/08-revive.txt에 한 줄로 적으세요. 본문의 나머지 내용은 자유입니다.
참고
- 작업 디렉터리는
/root/lk-chunks입니다. Loki 는 1단계에서 직접 띄웁니다. - 자료 생성기는
/opt/lab/d5/gen.py이고chunks와chunks-old두 자료를 씁니다. 채점기는 이 파일을 읽지 않습니다. - 이 실습 설정은
reject_old_samples: false라 오래된 타임스탬프도 접수됩니다. 운영 기본값은 켜져 있어서, 너무 오래된 자료는 접수 단계에서 거부됩니다. - 저장소는 로컬 파일 시스템(
/tmp/lokidata)입니다. 운영의 객체 저장소와 경로만 다르고 원리는 같습니다. - 흔한 실수: 플러시 직후에 바로 세는 것. 비동기라 몇 초 걸립니다 — 파일 수가 늘어날 때까지 상한을 둔 반복문으로 기다리세요.
- 흔한 실수: series API 에
start·end를 주지 않는 것. 기본 구간이 짧아 과거 스트림이 빠집니다. - Loki 구조 · 설정 문서 · HTTP API · 요청 검증과 속도 제한
최근 자료는 넣자마자 보인다
/root/lk-chunks 에서 Loki 를 띄우고 date +%s 를 /root/lk-chunks/anchor.txt 에 적은 뒤 python3 /opt/lab/d5/gen.py chunks "$(cat anchor.txt)" 로 자료를 넣으세요. 이 자료는 기준 시각에서 30분 안쪽에 있습니다. {app="fresh"} 를 한 시간 구간으로 질의해 줄 수를 세고, 저장소 디렉터리 /tmp/lokidata/chunks 아래의 파일 수도 세어 /root/lk-chunks/01-boot.txt 에 lines=<정수> 와 chunk_files=<정수> 두 줄로 적으세요.
설정 파일의 path_prefix 와 storage_config.filesystem.directory 가 저장소 자리를 정합니다. 파일 수는 find /tmp/lokidata/chunks -type f | wc -l 로 셉니다. 방금 넣었는데 그 디렉터리가 비어 있는 것이 이상하다면, 그것이 이 실습의 출발점입니다.
다섯 시간 전 자료는 204 를 받고도 안 보인다
python3 /opt/lab/d5/gen.py chunks-old "$(cat anchor.txt)" 로 기준 시각에서 다섯 시간 전 자료를 넣으세요. 그리고 그 시각 앞뒤 한 시간 구간으로 {app="aged"} 를 질의해 줄 수를 세고, /root/lk-chunks/02-aged.txt 에 두 줄로 적으세요 — push_code=<HTTP 상태 코드> 와 lines=<정수>.
생성기는 실패하면 0 이 아닌 값으로 끝납니다. 상태 코드를 직접 보고 싶으면 curl -o /dev/null -w '%{http_code}' 로 한 줄을 손으로 넣어 보세요. 질의 구간은 다섯 시간 전을 가운데 두어야 합니다 — 최근 한 시간으로 물으면 당연히 0 입니다.
경계가 어디인지 실측하고 설정에서 확인한다
새 스트림 네 개에 한 줄씩 넣어 보이는지 확인하세요 — 기준 시각에서 1·2·4·5시간 전이고 라벨은 {"app":"b1h"}·b2h·b4h·b5h 입니다. 결과를 /root/lk-chunks/boundary.tsv 에 머리글 없이 네 줄, 탭으로 나눈 두 칸 <시간><탭><yes|no> 로 적습니다(시간은 1·2·4·5). 그리고 이 경계를 정하는 설정을 서버의 /config 에서 찾아 /root/lk-chunks/03-param.txt 에 param=<설정 이름> 과 value=<기본값 그대로> 두 줄로 적으세요.
curl -s localhost:3100/config 가 지금 도는 설정을 통째로 내놓습니다. querier: 블록을 보세요. 값은 서버가 찍어 준 문자열 그대로 적습니다(예: 1h0m0s 같은 모양). 각 줄을 질의할 때는 그 시각 앞뒤로 넉넉한 구간을 주세요.
강제로 플러시하면 같은 질의가 답한다
플러시 전후를 한 파일에 기록하세요. 먼저 {app="aged"} 줄 수와 /tmp/lokidata/chunks 의 파일 수를 재고, curl -XPOST http://localhost:3100/flush 를 부른 뒤 같은 두 숫자를 다시 잽니다. /root/lk-chunks/04-flush.txt 에 네 줄로 적으세요 — aged_before=, chunks_before=, aged_after=, chunks_after=.
플러시는 비동기입니다. 파일이 실제로 생길 때까지 몇 초 걸리니, 고정 sleep 대신 파일 수가 늘어날 때까지 짧게 도는 반복문을 쓰세요(상한을 두는 것을 잊지 마세요). 질의 구간은 2단계와 같아야 두 숫자를 견줄 수 있습니다.
청크는 언제 닫히나 — 세 손잡이를 찾는다
서버의 /config 에서 열린 청크가 닫히는 조건을 정하는 세 설정을 찾아 /root/lk-chunks/chunkparams.tsv 에 머리글 없이 세 줄, 탭으로 나눈 두 칸 <설정이름><탭><값> 으로 적으세요. 순서는 chunk_idle_period, chunk_target_size, max_chunk_age 이고 값은 서버가 찍어 준 그대로입니다.
ingester: 블록에 있습니다. /config 출력이 길면 grep -n 으로 줄 번호를 먼저 찾고 sed -n 으로 그 근처만 보세요. 같은 이름이 다른 블록에도 나올 수 있으니 어느 블록의 값인지 확인해야 합니다.
색인에는 무엇이 남았나
플러시가 끝난 지금 색인에 어떤 스트림이 있는지 series API 로 확인해 /root/lk-chunks/06-series.txt 에 두 줄로 적으세요 — streams=<정수> 와 apps=<app 라벨 값들을 쉼표로, 사전순>. 조회 구간은 기준 시각에서 과거 일곱 시간으로 주세요. 8단계에서 만들 revive 스트림은 세지 않습니다(이 단계에서는 아직 없습니다).
/loki/api/v1/series 에 match[]={app=~".+"} 와 start·end 를 줍니다. 구간을 안 주면 기본값이 최근 몇 시간이라 과거 스트림이 빠집니다 — 그것 자체가 이 실습이 말하는 함정과 같은 종류입니다.
응용 ① — 같은 신고를 받았을 때의 점검 순서
/root/lk-chunks/runbook.txt 에 네 줄을 적으세요. 각 줄은 1. 부터 4. 로 시작하고, 각 줄에는 실제로 칠 수 있는 확인 명령이나 확인할 값이 하나씩 들어가야 합니다. 주제는 "과거 구간의 로그가 비어 보인다" 는 신고를 받았을 때의 점검 순서입니다. 네 줄을 합쳐 공백을 뺀 글자가 120자 이상이어야 합니다.
이 실습에서 실제로 쓴 것들을 순서대로 떠올리면 됩니다 — 접수됐는지, 어느 경로가 답하는지, 저장소에 내려갔는지, 설정이 어떻게 되어 있는지. 남이 새벽에 읽고 따라 할 수 있게 명령을 그대로 적으세요.
응용 ② — 여섯 시간 전 줄 하나를 보이게 만든다
본문에 revive-check 라는 낱말이 든 줄 하나를 라벨 {"app":"revive"} 로 기준 시각에서 여섯 시간 전에 넣고, 질의로 보이게 만든 뒤 그 줄의 본문을 /root/lk-chunks/08-revive.txt 에 한 줄로 적으세요. 본문의 나머지 내용은 자유입니다.
앞 단계에서 한 일을 순서대로 다시 하면 됩니다 — 넣고, 저장소로 내리고, 그 시각을 가운데 두는 구간으로 묻기. 질의가 빈 결과를 내면 아직 한 단계가 남은 것입니다.