Skip to main content

Loki

Loki는 Lumie 인프라의 로그 집계 시스템으로, Prometheus와 유사한 라벨 기반 접근 방식을 사용하여 효율적인 로그 저장 및 검색을 제공합니다.

아키텍처

배포 구성

  • 네임스페이스: loki
  • 차트: loki v6.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 기능을 통해 접근합니다:

  1. Grafana에서 Explore 선택
  2. 데이터 소스를 Loki로 변경
  3. LogQL 쿼리 입력
  4. 시간 범위 설정 후 실행

직접 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)은 성능에 영향을 줄 수 있으므로 주의합니다.

쿼리 최적화

  • 시간 범위를 적절히 제한
  • 라벨 필터를 먼저 적용
  • 정규식보다는 정확한 문자열 매칭 사용

보존 기간 관리

불필요한 로그는 적절한 보존 기간을 설정하여 자동 삭제되도록 합니다.

보안 고려사항

네트워크 보안

  • 클러스터 내부 통신만 허용
  • 외부 접근은 Grafana를 통해서만 가능

로그 스크러빙

민감한 정보가 포함된 로그는 수집 단계에서 필터링하거나 마스킹합니다.

관련 문서