Loki structured metadata, 이거 모르면 라벨 카디널리티로 계속 운다
라벨 vs structured metadata, 결정 기준
오늘 알게 된 건데, 아직 Loki에서 pod나 trace_id를 라벨로 박고 계신 분들 꽤 많더라. Loki 3.x부터는 structured metadata가 정식으로 들어왔는데 활용 안 하면 진짜 손해다. 라벨 카디널리티 폭증 없이 검색 가능한 메타데이터를 붙일 수 있는 기능이다.
Loki에서 라벨은 인덱스가 만들어지는 대상이라 카디널리티가 곧 비용이다. namespace, app 같은 저카디널리티 값만 라벨로 두고, trace_id, request_id, pod_name, thread_id 같은 고카디널리티 값은 structured metadata로 옮기는 게 정석이다. Grafana 공식 가이드도 "OpenTelemetry 데이터 ingest를 지원하기 위해 structured metadata를 설계했다"고 명시하고 있다.
문제는 이게 그냥 켜진다고 동작하는 게 아니다. limits_config.allow_structured_metadata: true 를 명시적으로 켜야 하고, 클라이언트(Promtail, Alloy, OTel Collector 등) 쪽에서도 metadata를 따로 보내줘야 한다.
설정 한 토막
Loki 쪽:
limits_config:
allow_structured_metadata: true
# 기본 1MB인 ingestion limit도 메타데이터 추가하면 빠르게 찰 수 있다
per_stream_rate_limit: 5MB
Alloy(또는 Promtail) 쪽에서 Kubernetes pod 이름을 메타데이터로 보내는 예:
loki.process "default" {
stage.structured_metadata {
values = {
pod = "",
container = "",
trace_id = "",
}
}
forward_to = [loki.write.default.receiver]
}
핵심은 pod를 라벨에서 빼고 structured_metadata로 보내는 것. 라벨에 그대로 두면 의미가 없다.
LogQL에서는 파이프로 필터링 가능:
{namespace="prod", app="api"} | pod="api-7d4f-xyz" | json
라벨로 박았을 때랑 쿼리 모양은 거의 동일한데, 인덱스 크기와 ingest 비용은 차이가 크다.
한 가지 함정
기존에 운영 중이던 Loki 환경에서 라벨을 metadata로 바꾸려고 했다가 흠칫한 부분. 공식 문서가 친절하게 적어두기를, 가장 쉬운 방법은 Loki를 멈추고 기존 로그 데이터를 다 지우고 처음부터 metadata로 보내는 것이라고 한다. 운영 중인 클러스터에서 이걸 하긴 좀 어렵다. 우리 팀에서는 새로운 테넌트를 만들어서 그쪽으로 점진적으로 옮기는 식으로 했다. 단순히 설정만 바꾸면 끝나는 작업이 아니라는 점 주의.
혹시 더 깔끔한 마이그레이션 방법 쓰시는 분 있으면 댓글로 알려주세요. 아직 우리도 검증 중이다.