관측성(Observability) 모니터링 설치
엔드포인트
서비스 용도 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에서 패널 정상 렌더링 확인
'개발' 카테고리의 다른 글
| 관측성 (Observability) 모니터링 설계 (1) | 2026.04.25 |
|---|---|
| 내가 이해한 MCP의 필요성 정리 (1) | 2025.05.15 |
| alpine linux puppeteer 실행 오류 (0) | 2025.01.03 |
| Fabric.js에서 수직 정렬과 크기 조절이 가능한 TextboxVertical 구현하기 (1) | 2024.12.19 |