> Hello World !!!

     

@syaku

관측성(Observability) 모니터링 설치

728x90
반응형

엔드포인트

서비스 용도 URL

Prometheus UI http://localhost:9090
  타겟 수집 상태 http://localhost:9090/targets
  헬스체크 http://localhost:9090/-/healthy
  메트릭 http://localhost:9090/metrics
  설정 확인 http://localhost:9090/config
  설정 reload (POST) http://localhost:9090/-/reload
Loki 헬스체크 http://localhost:3100/ready
  메트릭 http://localhost:3100/metrics
  로그 push (POST) http://localhost:3100/loki/api/v1/push
  로그 쿼리 http://localhost:3100/loki/api/v1/query_range
  레이블 목록 http://localhost:3100/loki/api/v1/labels
  설정 확인 http://localhost:3100/config
  서비스 상태 http://localhost:3100/services
Grafana UI http://localhost:3000
  헬스체크 http://localhost:3000/api/health
  메트릭 http://localhost:3000/metrics
Jaeger UI http://localhost:16686
  헬스체크 http://localhost:14269
  메트릭 http://localhost:14269/metrics
  OTLP gRPC 수신 http://localhost:4317
  OTLP HTTP 수신 http://localhost:4318
Spring Boot 헬스체크 http://localhost:{port}/actuator/health
  메트릭 http://localhost:{port}/actuator/prometheus

Docker 기반 모니터링 설치 및 설정

네트워크 생성

docker network create monitoring

docker-compose.yml

services:
  # <http://localhost:16686>
  jaeger:
    image: jaegertracing/jaeger:latest
    container_name: jaeger
    volumes:
      - ./jaeger/jaeger-config.yml:/etc/jaeger/config.yml:ro
      - badger-data:/badger
    command: ["--config=/etc/jaeger/config.yml"]
    ports:
      - "16686:16686"  # Jaeger UI
      - "4317:4317"    # OTLP gRPC
      - "4318:4318"    # OTLP HTTP
      - "14269:14269"  # metrics / healthcheck
    networks:
      - monitoring
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--web.enable-lifecycle'               # 설정 hot-reload 활성화 (선택)
    environment:
      - DOCKER_API_VERSION=1.44
    networks:
      - monitoring
  loki:
    image: grafana/loki:2.9.3
    container_name: loki
    volumes:
      - ./loki/loki-config.yml:/etc/loki/loki-config.yml
      - loki-data:/loki
    command: -config.file=/etc/loki/loki-config.yml
    # user: "0"                        # 권한 문제 방지용 (개발환경)
    ports:
      - "3100:3100"
    networks:
      - monitoring
  # <http://localhost:3000>
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_PATHS_PROVISIONING=/etc/grafana/provisioning   # provisioning 경로 명시
    volumes:
      - ./grafana/provisioning:/etc/grafana/provisioning  # 데이터소스 + 대시보드 자동 설정
      - ./grafana/dashboards:/var/lib/grafana/dashboards  # 대시보드 JSON 파일
      - grafana-data:/var/lib/grafana
    networks:
      - monitoring
    depends_on:
      - prometheus
      - loki
      - jaeger
volumes:
  badger-data:
  prometheus-data:
  loki-data:
  grafana-data:
networks:
  monitoring:
    external: true

Jaeger 설정

jaeger/jaeger-config.yml

extensions:
  jaeger_storage:
    backends:
      badger_storage:
        badger: {}          # 기본값으로 /badger 경로 사용 — 볼륨 마운트와 자동 매칭
        # badger:
        #   # ── 데이터 저장 경로 ──────────────────────
        #   directories:
        #     keys: /badger/keys       # 기본값: /badger/keys
        #     values: /badger/values   # 기본값: /badger/values

        #   # ── 데이터 보존 ───────────────────────────
        #   ttl: 72h                   # 스팬 보존 기간. 기본값: 72h

        #   # ── 유지보수 ──────────────────────────────
        #   maintenance_interval: 15m  # GC 실행 주기. 기본값: 15m
        #   metrics_update_interval: 10s  # Badger 내부 메트릭 갱신 주기. 기본값: 10s

  jaeger_query:
    storage:
      traces: badger_storage
    ui:
      log_access: true
    http:
      endpoint: 0.0.0.0:16686
    grpc:
      endpoint: 0.0.0.0:16685

  healthcheckv2:
    use_v2: true
    http:
      endpoint: 0.0.0.0:14269   # metrics / healthcheck 포트

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:

exporters:
  jaeger_storage_exporter:
    trace_storage: badger_storage

service:
  extensions: [jaeger_storage, jaeger_query, healthcheckv2]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [jaeger_storage_exporter]

Jaeger v2 Badger 설정 옵션

옵션 기본값 설명

directories.keys /badger/keys 스팬 키 저장 경로
directories.values /badger/values 스팬 값 저장 경로
ttl 72h 이 기간이 지난 스팬은 자동 삭제. 디스크 용량 관리의 핵심
maintenance_interval 15m Badger GC(가비지 컬렉션) 실행 주기
metrics_update_interval 10s Prometheus 메트릭 갱신 주기

Prometheus 설정

prometheus/prometheus.yml

global:
  scrape_interval: 15s      # 수집 주기
  evaluation_interval: 15s  # 룰 평가 주기

scrape_configs:

  # Prometheus 자기 자신 수집
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # Loki 메트릭 수집
  - job_name: 'loki'
    static_configs:
      - targets: ['loki:3100']

  # Grafana 메트릭 수집
  - job_name: 'grafana'
    static_configs:
      - targets: ['grafana:3000']

  # Jaeger 메트릭 수집
  - job_name: 'jaeger'
    static_configs:
      - targets: ['jaeger:14269']

Loki 설정

loki/loki-config.yml

auth_enabled: false

server:
  http_listen_port: 3100

common:
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: 2024-01-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

compactor:
  working_directory: /loki/compactor
  shared_store: filesystem
  retention_enabled: true
  delete_request_store: filesystem

limits_config:
  retention_period: 72h

Grafana 설정

grafana/provisioning/datasources/datasources.yml

apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: <http://prometheus:9090>
    isDefault: true
    editable: true
    jsonData:
      httpMethod: POST
      timeInterval: 15s   # prometheus scrape_interval 과 동일하게

  - name: Loki
    type: loki
    access: proxy
    url: <http://loki:3100>
    editable: true

  - name: Jaeger
    type: jaeger
    access: proxy
    url: <http://jaeger:16686>
    editable: true

grafana/provisioning/dashboards/dashboards.yml

apiVersion: 1

providers:
  - name: default
    type: file
    disableDeletion: false
    updateIntervalSeconds: 30   # 파일 변경 시 자동 반영 주기
    options:
      path: /var/lib/grafana/dashboards

🔴 필수 항목 모니터링 수집 연동 (macOS, Docker 기반 테스트)

📋 필수 항목 작업 목록

작업 대상 방식

로그 수집 에이전트 설치 macOS (Spring, Nginx) Grafana Alloy (Homebrew)
시스템 메트릭 수집 macOS Node Exporter (Homebrew)
MariaDB Exporter Docker mysqld_exporter 컨테이너 추가
Redis Exporter Docker redis_exporter 컨테이너 추가
ActiveMQ Exporter Docker jmx_exporter sidecar 추가
Spring 분산 트레이싱 Spring App OpenTelemetry Java Agent
Grafana Alert 설정 Grafana Alert Rules + Slack 채널

Grafana Alloy 설치 (로그 수집 — macOS)

Promtail EOL 대안으로 Alloy를 사용합니다.

brew install grafana/grafana/alloy

Alloy 설정 파일 작성

설정 파일 위치 변경: alloy run /Volumes/Develop/workspace/docker/monitoring/config.alloy

/opt/homebrew/etc/alloy/config.alloy 생성:

// ── Loki 전송 대상 ──────────────────────────────────────────
loki.write "default" {
  endpoint {
    url = "<http://localhost:3100/loki/api/v1/push>"
  }
}

// ── Spring App 로그 수집 ────────────────────────────────────
local.file_match "spring_logs" {
  path_targets = [{
    __path__ = "/var/log/app/*.log",
    job       = "spring",
  }]
}

loki.source.file "spring" {
  targets    = local.file_match.spring_logs.targets
  forward_to = [loki.process.spring_parse.receiver]
}

loki.process "spring_parse" {
  stage.json {
    expressions = {
      level  = "level",
      logger = "logger",
      msg    = "msg",
    }
  }
  stage.labels {
    values = {
      level  = "",
      logger = "",
    }
  }
  stage.output {
    source = "msg"
  }
  forward_to = [loki.write.default.receiver]
}

// ── Nginx 로그 수집 ─────────────────────────────────────────
local.file_match "nginx_logs" {
  path_targets = [{
    __path__ = "/opt/homebrew/var/log/nginx/*.log",
    job       = "nginx",
  }]
}

loki.source.file "nginx" {
  targets    = local.file_match.nginx_logs.targets
  forward_to = [loki.write.default.receiver]
}

Alloy 서비스 실행

# 서비스 등록 및 시작
brew services start alloy

# 상태 확인
brew services info alloy

# 로그 확인
tail -f /opt/homebrew/var/log/alloy/alloy.log

Node Exporter 설치 (시스템 메트릭 — macOS)

Docker 내부에서 실행하면 호스트 메트릭이 제대로 수집되지 않으므로, macOS에 직접 설치합니다.

# 설치
brew install node_exporter

# 시작
brew services start node_exporter

혹은

node_exporter

# 확인 (기본 포트 9100)
curl <http://localhost:9100/metrics> | head -20

Prometheus 설정에 Node Exporter 추가

기존 prometheus.yml에 아래 job 추가:

scrape_configs:
  # ... 기존 spring-apps, spring-eureka job ...

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['host.docker.internal:9100']
        labels:
          instance: 'macos-host'

Docker 미들웨어 Exporter 추가

기존 docker-compose.yml에 아래 서비스들을 추가합니다.

MariaDB Exporter

먼저 MariaDB에 exporter 전용 계정 생성:

CREATE USER 'exporter'@'%' IDENTIFIED BY 'exporter_password';

GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'%';

// MariaDB 10.5+ / MySQL 계열
GRANT SLAVE MONITOR ON *.* TO 'exporter'@'%';

or

GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'exporter'@'%';

FLUSH PRIVILEGES;

SHOW SLAVE STATUS;

SHOW REPLICA STATUS;

mysql_exporter.cnf 파일 생성:

[client]
user=exporter
password=exporter_password

docker-compose.yml에 추가:

  mysqld-exporter:
    image: prom/mysqld-exporter:latest
    command:
      - "--config.my-cnf=/etc/mysql_exporter.cnf"
      - "--mysqld.address=mariadb:3306"   # mariadb 서비스명으로 변경
    volumes:
      - ./mysql_exporter.cnf:/etc/mysql_exporter.cnf:ro
    ports:
      - "9104:9104"
    networks:
      - monitoring
    depends_on:
      - mariadb   # 실제 서비스명으로 변경

Redis Exporter

  redis-exporter:
    image: oliver006/redis_exporter:latest
    environment:
      REDIS_ADDR: "redis:6379"   # redis 서비스명으로 변경
      # REDIS_PASSWORD: "your_password"  # 비밀번호 있을 경우 활성화
    ports:
      - "9121:9121"
    networks:
      - monitoring
    depends_on:
      - redis   # 실제 서비스명으로 변경

ActiveMQ Exporter (JMX Exporter)

ActiveMQ는 전용 exporter가 없으므로 jmx_exporter를 sidecar 방식으로 구성합니다.

activemq_jmx_config.yaml 파일 생성:

hostPort: activemq:1099
lowercaseOutputName: true
whitelistObjectNames:
  - "org.apache.activemq:destinationType=Queue,*"
  - "org.apache.activemq:destinationType=Topic,*"
  - "org.apache.activemq:type=Broker,brokerName=*"
rules:
  - pattern: 'org.apache.activemq<type=Broker, brokerName=(\\\\S*), destinationType=Queue, destinationName=(\\\\S*)><>(.*)'
    name: activemq_queue_$3
    attrNameSnakeCase: true
    labels:
      destination: $2
  - pattern: 'org.apache.activemq<type=Broker, brokerName=(\\\\S*)><>CurrentConnectionsCount'
    name: activemq_connections
    type: GAUGE
  - pattern: 'org.apache.activemq<type=Broker, brokerName=(\\\\S*)><>Total(.*)Count'
    name: activemq_$2_total
    type: COUNTER

docker-compose.yml에 추가:

  activemq-exporter:
    image: bitnami/jmx-exporter:latest
    command:
      - "9404"
      - /etc/jmx/config.yaml
    volumes:
      - ./activemq_jmx_config.yaml:/etc/jmx/config.yaml:ro
    ports:
      - "9404:9404"
    networks:
      - monitoring
    depends_on:
      - activemq   # 실제 서비스명으로 변경

ActiveMQ 컨테이너에 JMX 포트가 열려있어야 합니다. 기존 activemq 서비스에 환경변수 추가:

environment:
  ACTIVEMQ_JMX: "1099"
  ACTIVEMQ_OPTS: "-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"

Prometheus에 미들웨어 Exporter job 추가

prometheus/prometheus.yml

  - job_name: 'mysqld-exporter'
    static_configs:
      - targets: ['mysqld-exporter:9104']
        labels:
          service: 'mariadb'

  - job_name: 'redis-exporter'
    static_configs:
      - targets: ['redis-exporter:9121']
        labels:
          service: 'redis'

  - job_name: 'activemq-exporter'
    static_configs:
      - targets: ['activemq-exporter:9404']
        labels:
          service: 'activemq'

Spring 분산 트레이싱 (OTel Agent → Jaeger)

프로토콜 기본 포트 엔드포인트 형식

grpc 4317 http://host:4317
http/protobuf 4318 http://host:4318/v1/traces

OpenTelemetry Java Agent 다운로드

curl -L <https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar> \\
  -o ~/agents/opentelemetry-javaagent.jar

Spring 앱 실행 시 Agent 적용

각 Spring 서비스 실행 옵션에 추가:

java \\
  -javaagent:~/agents/opentelemetry-javaagent.jar \\
  -Dotel.service.name=my-service-name \\
  -Dotel.exporter.otlp.endpoint=http://localhost:4317 \\
  -Dotel.exporter.otlp.protocol=grpc \\
#  -Dotel.exporter.otlp.endpoint=http://localhost:4318 \\
#  -Dotel.exporter.otlp.protocol=http/protobuf \\
  -Dotel.logs.exporter=none \\
  -jar my-app.jar

Spring Boot application.yml 설정

management:
  tracing:
    sampling:
      probability: 1.0   # 개발환경 100% 샘플링
  endpoints:
    web:
      exposure:
        include: prometheus, health, info

Jaeger가 OTLP 포트(4317)를 수신하는지 docker-compose 확인:

  jaeger:
    image: jaegertracing/jaeger:latest
    ports:
      - "16686:16686"   # UI
      - "4317:4317"     # OTLP gRPC
      - "4318:4318"     # OTLP HTTP

Grafana Alert 설정

Slack Notification Channel 연동

Grafana UI → Alerting → Contact points → Add contact point:

Name: slack-alert
Type: Slack
Webhook URL: <https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK>

필수 Alert Rule 생성

Grafana UI → Alerting → Alert rules → New alert rule:

Alert 명 PromQL 임계치

5xx 오류율 `sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))  
/    
sum(rate(http_server_requests_seconds_count[5m]))    
100` > 1%  
CPU 사용률 (1 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))) * 100 > 70%
메모리 사용률 (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 80%
ActiveMQ DLQ activemq_queue_queue_size{destination=~".*DLQ.*"} > 0
HikariCP pending hikaricp_connections_pending > 5

✅ 필수 항목 최종 동작 확인 체크리스트

# 1. Node Exporter 메트릭 확인
curl <http://localhost:9100/metrics> | grep node_cpu

# 2. Spring Actuator Prometheus 확인
curl <http://localhost>:{서비스포트}/actuator/prometheus | grep hikaricp

# 3. MariaDB Exporter 확인
curl <http://localhost:9104/metrics> | grep mysql_up

# 4. Redis Exporter 확인
curl <http://localhost:9121/metrics> | grep redis_up

# 5. ActiveMQ Exporter 확인
curl <http://localhost:9404/metrics> | grep activemq

# 6. Prometheus Targets 확인 (브라우저)
# <http://localhost:9090/targets> → 모든 job이 UP 상태인지 확인

# 7. Loki 로그 수집 확인 (Grafana → Explore → Loki)
# {job="spring"} | json 쿼리로 로그 유입 확인

Grafana 기본 대시보드 추천: Prometheus node exporter ID 1860, Spring Boot JVM ID 4701, Redis ID 763, MySQL Overview ID 7362를 Grafana → Dashboards → Import에서 바로 불러올 수 있습니다.


🟡 권장 항목 모니터링 수집 연동 (macOS, Docker 기반 테스트)

📋 권장 항목 작업 목록

작업 대상 방식

Spring 인증/인가 실패 로그 필터링 Alloy config 수정 stage.match 파이프라인 추가
SQL 쿼리 로깅 (P6Spy) Spring App 의존성 추가 + 설정 파일
Disk I/O Alert 추가 Grafana PromQL Alert Rule 등록
HTTP Endpoint별 RPS 패널 Grafana PromQL Panel 등록
Slow Query 임계치 Alert Grafana Alert Rule 등록
스레드 상태 Alert Grafana Alert Rule 등록
MariaDB Slow Query 로그 활성화 Docker mariadb 서비스 command 옵션 추가
MariaDB InnoDB 메트릭 활성화 mysqld_exporter --collect.info_schema.innodb_metrics 옵션 추가
Nginx stub_status 활성화 macOS Nginx nginx.conf 수정
nginx-prometheus-exporter 설치 macOS Homebrew 설치 및 실행
ActiveMQ Consumer Lag 패널 Grafana PromQL Panel 등록
Redis Eviction Alert 추가 Grafana Alert Rule 등록
배포 Annotation 스크립트 작성 배포 스크립트 Grafana API 호출 추가

📁 로그 수집 — 권장 항목

인증/인가 실패 로그 (401/403)

별도 파일 수집보다 기존 Spring 로그에서 필터링하는 방식이 효율적입니다. Alloy 설정에 파이프라인 stage만 추가합니다.

config.alloy의 loki.process "spring_parse" 블록에 추가:

  stage.match {
    selector = "{job=\\\\"spring\\\\"}"
    pipeline_name = "auth_failure"
    stage.regex {
      expression = "(?P<status>401|403)"
    }
    stage.labels {
      values = {
        auth_failure = "true",
      }
    }
  }

Grafana에서 {job="spring", auth_failure="true"} 쿼리로 401/403 급증 감지 Alert 추가.


SQL 쿼리 로깅 (P6Spy / Hibernate)

Spring build.gradle에 P6Spy 의존성 추가:

implementation 'p6spy:p6spy:3.9.1'

src/main/resources/spy.properties 생성:

appender=com.p6spy.engine.spy.appender.FileLogger
logfile=/var/log/app/sql.log
logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
customLogMessageFormat=%(currentTime)|%(executionTime)ms|%(category)|%(sqlSingleLine)
filter=true
execution=true

application.yml datasource url 변경:

spring:
  datasource:
    # 기존: jdbc:mariadb://localhost:3306/dbname
    url: jdbc:p6spy:mariadb://localhost:3306/dbname
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver

Alloy에서 /var/log/app/sql.log 추가 수집:

local.file_match "sql_logs" {
  path_targets = [{
    __path__ = "/var/log/app/sql.log",
    job       = "sql",
  }]
}

loki.source.file "sql" {
  targets    = local.file_match.sql_logs.targets
  forward_to = [loki.write.default.receiver]
}

🖥️ 하드웨어 — Disk I/O 메트릭

Node Exporter 설치 시 기본으로 수집됩니다. Prometheus에 별도 설정 없이 아래 Alert만 추가하면 됩니다.

Grafana Alert Rules에 추가:

# Disk I/O 읽기 속도 (MB/s)
rate(node_disk_read_bytes_total[5m]) / 1024 / 1024

# Disk I/O 쓰기 속도 (MB/s)
rate(node_disk_written_bytes_total[5m]) / 1024 / 1024

Alert 명 PromQL 임계치

Disk Read 과부하 rate(node_disk_read_bytes_total[5m]) / 1024 / 1024 > 100 MB/s
Disk Write 과부하 rate(node_disk_written_bytes_total[5m]) / 1024 / 1024 > 100 MB/s

☕ JVM/Spring — 권장 항목

HTTP Endpoint별 RPS

Spring Boot Actuator가 http_server_requests_seconds_count 메트릭을 기본 제공하므로 별도 설정 없이 PromQL만 작성하면 됩니다.

Grafana Panel PromQL:

# 엔드포인트별 RPS
sum by (uri, method) (
  rate(http_server_requests_seconds_count[1m])
)

# 엔드포인트별 에러율
sum by (uri) (
  rate(http_server_requests_seconds_count{status=~"5.."}[5m])
)
/ sum by (uri) (
  rate(http_server_requests_seconds_count[5m])
) * 100

Slow Query 임계치 Alert

Spring Actuator의 Timer 메트릭을 활용합니다:

# P95 응답시간이 1초 초과인 엔드포인트 감지
histogram_quantile(0.95,
  sum by (uri, le) (
    rate(http_server_requests_seconds_bucket[5m])
  )
) > 1

Grafana Alert Rule:

Alert: SlowEndpointDetected
Condition: histogram_quantile(0.95, ...) > 1
For: 5m
Message: "{{ $labels.uri }} P95 응답시간 초과"

스레드 수 (BLOCKED/WAITING)

Actuator 기본 제공 메트릭 jvm_threads_states_threads 활용:

# BLOCKED 스레드 수
jvm_threads_states_threads{state="blocked"}

# WAITING 스레드 수
jvm_threads_states_threads{state="waiting"}

Grafana Alert:

Alert 명 PromQL 임계치

BLOCKED 스레드 급증 jvm_threads_states_threads{state="blocked"} > 10
전체 스레드 포화 jvm_threads_live_threads > 200

🗄️ 미들웨어 — 권장 항목

MariaDB Slow Query 수집

MariaDB 컨테이너에 Slow Query 로그 활성화. 기존 docker-compose.yml의 mariadb 서비스에 추가:

  mariadb:
    # 기존 설정 유지...
    command:
      - --slow-query-log=1
      - --slow-query-log-file=/var/log/mysql/slow.log
      - --long-query-time=1
    volumes:
      - ./mysql-logs:/var/log/mysql

mysqld_exporter에 slow query 수집 옵션 추가:

  mysqld-exporter:
    command:
      - "--config.my-cnf=/etc/mysql_exporter.cnf"
      - "--mysqld.address=mariadb:3306"
      - "--collect.global_status"
      - "--collect.info_schema.innodb_metrics"   # InnoDB 버퍼풀 포함
      - "--collect.perf_schema.eventsstatements" # 슬로우 쿼리 통계
      - "--exporter.log_slow_filter"             # exporter 자체 쿼리는 slow log 제외

Slow query Alloy 수집 (config.alloy에 추가):

local.file_match "mysql_slow" {
  path_targets = [{
    __path__ = "/absolute/path/to/mysql-logs/slow.log",
    job       = "mysql-slow",
  }]
}

loki.source.file "mysql_slow_log" {
  targets    = local.file_match.mysql_slow.targets
  forward_to = [loki.write.default.receiver]
}

MariaDB InnoDB Buffer Pool Hit Rate

mysqld_exporter에서 --collect.info_schema.innodb_metrics 활성화 시 자동 수집됩니다. Grafana Panel PromQL:

# Buffer Pool Hit Rate (95% 이상 유지 권장)
(
  1 - (
    rate(mysql_global_status_innodb_buffer_pool_reads_total[5m]) /
    rate(mysql_global_status_innodb_buffer_pool_read_requests_total[5m])
  )
) * 100

Alert: 히트율 < 95% 지속 시 알림.


Nginx Exporter 설치 (macOS)

Nginx stub_status 모듈 활성화 → nginx_exporter 설치 순서로 진행합니다.

1) Nginx stub_status 설정 (/opt/homebrew/etc/nginx/nginx.conf에 추가):

server {
    listen 8080;
    server_name localhost;

    location /stub_status {
        stub_status on;
        allow 127.0.0.1;
        deny all;
    }
}
brew services restart nginx
curl <http://localhost:8080/stub_status>  # 확인

2) nginx-prometheus-exporter 설치:

brew tap nginx/tap
brew install nginx-prometheus-exporter

# 실행 (stub_status 주소 지정)
nginx-prometheus-exporter -nginx.scrape-uri=http://localhost:8080/stub_status &

# 백그라운드 서비스로 등록하려면 LaunchAgent 생성
# 포트 기본값: 9113
curl <http://localhost:9113/metrics> | grep nginx_connections

3) Prometheus job 추가:

  - job_name: 'nginx-exporter'
    static_configs:
      - targets: ['host.docker.internal:9113']
        labels:
          service: 'nginx'

ActiveMQ 메시지 처리 지연 / Consumer Lag

jmx_exporter를 이미 설정했다면 아래 PromQL로 Grafana Panel 구성합니다:

# 큐에 대기 중인 메시지 수 (consumer lag)
activemq_queue_queue_size

# Enqueue vs Dequeue 차이 (처리 속도 차이)
rate(activemq_queue_enqueue_count[5m]) - rate(activemq_queue_dequeue_count[5m])

Alert: activemq_queue_queue_size > 100 조건으로 Consumer Lag 급증 탐지.


Redis Evicted Keys

redis_exporter 설치 시 기본 수집됩니다. Grafana Alert:

# Evicted Keys 증가율
rate(redis_evicted_keys_total[5m]) > 0

📊 Grafana — 배포 시점 Annotation

Grafana Annotations API를 활용해 배포 시 수직선을 자동으로 그립니다.

1) Grafana API Key 발급: Grafana UI → Administration → Service accounts → Add service account token

2) 배포 스크립트에 Annotation 호출 추가:

#!/bin/bash
GRAFANA_URL="<http://localhost:3000>"
GRAFANA_TOKEN="your_service_account_token"
SERVICE_NAME="my-service"
VERSION=$(git describe --tags --always)

curl -s -X POST "${GRAFANA_URL}/api/annotations" \\\\
  -H "Authorization: Bearer ${GRAFANA_TOKEN}" \\\\
  -H "Content-Type: application/json" \\\\
  -d "{
    \\\\"text\\\\": \\\\"Deploy: ${SERVICE_NAME} ${VERSION}\\\\",
    \\\\"tags\\\\": [\\\\"deployment\\\\", \\\\"${SERVICE_NAME}\\\\"],
    \\\\"time\\\\": $(date +%s)000
  }"

3) Grafana Dashboard Annotation 소스 등록:

Dashboard Settings → Annotations → Add annotation query:

Data source: -- Grafana --
Filter by tags: deployment

📋 권장 항목 추가 후 Alert 전체 목록

Alert 명 PromQL 요약 임계치

Slow Endpoint P95 histogram_quantile(0.95, ...) > 1s
BLOCKED 스레드 jvm_threads_states_threads{state="blocked"} > 10
Disk 쓰기 I/O rate(node_disk_written_bytes_total[5m]) > 100 MB/s
InnoDB Hit Rate Buffer pool 히트율 < 95%
Consumer Lag activemq_queue_queue_size > 100
Redis Eviction rate(redis_evicted_keys_total[5m]) > 0
Auth Failure 급증 {job="spring", auth_failure="true"} rate > 10/min

Grafana 권장 대시보드 Import ID: Nginx Exporter 12708, ActiveMQ JMX 8050, MySQL InnoDB 7371 을 Dashboards → Import에서 바로 사용할 수 있습니다.


✅ 권장 항목 최종 동작 확인 체크리스트

# ── 로그 수집 ───────────────────────────────────────────────

# 1. Auth Failure 라벨 확인 (Grafana → Explore → Loki)
# 쿼리: {job="spring", auth_failure="true"}
# 401/403 응답 로그가 유입되는지 확인

# 2. SQL 로그 파일 생성 확인
tail -f /var/log/app/sql.log
# P6Spy 포맷: timestamp|executionTime|category|sql 형태 출력 확인

# 3. Alloy SQL 로그 수집 확인 (Grafana → Explore → Loki)
# 쿼리: {job="sql"}

# ── 시스템 메트릭 ────────────────────────────────────────────

# 4. Disk I/O 메트릭 확인 (Node Exporter 기본 제공)
curl <http://localhost:9100/metrics> | grep node_disk_read_bytes_total

# ── Spring JVM 메트릭 ────────────────────────────────────────

# 5. HTTP RPS 메트릭 확인
curl <http://localhost>:{서비스포트}/actuator/prometheus | grep http_server_requests_seconds_count

# 6. 스레드 상태 메트릭 확인
curl <http://localhost>:{서비스포트}/actuator/prometheus | grep jvm_threads_states_threads

# ── 미들웨어 ─────────────────────────────────────────────────

# 7. MariaDB Slow Query 로그 파일 생성 확인
tail -f ./mysql-logs/slow.log

# 8. InnoDB 메트릭 수집 확인
curl <http://localhost:9104/metrics> | grep innodb_buffer_pool

# 9. Nginx stub_status 응답 확인
curl <http://localhost:8080/stub_status>
# 출력 예시:
# Active connections: 1
# server accepts handled requests
#  3 3 3

# 10. nginx-prometheus-exporter 메트릭 확인
curl <http://localhost:9113/metrics> | grep nginx_connections_active

# 11. ActiveMQ Consumer Lag 메트릭 확인
curl <http://localhost:9404/metrics> | grep activemq_queue_queue_size

# 12. Redis Evicted Keys 메트릭 확인
curl <http://localhost:9121/metrics> | grep redis_evicted_keys_total

# ── Grafana ──────────────────────────────────────────────────

# 13. Prometheus Targets 전체 UP 확인 (브라우저)
# <http://localhost:9090/targets>
# nginx-exporter job 포함 모든 job이 State: UP인지 확인

# 14. 배포 Annotation 등록 테스트
curl -X POST "<http://localhost:3000/api/annotations>" \\\\
  -H "Authorization: Bearer {your_token}" \\\\
  -H "Content-Type: application/json" \\\\
  -d '{"text":"Deploy test annotation","tags":["deployment"],"time":'$(date +%s)'000}'
# 응답: {"id": 1, "message": "Annotation added"} 확인

# 15. Grafana Dashboard Import 확인 (브라우저)
# Nginx Exporter ID: 12708
# MySQL InnoDB ID:   7371
# ActiveMQ JMX ID:   8050
# → Dashboards → Import에서 패널 정상 렌더링 확인
728x90
반응형