728x90
반응형

- Loki: 인덱스 프리 아키텍처로 완성하는 압도적 가성비의 로그 중앙화
지난 포스팅에서는 Mimir를 통해 어떻게 메트릭 데이터의 수평 확장과 장기 보관 문제를 해결하는지 살펴보았습니다. 메트릭(Mimir)이 알람을 통해 장애의 '발생'을 알려주었다면, 이제 엔지니어는 에러의 스택 트레이스나 쿼리 로그를 뒤져 장애의 '근본 원인'을 찾아내야 합니다.
이번 포스팅에서는 LGTM 스택에서 로그(Logs)를 담당하며, 기존 로그 관리의 패러다임을 완전히 뒤집어버린 Loki(로키)에 대해 깊이 있게 다뤄보겠습니다.
1. ELK 스택의 딜레마: 로그는 너무 무겁고 비싸다
지난 수년간 로그 중앙화의 사실상 표준은 Elasticsearch, Logstash, Kibana로 이루어진 ELK 스택이었습니다. Elasticsearch는 강력한 전문 검색 엔진입니다. 어떤 단어로 검색하든 0.1초 만에 결과를 뱉어냅니다. 하지만 이 강력한 성능 이면에는 치명적인 단점이 존재합니다. 바로 '압도적인 유지보수 비용'입니다.
- 무거운 역색인(Inverted Index): Elasticsearch는 들어오는 모든 로그 메시지를 파싱하고 쪼개어 거대한 역색인 사전을 만듭니다. 이로 인해 원본 로그 데이터보다 인덱스 용량이 더 커지는 배보다 배꼽이 큰 상황이 빈번하게 발생합니다.
- 고가의 스토리지 요구: 빠른 인덱스 검색을 위해 필연적으로 고성능의 SSD를 요구합니다. 하루에 수백 GB에서 수 TB씩 쏟아지는 MSA 환경의 로그를 모두 SSD에 장기 보관하는 것은 인프라 예산의 재앙과 같습니다.
- OOM(Out Of Memory) 이슈: 인덱스가 커질수록 JVM Heap 메모리 사용량이 급증하여, 클러스터 안정성을 유지하기 위한 엔지니어의 운영 피로도가 극심해집니다.

대부분의 로그는 'Write Heavy, Read Rarely'(쓰기는 엄청나게 많지만, 실제 읽히는 것은 장애가 났을 때뿐인) 특성을 가집니다. 어쩌다 한 번 장애 추적을 위해 검색할 로그에, 막대한 인덱싱 비용과 SSD를 투자하는 것이 과연 합리적일까요?
2. Loki의 철학: 인덱스 프리(Index-Free) 아키텍처
Grafana Labs는 이 지점에서 완전히 다른 접근을 시도합니다. "모든 것을 인덱싱하지 말고, 메타데이터만 인덱싱하자." 이것이 바로 프로메테우스에서 영감을 받은 Loki의 핵심 철학입니다.
① 라벨(Label) 기반의 메타데이터 인덱싱
Loki는 로그의 본문(메시지 텍스트)을 분석하거나 인덱싱하지 않습니다. 대신 해당 로그가 어디서 왔는지 나타내는 꼬리표, 즉 라벨(예: app="payment", env="prod", level="error")만 인덱스로 만듭니다. 인덱스가 극도로 작아지므로 메모리와 스토리지 사용량이 기하급수적으로 줄어듭니다.
② 저렴한 오브젝트 스토리지에 압축 저장
인덱싱되지 않은 거대한 원본 로그 데이터는 Chunk 형태로 고도로 압축되어 AWS S3, GCS, MinIO와 같은 매우 저렴한 오브젝트 스토리지에 곧바로 저장됩니다. 비싼 SSD가 필요 없습니다.
3. Loki 아키텍처: 분산 Grep 엔진
그렇다면 인덱스도 없이 어떻게 방대한 로그에서 원하는 에러 문구를 찾아낼까요? Loki는 검색 시점에 압도적인 분산 병렬 컴퓨팅 능력으로 오브젝트 스토리지를 Full Scan 해버리는 방식을 택했습니다. 쉽게 말해 클러스터 전체가 달려들어 수행하는 '초고속 분산 Grep'입니다.

동작 워크플로우:
- 수집 (Alloy/Promtail): 각 노드에 배포된 에이전트가 로그를 수집하고, 쿠버네티스의 파드 라벨 등을 메타데이터로 부착하여 Loki 서버의 Distributor로 전송합니다.
- 저장 (Ingester): 수집된 로그는 메모리에 잠시 머물다 압축된 Chunk 형태로 S3와 같은 Object Storage에 쓰입니다.
- 조회 (Querier & Query Frontend): 사용자가 검색을 요청하면, Query Frontend가 쿼리를 잘게 쪼개어 수많은 Querier 워커들에게 분배합니다.
- 분산 Grep 처리: Querier들은 먼저 라벨 인덱스를 통해 S3의 수많은 파일 중 '어느 청크를 뒤져야 할지' 범위를 대폭 좁힙니다. (예: app="payment" 라벨이 붙은 청크만 다운로드). 이후 해당 청크들의 압축을 풀고 메모리 위에서 정규표현식으로 타겟 텍스트를 고속으로 스캔합니다.
4. LogQL: 프로메테우스의 유산을 이어받다
Loki의 쿼리 언어인 LogQL은 PromQL과 매우 유사한 문법을 가집니다. 크게 두 가지 역할을 수행합니다.
① Log Queries (로그 필터링)
라벨 매처를 통해 로그 스트림을 좁히고, 파이프(|) 연산자를 통해 텍스트를 필터링하거나 파싱합니다.
- 예시: {app="payment", env="prod"} |= "NullPointerException" | json
(해석: prod 환경의 payment 앱 로그 중 "NullPointerException"이 포함된 로그만 가져와서 JSON 형태로 파싱하라)
② Metric Queries (로그의 메트릭화)
단순한 텍스트 검색을 넘어, 특정 에러 로그가 발생하는 빈도를 실시간 시계열 메트릭(그래프)으로 변환할 수 있습니다.
- 예시: rate({app="payment"} |= "error" [5m])
(해석: payment 앱에서 최근 5분간 "error" 단어가 포함된 로그의 초당 발생 횟수를 그래프로 그려라)
이 기능을 통해 별도의 애플리케이션 메트릭 코드를 작성하지 않아도, 로그만으로 훌륭한 모니터링 대시보드와 알람을 구축할 수 있습니다.
5. 마무리하며
Loki는 "검색 속도를 위해 모든 자원을 쏟아붓는" 기존의 패러다임에서 벗어나, "클라우드 시대의 저렴한 오브젝트 스토리지와 스케일 아웃이 쉬운 컴퓨팅 파워를 활용하여 비용 효율성을 극대화하는" 방향으로 로그 아키텍처를 재정의했습니다.
단순 검색 속도는 역색인 기반의 Elasticsearch가 빠를 수 있지만, 라벨이라는 동일한 문맥을 통해 메트릭(Mimir)에서 로그(Loki)로 단 한 번의 클릭만으로 이어지는 Seamless한 트러블슈팅 경험, 그리고 S3 기반의 압도적인 스토리지 비용 절감은 왜 많은 조직이 LGTM 스택으로 마이그레이션을 선택하는지 증명합니다.
728x90
반응형