마이크로서비스 아키텍처가 사실상 일반화 되면서 수십개의 이상의 컨테이너를 운영하는 것이 일상이 되었다. 이러한 환경에서 로그 관리는 필수지만 내부 보안 정책이나 권한, 인력 문제 등으로 동시에 관리한다는 것도 쉽지 않은 일이다. Grafana Loki 는 이 문제를 조금이나마 해결하기 위한 오픈소스이자 경량화된 로그 집계 시스템이다.
Loki란?
Loki는 Grafana Labs 에서 개발한 로그 집계 시스템으로, "Prometheus for logs"라는 컨셉으로 설계되었다. Elasticsearch 와 달리 로그 내용을 인덱싱하지 않고 메타데이터(레이블)만 인덱싱하여 비용 효율적이고 운영이 간단하다.
주요 특징
- 경량성: 로그 내용이 아닌 레이블만 인덱싱
- 비용 효율성: 저장 공간과 메모리 사용량 최소화
- Grafana 통합: 별도 대시보드 없이 Grafana에서 직접 조회
- LogQL: 강력하고 직관적인 쿼리 언어
아키텍처 구성
Loki 스택은 크게 세 가지 컴포넌트로 구성된다.
- Loki: 로그를 저장하고 쿼리를 처리하는 서버
- Promtail: 로그를 수집하여 Loki로 전송하는 에이전트
- Grafana: 로그를 시각화하고 쿼리하는 UI
Docker Compose로 Loki 스택 구축하기
1. docker-compose.yml 작성
version: "3.8"
networks:
loki:
services:
loki:
image: grafana/loki:2.9.0
container_name: loki
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
networks:
- loki
promtail:
image: grafana/promtail:2.9.0
container_name: promtail
volumes:
- /var/log:/var/log
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
networks:
- loki
depends_on:
- loki
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
networks:
- loki
depends_on:
- loki
# 로그 생성용 예제 애플리케이션
nginx:
image: nginx:alpine
container_name: nginx-app
ports:
- "8080:80"
labels:
logging: "promtail"
logging_jobname: "nginx"
networks:
- loki
2. Promtail 설정 (promtail-config.yml)
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
# Docker 컨테이너 로그 수집
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
filters:
- name: label
values: ["logging=promtail"]
relabel_configs:
- source_labels: ['__meta_docker_container_name']
regex: '/(.*)'
target_label: 'container'
- source_labels: ['__meta_docker_container_log_stream']
target_label: 'stream'
- source_labels: ['__meta_docker_container_label_logging_jobname']
target_label: 'job'
3. Docker 로깅 드라이버 설정
개별 컨테이너에 Loki 로깅 드라이버를 직접 설정할 수 있다.
services:
myapp:
image: myapp:latest
logging:
driver: loki
options:
loki-url: "http://localhost:3100/loki/api/v1/push"
loki-batch-size: "400"
labels: "app,environment"
loki-retries: "2"
실행 및 확인
스택 실행
# Promtail 설정 파일 생성
cat > promtail-config.yml << 'EOF'
# 위의 promtail-config.yml 내용 붙여넣기
EOF
# Docker Compose 실행
docker-compose up -d
# 로그 확인
docker-compose logs -f
Grafana 설정
- 브라우저에서
http://localhost:3000접속 - 기본 계정으로 로그인 (admin/admin)
- Configuration → Data Sources → Add data source
- Loki 선택
- URL에
http://loki:3100입력 - Save & Test
LogQL 쿼리 실습
Grafana의 Explore 메뉴에서 다음 쿼리들을 실행해보세요:
기본 쿼리
# 특정 컨테이너의 모든 로그
{container="nginx-app"}
# 특정 job의 로그
{job="nginx"}
# 여러 조건 조합
{container="nginx-app", stream="stdout"}
필터링
# 에러 로그만 조회
{container="nginx-app"} |= "error"
# 특정 패턴 제외
{container="nginx-app"} != "health"
# 정규식 필터
{container="nginx-app"} |~ "POST /api/.*"
집계 쿼리
# 최근 5분간 로그 수
count_over_time({container="nginx-app"}[5m])
# 에러 발생 비율
sum(rate({container="nginx-app"} |= "error"[5m]))
# 컨테이너별 로그 수
sum by (container) (count_over_time({job="nginx"}[1h]))
파싱 및 분석
# JSON 로그 파싱
{container="nginx-app"} | json | status_code >= 500
# 로그 레벨별 분류
{container="nginx-app"}
| pattern `<_> level=<level> <_>`
| level = "error"
# 응답 시간 분석
{container="nginx-app"}
| json
| response_time > 1000
실전 활용 팁
1. 효율적인 레이블 설계
labels:
- environment=production
- team=backend
- service=api-gateway
- version=v1.2.3
레이블이 너무 많으면 cardinality가 높아져 성능이 저하된다는 문제가 있기 때문에 꼭 필요한 메타데이터만 레이블로 사용해야한다.
2. 로그 보존 정책 설정
# loki-config.yaml
limits_config:
retention_period: 744h # 31일
table_manager:
retention_deletes_enabled: true
retention_period: 744h
3. 알림 설정
Grafana에서 로그 기반 알림을 설정할 수 있다
# 1분간 에러가 10개 이상 발생하면 알림
sum(count_over_time({container=~".+"} |= "error"[1m])) > 10
4. 대시보드 구성 예시
컨테이너 로그 모니터링 대시보드:
- 전체 로그 볼륨 (시계열)
- 컨테이너별 로그 수
- 로그 레벨 분포 (INFO, WARN, ERROR)
- 최근 에러 로그 테이블
- 응답 시간 히트맵
성능 최적화
Promtail 최적화
limits_config:
readline_rate_enabled: true
readline_rate: 10000
readline_burst: 20000
Loki 쿼리 최적화
- 시간 범위 제한: 너무 긴 시간 범위는 피하기
- 레이블 필터 우선: 텍스트 필터보다 레이블 필터를 먼저 사용
- 인덱스 활용:
{container="app"}(O) vs{} |= "app"(X)
# good
{container="nginx-app"} |= "error" [5m]
# bad
{} |= "nginx-app" |= "error" [24h]
트러블슈팅
로그가 보이지 않을 때
# Promtail 로그 확인
docker logs promtail
# Loki 상태 확인
curl http://localhost:3100/ready
# Docker 소켓 권한 확인
ls -l /var/run/docker.sock
메모리 부족 문제
# docker-compose.yml
services:
loki:
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 512M
프로덕션 환경 고려사항
1. 고가용성 구성
프로덕션 환경에서는 Loki를 마이크로서비스 모드로 실행해야한다.
- Distributor: 로그 수집 엔드포인트
- Ingester: 로그 저장
- Querier: 쿼리 처리
2. 오브젝트 스토리지 연동
장기 보관을 위해 S3, GCS 등과 연동
storage_config:
aws:
s3: s3://region/bucket
s3forcepathstyle: true
3. 보안 설정
auth_enabled: true
server:
http_tls_config:
cert_file: /path/to/cert.pem
key_file: /path/to/key.pem
마치며
Grafana Loki는 도커 환경에서 로그 관리를 위한 강력하면서도 가벼운 솔루션이다. Elasticsearch 보다 리소스 효율적이면서 Grafana와 잘 맞아서 사용하기도 좋다. 여기서 가장 중요하건 적절한 레이블 설계와 효율적인 쿼리를 작성하는 것인다 참으로 어려우면서 쉬운 것 같다.
참고 자료
'DevOps > 모니터링' 카테고리의 다른 글
| [모니터링] - 그라파나 알림 매니저 (Grafana AlertManager) 활용하기 (0) | 2024.02.16 |
|---|---|
| [모니터링] - Node.js로 그라파나에서 DB 데이터 로그 보기 (0) | 2024.02.16 |
| [모니터링] - Grafana + Prometheus + cAdvisor로 컨테이너 상태(리소스) 수집하기 (0) | 2024.02.16 |
| [DevOps] - Jenkins와 Spring Boot로 구축하는 CI/CD 파이프라인 (0) | 2024.02.16 |
| [모니터링] - 그라파나 + 프로메테우스로 네트워크 로그 수집하기 (0) | 2024.02.15 |