Grafana Loki Stack으로 로그 시스템 구축 [로그 시스템 2]

Hyeok_Choi·2024년 1월 3일
0

로그 시스템

목록 보기
2/4
post-thumbnail

Intro

이전에 로그 시스템을 Cloudtype으로 구축했으나 최근 정책 변경으로 인해 Cloudtype으로 운용하기 어려워졌다. 따라서 로그 시스템을 ec2 환경으로 다시 구축해보겠다. 대략적인 구조는 다음과 같다.

test 서버(free tier) 환경 (mem: 1GB, swap mem: 2GB)
docker compose로 was 환경과 monitoring 환경을 관리한다.
was: nginx, spring boot(tomcat)
monitor: grafana, loki, prometheus, promtail

로그 시스템 도입

실서버에서 스프링 서버는 logback logging framework를 사용하여 로그를 포멧팅하고 레벨별로 나누어 file에 저장하고 있었다. 이 로그 파일을 promtail가 읽어 라벨링하여 Loki로 전달하게 하고, Grafana가 Loki를 통해 로그를 수집하도록 구축했다.

# docker-compose.yml
version: '3'

services:
  prometheus: # 현재 게시글에선 필요없는 부분
    image: prom/prometheus
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    command:
      - --config.file=/etc/prometheus/prometheus.yml
    networks:
      - prometheus

  loki:
    image: grafana/loki
    container_name: loki
    user: "$UID:$GID"
    ports:
      - "3100:3100"
    volumes:
      - ./loki/local-config.yaml:/etc/loki/local-config.yaml
      - ./loki/data:/var/loki
    command: -config.file=/etc/loki/local-config.yaml
    networks:
      - loki

  promtail:
    image: grafana/promtail
    container_name: promtail
    volumes:
      - /home/ubuntu/was/logs:/logs
      - ./promtail/promtail-config.yml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml
    depends_on:
      - loki
    networks:
      - loki

  grafana:
    image: grafana/grafana
    container_name: grafana
    user: "$UID:$GID"
    ports:
      - "3000:3000"
    volumes:
      - ./grafana:/var/lib/grafana
    depends_on:
      - prometheus
      - loki
    networks:
      - prometheus
      - loki

networks:
  prometheus:
    driver: bridge
  loki:
    driver: bridge

Loki

promtail이 push한 로그를 수집하고 저장한다. 컨테이너 내부에서 실행되는 프로세스의 사용자와 그룹을 호스트의 UID와 GID로 설정하여 마운트한 파일 접근 권한을 부여하고, config 파일과 data 파일을 바인딩 마운트한다. 따라서 loki가 실행될 때 config 파일을 적용할 수 있도록 커멘드를 작성하고, 네트워크를 구성하여 grafana와 통신하도록 구성한다.

# local-config.yaml
# 사용자 인증 활성화 여부
auth_enabled: false

# Loki 서버의 포트번호
server:
  http_listen_port: 3100

# Loki 인스턴스의 주소
common:
  instance_addr: 127.0.0.1
  path_prefix: /var/loki
  storage:
    filesystem:  # 파일시스템 기반 저장소를 사용
      chunks_directory: /var/loki/chunks
      rules_directory: /var/loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory

# 쿼리 범위에 대한 설정
query_range:
  results_cache:
    cache:
      embedded_cache:
        enabled: true
        max_size_mb: 100

# Loki의 스키마 설정
schema_config:
  configs:
    - from: 2020-10-24
      store: tsdb
      object_store: filesystem
      schema: v12
      index:
        prefix: index_
        period: 24h

ruler:
  alertmanager_url: http://localhost:9093
  • local에 적용할 config 파일은 공식 문서에서 받을 수 있다.
  • 공식 문서는 path_prefix를 /tmp/loki로 되어있지만 /var/loki로 구성한 이유는 /tmp 경로는 임시 파일 및 디렉토리를 저장하는 데 사용되기에 임시 파일, 캐시, 일시적인 데이터 등을 저장하는 목적으로 적합하다고 판단했기 때문이다.
  • store를 tsdb로 사용했다. Loki는 청크와 인덱스라는 두 가지 유형의 데이터를 저장해야 하는데 Loki 2.0부터 하나의 single store로 관리된다. 대표적이 single store로 tsdb가 있다.

promtail

서버에 저장된 로그를 Loki로 보내기 위해 필요한 서비스이다. log 파일과 config 파일을 바인드 마운트하고 promtail이 실행될 때 config 파일이 등록되도록 구성한다. Loki가 뜬 이후에 서비스를 띄우도록 하여 promtail이 loki로 push할 때 fail이 뜨는 경우가 안 나오도록 설정했다. 마지막으로 loki 네트워크에 참여하여 통신하도록 구성했다.

# 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:
  - job_name: info
    static_configs:
      - targets:
          - localhost
        labels:
          job: test_info_logs
          __path__: /logs/*/info-*.log
  - job_name: warn
    static_configs:
      - targets:
          - localhost
        labels:
          job: test_warn_logs
          __path__: /logs/*/warn-*.log
  - job_name: error
    static_configs:
      - targets:
          - localhost
        labels:
          job: test_error_logs
          __path__: /logs/*/error-*.log
  • config 파일은 공식 문서에서 받을 수 있다.
  • 각 로그별 job을 생성한다.

grafana

Loki에서 수집한 로그를 받아 시각하기 위한 서비스로 loki가 뜬 이후로 실행한다. grafana 설정 정보를 바인드 마운트하여 혹시나 컨테이너가 삭제될 경우를 대비했다.

docker compose로 로그 시스템을 띄운 뒤 grafana 연동 작업은 다음과 같다.

  1. Administration → Data sources → Add data source → Loki 선택 → HTTP url에 Loki 주소 입력(http://loki:3100) → 등록
  2. dashboard나 explore로 로그를 잘 받아오는지 확인

트러블슈팅

  • 그라파나로 연동하는 과정에서 data source가 등록되지 않는 이슈가 발생했다. 나와 비슷한 문제를 겪는 사람들이 있어서 찾아보니 loki가 promtail 메트릭을 수집할게 없어서 발생한 문제였다. 위 링크의 조언대로 system log를 전송하도록 구성 파일을 바꾸니 정상적으로 data source가 등록됐다. 나중에 확인해보니 로그를 수집할 대상 파일 경로에 ~를 넣어서 경로 인식을 제대로 못 했었던 것이다.(system log를 전송하지 않아서 발생한 문제가 아니었던 것이다)

  • loki가 로그를 수집하지 못하면 보통 promtail 설정 문제이다.(보통 에러는 promtail에 error log로 확인 가능하지만 불친절하다. error log가 아예 뜨지 않는 경우도 있다)

다음으로

이렇게 간단하게 로그 시스템을 구축해보았다. 하지만 우리 목표는 단순히 로그 시스템을 구축하는 것이 아니다. 다음은 앞으로 달성할 과제들이다.

  • log를 json 형식으로 파싱하여 보내기
    • logback 활용
  • promtail에서 json 형식의 로그를 labeling하여 로그를 보기 좋게 구성
    • promtail의 pipeline stage 요소 활용
  • 쿼리 호출 시간 기록
    • aop를 활용하여 repository단의 쿼리 호출 시간을 기록하여 grafana에 평균 쿼리 호출 시간을 출력하도록 구성
    • 쿼리 튜닝 이전, 이후 개선을 정량적으로 확인하기 위함

다음 게시글에서 위 과제를 해결해보자!

참고

profile
Backend-Developer

0개의 댓글