Loki
Loki는 Lumie 인프라의 로그 집계 시스템으로, Prometheus와 유사한 라벨 기반 접근 방식을 사용하여 효율적인 로그 저장 및 검색을 제공합니다.
아키텍처
배포 구성
- 네임스페이스:
loki - 차트:
lokiv6.24.0 - 이미지:
zot.lumie-infra.com/grafana/loki:3.6.5 - 모드: Single Binary (단일 바이너리)
- 복제본: 1개
로그 파이프라인
저장 구성
로컬 파일시스템
Loki는 단순성을 위해 로컬 파일시스템(emptyDir)을 사용합니다:
storage:
type: filesystem
schemaConfig:
configs:
- from: "2024-01-01"
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
데이터 보존
limits_config:
retention_period: 72h # 3일 보존
ingestion_rate_mb: 10
ingestion_burst_size_mb: 20
max_streams_per_user: 10000
로그 수집
OpenTelemetry Collector 통합
로그는 OpenTelemetry Collector의 filelog 수신기를 통해 수집됩니다:
# OTel Collector 설정 (참고용)
receivers:
filelog:
include:
- /var/log/pods/*/*/*.log
exclude:
- /var/log/pods/opentelemetry_otel-collector*/*/*.log
operators:
- type: router
routes:
- output: parser-docker
expr: 'body matches "^\\{"'
- output: parser-containerd
expr: 'body matches "^[^ Z]+Z"'
로그 라벨링
수집된 로그에는 다음 라벨이 자동으로 추가됩니다:
k8s.namespace.name: Kubernetes 네임스페이스k8s.pod.name: 파드 이름k8s.container.name: 컨테이너 이름k8s.node.name: 노드 이름log.iostream: stdout/stderr 구분
단일 바이너리 설정
구성 요소 비활성화
단일 바이너리 모드에서는 개별 구성 요소들이 비활성화됩니다:
backend:
replicas: 0
read:
replicas: 0
write:
replicas: 0
gateway:
enabled: false
리소스 설정
singleBinary:
resources:
requests:
cpu: 10m
memory: 309Mi
limits:
memory: 561Mi # CPU 제한 없음
priorityClassName: medium-priority
볼륨 마운트
extraVolumes:
- name: data
emptyDir: {}
extraVolumeMounts:
- name: data
mountPath: /var/loki
쿼리 언어 (LogQL)
기본 쿼리
# 특정 네임스페이스의 모든 로그
{k8s_namespace_name="my-app"}
# 에러 로그 필터링
{k8s_namespace_name="my-app"} |= "error"
# 정규식 매칭
{k8s_namespace_name="my-app"} |~ "error|ERROR|Error"
# JSON 로그 파싱
{k8s_namespace_name="my-app"} | json | level="error"
집계 쿼리
# 시간당 로그 카운트
count_over_time({k8s_namespace_name="my-app"}[1h])
# 에러 로그 비율
sum(rate({k8s_namespace_name="my-app"} |= "error" [5m])) /
sum(rate({k8s_namespace_name="my-app"}[5m]))
# 파드별 로그 분포
sum by (k8s_pod_name) (count_over_time({k8s_namespace_name="my-app"}[5m]))
고급 쿼리
# 로그 레벨별 분류
{k8s_namespace_name="my-app"}
| json
| level != ""
| line_format "{{.timestamp}} [{{.level}}] {{.message}}"
# 특정 시간대 필터링
{k8s_namespace_name="my-app"}
| json
| timestamp > "2024-01-01T00:00:00Z"
# 메트릭 추출
sum by (level) (
count_over_time(
{k8s_namespace_name="my-app"}
| json
| level != "" [5m]
)
)
접근 방법
Grafana를 통한 접근
Loki는 주로 Grafana의 Explore 기능을 통해 접근합니다:
- Grafana에서 Explore 선택
- 데이터 소스를 Loki로 변경
- LogQL 쿼리 입력
- 시간 범위 설정 후 실행
직접 API 접근
# 포트 포워딩
kubectl port-forward -n loki svc/loki 3100:3100
# 라벨 조회
curl -s "http://localhost:3100/loki/api/v1/labels"
# 로그 쿼리
curl -s "http://localhost:3100/loki/api/v1/query_range" \
--data-urlencode 'query={k8s_namespace_name="default"}' \
--data-urlencode 'start=2024-01-01T00:00:00Z' \
--data-urlencode 'end=2024-01-01T01:00:00Z'
모니터링
ServiceMonitor
Loki 메트릭은 Prometheus에서 수집됩니다:
monitoring:
serviceMonitor:
enabled: true
interval: 60s
labels:
release: prometheus
relabelings:
- targetLabel: cluster
replacement: "mayne-cluster"
주요 메트릭
loki_ingester_streams: 활성 스트림 수loki_ingester_chunks_created_total: 생성된 청크 수loki_request_duration_seconds: 요청 지연 시간loki_panic_total: 패닉 발생 횟수
문제 해결
로그가 수집되지 않는 경우
# Loki 상태 확인
kubectl logs -n loki deployment/loki
# OpenTelemetry Collector 상태 확인
kubectl logs -n opentelemetry daemonset/otel-collector-collector
# 로그 파일 존재 확인
kubectl exec -n opentelemetry daemonset/otel-collector-collector -- \
ls -la /var/log/pods/
쿼리 성능 문제
# 스트림 수 확인
curl -s "http://loki.loki.svc.cluster.local:3100/metrics" | \
grep loki_ingester_streams
# 청크 수 확인
curl -s "http://loki.loki.svc.cluster.local:3100/metrics" | \
grep loki_ingester_chunks_created_total
저장 공간 문제
# 디스크 사용량 확인
kubectl exec -n loki deployment/loki -- df -h /var/loki
# 파드 재시작 (emptyDir 초기화)
kubectl rollout restart -n loki deployment/loki
로그 관리 모범 사례
구조화된 로깅
애플리케이션에서 JSON 형태의 구조화된 로그를 출력하면 검색과 분석이 용이합니다:
{
"timestamp": "2024-01-01T12:00:00Z",
"level": "error",
"message": "Database connection failed",
"service": "user-service",
"trace_id": "abc123",
"error": {
"type": "ConnectionError",
"details": "Connection timeout after 30s"
}
}
로그 레벨 활용
적절한 로그 레벨을 사용하여 중요도별로 필터링:
ERROR: 오류 상황WARN: 경고 상황INFO: 일반 정보DEBUG: 디버깅 정보
민감 정보 제거
로그에 민감한 정보(비밀번호, 토큰 등)가 포함되지 않도록 주의합니다.
성능 최적화
라벨 카디널리티
높은 카디널리티 라벨(예: 사용자 ID, 요청 ID)은 성능에 영향을 줄 수 있으므로 주의합니다.
쿼리 최적화
- 시간 범위를 적절히 제한