diff --git a/.github/deployments/monitoring/docker-compose.yml b/.github/deployments/monitoring/docker-compose.yml index a226f57c..75545da2 100644 --- a/.github/deployments/monitoring/docker-compose.yml +++ b/.github/deployments/monitoring/docker-compose.yml @@ -47,6 +47,7 @@ services: - GF_ANALYTICS_REPORTING_ENABLED=false volumes: - grafana-data:/var/lib/grafana + - ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources:ro depends_on: - prometheus - loki @@ -71,6 +72,21 @@ services: networks: - monitoring-network + promtail: + image: grafana/promtail:2.9.0 + container_name: ${CONTAINER_PREFIX:-plum}-promtail + restart: always + volumes: + - ./promtail/promtail-config.yaml:/etc/promtail/promtail-config.yaml:ro + - /var/log:/var/log:ro + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /var/run/docker.sock:/var/run/docker.sock:ro + command: -config.file=/etc/promtail/promtail-config.yaml + networks: + - monitoring-network + depends_on: + - loki + volumes: prometheus-data: driver: local diff --git a/.github/deployments/monitoring/grafana/provisioning/datasources/datasources.yml b/.github/deployments/monitoring/grafana/provisioning/datasources/datasources.yml new file mode 100644 index 00000000..f7e5e5de --- /dev/null +++ b/.github/deployments/monitoring/grafana/provisioning/datasources/datasources.yml @@ -0,0 +1,24 @@ +# Grafana 데이터소스 자동 프로비저닝 +# Prometheus와 Loki를 자동으로 등록 + +apiVersion: 1 + +datasources: + # Prometheus - 메트릭 데이터 + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: true + jsonData: + timeInterval: 15s + + # Loki - 로그 데이터 + - name: Loki + type: loki + access: proxy + url: http://loki:3100 + editable: true + jsonData: + maxLines: 1000 diff --git a/.github/deployments/monitoring/loki/loki-config.yaml b/.github/deployments/monitoring/loki/loki-config.yaml index 1e8d87fb..f990ebd0 100644 --- a/.github/deployments/monitoring/loki/loki-config.yaml +++ b/.github/deployments/monitoring/loki/loki-config.yaml @@ -43,7 +43,11 @@ storage_config: # 제한 설정 limits_config: - retention_period: 168h # 7일 보관 + reject_old_samples: false # 과거 샘플 거부 비활성화 + reject_old_samples_max_age: 168h # 7일 이전 데이터도 허용 + creation_grace_period: 12h # 미래 타임스탬프 12시간까지 허용 (KST +9시간 커버) + unordered_writes: true # 순서 없는 쓰기 허용 + retention_period: 168h # 7일 보관 ingestion_rate_mb: 16 ingestion_burst_size_mb: 32 max_query_length: 721h # 30일 쿼리 가능 diff --git a/.github/deployments/monitoring/promtail/promtail-config.yaml b/.github/deployments/monitoring/promtail/promtail-config.yaml new file mode 100644 index 00000000..3346a851 --- /dev/null +++ b/.github/deployments/monitoring/promtail/promtail-config.yaml @@ -0,0 +1,25 @@ +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: docker + docker_sd_configs: + - host: unix:///var/run/docker.sock + refresh_interval: 5s + relabel_configs: + - source_labels: ['__meta_docker_container_name'] + regex: '/(.*)' + target_label: 'container' + - source_labels: ['__meta_docker_container_log_stream'] + target_label: 'logstream' + - source_labels: ['__meta_docker_container_label_com_docker_compose_project'] + target_label: 'project' + - source_labels: ['__meta_docker_container_label_com_docker_compose_service'] + target_label: 'service' diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 0a4a762b..8908d8b0 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -112,8 +112,8 @@ jobs: echo "LOKI_HOST=${{ secrets.NCP_DEV_MONITORING_HOST }}" >> .env fi - docker compose -f docker-compose.yml -f docker-compose.develop.yml pull backend - docker compose -f docker-compose.yml -f docker-compose.develop.yml up -d backend + docker compose -f docker-compose.yml -f docker-compose.develop.yml pull + docker compose -f docker-compose.yml -f docker-compose.develop.yml up -d # 헬스체크 (30초 타임아웃) echo "헬스체크 중..." @@ -190,8 +190,8 @@ jobs: echo "LOKI_HOST=${{ secrets.NCP_PROD_MONITORING_HOST }}" >> .env fi - docker compose -f docker-compose.yml -f docker-compose.prod.yml pull backend - docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d backend + docker compose -f docker-compose.yml -f docker-compose.prod.yml pull + docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d # 헬스체크 (60초 타임아웃, 운영 환경은 더 긴 시간) echo "헬스체크 중..." @@ -322,9 +322,10 @@ jobs: # 설정 파일 복사 cp .github/deployments/monitoring/docker-compose.yml ./docker-compose.yml - mkdir -p prometheus loki + mkdir -p prometheus loki promtail cp .github/deployments/monitoring/prometheus/prometheus-dev.yml ./prometheus/prometheus.yml cp .github/deployments/monitoring/loki/loki-config.yaml ./loki/loki-config.yaml + cp .github/deployments/monitoring/promtail/promtail-config.yaml ./promtail/promtail-config.yaml # Prometheus 설정 파일의 IP 플레이스홀더 치환 sed -i "s/BACKEND_HOST_PLACEHOLDER/${{ secrets.NCP_DEV_HOST }}/g" ./prometheus/prometheus.yml @@ -457,9 +458,10 @@ jobs: fi cp .github/deployments/monitoring/docker-compose.yml ./docker-compose.yml - mkdir -p prometheus loki + mkdir -p prometheus loki promtail cp .github/deployments/monitoring/prometheus/prometheus-prod.yml ./prometheus/prometheus.yml cp .github/deployments/monitoring/loki/loki-config.yaml ./loki/loki-config.yaml + cp .github/deployments/monitoring/promtail/promtail-config.yaml ./promtail/promtail-config.yaml # Prometheus 설정 파일의 IP 플레이스홀더 치환 sed -i "s/BACKEND_HOST_PLACEHOLDER/${{ secrets.NCP_PROD_HOST }}/g" ./prometheus/prometheus.yml diff --git a/apps/backend/package.json b/apps/backend/package.json index fcf7be54..c726860b 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -22,6 +22,7 @@ "@nestjs/websockets": "^10.4.20", "@plum/shared-interfaces": "workspace:*", "nest-winston": "^1.10.2", + "prom-client": "^15.1.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "socket.io": "^4.8.1", diff --git a/apps/backend/promtail.yaml b/apps/backend/promtail.yaml index 465647fc..d8c66965 100644 --- a/apps/backend/promtail.yaml +++ b/apps/backend/promtail.yaml @@ -10,7 +10,7 @@ positions: clients: # Loki 서버 주소 (환경변수로 설정) - - url: ${LOKI_URL:-http://localhost:3100}/loki/api/v1/push + - url: ${LOKI_URL}/loki/api/v1/push scrape_configs: # 일반 로그 파일 (app-*.log) @@ -20,31 +20,18 @@ scrape_configs: - localhost labels: job: plum-backend - env: ${NODE_ENV:-development} + env: ${NODE_ENV} __path__: /app/logs/nestjs/app-*.log + # JSON 파싱과 타임스탬프만 처리 (동적 labels 제거) pipeline_stages: - # JSON 파싱 - json: expressions: timestamp: timestamp - level: level message: message - context: context - trace: trace - span: span - - # 타임스탬프 파싱 - timestamp: source: timestamp - format: '2006-01-02 15:04:05' - - # 레이블 추가 - - labels: - level: - context: - - # 출력 포맷 + format: '2026-01-02 15:04:05' - output: source: message @@ -54,31 +41,18 @@ scrape_configs: - targets: - localhost labels: - job: plum-backend - env: ${NODE_ENV:-development} - level: error + job: plum-backend-error + env: ${NODE_ENV} __path__: /app/logs/nestjs/error-*.log + # JSON 파싱과 타임스탬프만 처리 (동적 labels 제거) pipeline_stages: - # JSON 파싱 - json: expressions: timestamp: timestamp message: message - context: context - stack: stack - trace: trace - span: span - - # 타임스탬프 파싱 - timestamp: source: timestamp - format: '2006-01-02 15:04:05' - - # 레이블 추가 - - labels: - context: - - # 출력 포맷 + format: '2026-01-02 15:04:05' - output: source: message diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index 43d2a17d..f83b0cad 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -8,11 +8,13 @@ import { HttpExceptionFilter } from './common/filters/index.js'; import { MediaModule } from './media/media.module.js'; import { InteractionModule } from './interaction/interaction.module.js'; import { RoomModule } from './room/room.module.js'; +import { PrometheusModule, MetricsInterceptor } from './prometheus/index.js'; @Module({ imports: [ HealthModule, WinstonModule.forRoot(winstonConfig), + PrometheusModule, MediaModule, InteractionModule, RoomModule, @@ -25,6 +27,12 @@ import { RoomModule } from './room/room.module.js'; useClass: LoggingInterceptor, }, + // 전역 메트릭 수집 인터셉터 + { + provide: APP_INTERCEPTOR, + useClass: MetricsInterceptor, + }, + // 전역 예외 필터 (404 에러 등 로깅) { provide: APP_FILTER, diff --git a/apps/backend/src/prometheus/index.ts b/apps/backend/src/prometheus/index.ts new file mode 100644 index 00000000..368905e8 --- /dev/null +++ b/apps/backend/src/prometheus/index.ts @@ -0,0 +1,4 @@ +export { PrometheusModule } from './prometheus.module.js'; +export { PrometheusService } from './prometheus.service.js'; +export { PrometheusController } from './prometheus.controller.js'; +export { MetricsInterceptor } from './metrics.interceptor.js'; diff --git a/apps/backend/src/prometheus/metrics.interceptor.ts b/apps/backend/src/prometheus/metrics.interceptor.ts new file mode 100644 index 00000000..faaf33eb --- /dev/null +++ b/apps/backend/src/prometheus/metrics.interceptor.ts @@ -0,0 +1,85 @@ +import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { PrometheusService } from './prometheus.service.js'; + +/** + * + * 모든 HTTP 요청을 자동으로 가로채서 Prometheus 메트릭을 수집하는 인터셉터 + * + * 1. HTTP 요청이 시작되면 타이머 시작 + * 2. 요청 처리 후 응답 시간 측정 + * 3. 요청 정보(method, route, status code)를 라벨로 저장 + * 4. Prometheus 메트릭에 기록 (Duration Histogram, Count Counter) + * + * 왜 필요할까? + * - 모든 엔드포인트에 메트릭 코드를 일일이 추가하지 않아도 됨 + * - 컨트롤러 코드를 수정하지 않고 자동으로 메트릭 수집 + * - 전역 인터셉터로 등록하면 모든 HTTP 요청에 자동 적용 + * + * 흐름 + * 요청 도착 → Interceptor 실행 → 타이머 시작 → Controller 처리 → + * Interceptor로 돌아옴 → 시간 측정 → 메트릭 기록 → 응답 반환 + */ +@Injectable() +export class MetricsInterceptor implements NestInterceptor { + constructor(private readonly prometheusService: PrometheusService) {} + + /** + * HTTP 요청을 가로채는 메인 메서드 + * + * @param context - NestJS 실행 컨텍스트 (요청/응답 정보 접근) + * @param next - 다음 핸들러 (실제 Controller 로직) + * @returns Observable - RxJS 스트림 (비동기 처리) + */ + intercept(context: ExecutionContext, next: CallHandler): Observable { + // 1. 요청 정보 추출 + const request = context.switchToHttp().getRequest(); + const method = request.method; // GET, POST, PUT, DELETE 등 + const route = request.route?.path || request.url; // /health, /metrics 등 + + // 2. 타이머 시작 (요청 처리 시작 시간 기록) + const start = Date.now(); + + // 3. 실제 Controller + return next.handle().pipe( + tap({ + // 성공 - 정상 응답 시 메트릭 기록 + next: () => { + const response = context.switchToHttp().getResponse(); + const statusCode = response.statusCode; // 200, 201 등 + const duration = (Date.now() - start) / 1000; // 초 단위로 변환 + + // HTTP 요청 응답 시간 + // GET /health 200 → 0.05초 + this.prometheusService.httpRequestDuration + .labels(method, route, statusCode.toString()) + .observe(duration); + + // HTTP 요청 횟수 증가 (카운터) + // GET /health 200 → +1 + this.prometheusService.httpRequestsTotal + .labels(method, route, statusCode.toString()) + .inc(); + }, + + //실패 - 에러 발생 시에도 메트릭 기록 + error: (error) => { + const statusCode = error.status || 500; // 404, 500 등 + const duration = (Date.now() - start) / 1000; + + // 에러 케이스도 응답 시간과 횟수 기록 + // GET /invalid-path 404 → 0.01초 + this.prometheusService.httpRequestDuration + .labels(method, route, statusCode.toString()) + .observe(duration); + + // GET /invalid-path 404 → +1 + this.prometheusService.httpRequestsTotal + .labels(method, route, statusCode.toString()) + .inc(); + }, + }), + ); + } +} diff --git a/apps/backend/src/prometheus/prometheus.controller.ts b/apps/backend/src/prometheus/prometheus.controller.ts new file mode 100644 index 00000000..571d6843 --- /dev/null +++ b/apps/backend/src/prometheus/prometheus.controller.ts @@ -0,0 +1,13 @@ +import { Controller, Get, Header } from '@nestjs/common'; +import { PrometheusService } from './prometheus.service.js'; + +@Controller() +export class PrometheusController { + constructor(private readonly prometheusService: PrometheusService) {} + + @Get('/metrics') + @Header('Content-Type', 'text/plain; version=0.0.4; charset=utf-8') + async getMetrics(): Promise { + return this.prometheusService.getMetrics(); + } +} diff --git a/apps/backend/src/prometheus/prometheus.module.ts b/apps/backend/src/prometheus/prometheus.module.ts new file mode 100644 index 00000000..ea2c1444 --- /dev/null +++ b/apps/backend/src/prometheus/prometheus.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { PrometheusService } from './prometheus.service.js'; +import { PrometheusController } from './prometheus.controller.js'; + +@Module({ + providers: [PrometheusService], + controllers: [PrometheusController], + exports: [PrometheusService], +}) +export class PrometheusModule {} diff --git a/apps/backend/src/prometheus/prometheus.service.ts b/apps/backend/src/prometheus/prometheus.service.ts new file mode 100644 index 00000000..563f4f1d --- /dev/null +++ b/apps/backend/src/prometheus/prometheus.service.ts @@ -0,0 +1,42 @@ +import { Injectable, OnModuleInit } from '@nestjs/common'; +import { Registry, Histogram, Counter, collectDefaultMetrics } from 'prom-client'; + +@Injectable() +export class PrometheusService implements OnModuleInit { + public readonly registry: Registry; + public readonly httpRequestDuration: Histogram; + public readonly httpRequestsTotal: Counter; + + constructor() { + this.registry = new Registry(); + + // HTTP 요청 시간 (히스토그램) + this.httpRequestDuration = new Histogram({ + name: 'http_request_duration_seconds', + help: 'Duration of HTTP requests in seconds', + labelNames: ['method', 'route', 'status_code'], + buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10], + registers: [this.registry], + }); + + // HTTP 요청 횟수 (카운터) + this.httpRequestsTotal = new Counter({ + name: 'http_requests_total', + help: 'Total number of HTTP requests', + labelNames: ['method', 'route', 'status_code'], + registers: [this.registry], + }); + } + + onModuleInit() { + // 프로세스 메트릭 자동 수집 (CPU, 메모리, 이벤트 루프 등) + collectDefaultMetrics({ + register: this.registry, + prefix: 'nodejs_', + }); + } + + async getMetrics(): Promise { + return this.registry.metrics(); + } +} diff --git a/docker-compose.develop.yml b/docker-compose.develop.yml index 54308cc4..dae384e5 100644 --- a/docker-compose.develop.yml +++ b/docker-compose.develop.yml @@ -47,7 +47,7 @@ services: environment: - LOKI_URL=http://${LOKI_HOST}:3100 # Dev 모니터링 서버 Loki 주소 (환경변수 필수) - NODE_ENV=development - command: -config.file=/etc/promtail/config.yml + command: -config.file=/etc/promtail/config.yml -config.expand-env=true networks: - plum-network depends_on: diff --git a/docker-compose.local.yml b/docker-compose.local.yml new file mode 100644 index 00000000..27d6bd72 --- /dev/null +++ b/docker-compose.local.yml @@ -0,0 +1,140 @@ +# 백엔드: 로컬 직접 실행 (pnpm dev) - Hot Reload +# 나머지: Docker 컨테이너 + +version: '3.8' + +services: + # Redis - 백엔드가 사용하는 캐시 + redis: + image: redis:7.2-alpine + container_name: redis-local + ports: + - "6379:6379" + command: redis-server --appendonly yes + volumes: + - redis-data:/data + networks: + - plum-local + restart: unless-stopped + + # Redis Exporter - Redis 메트릭 수집 + redis-exporter: + image: oliver006/redis_exporter:v1.55.0 + container_name: redis-exporter-local + ports: + - "9121:9121" + environment: + - REDIS_ADDR=redis:6379 + networks: + - plum-local + depends_on: + - redis + restart: unless-stopped + + # Node Exporter - 호스트 시스템 메트릭 + node-exporter: + image: prom/node-exporter:v1.6.1 + container_name: node-exporter-local + ports: + - "9100:9100" + command: + - '--path.procfs=/host/proc' + - '--path.sysfs=/host/sys' + - '--path.rootfs=/host' + - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)' + volumes: + - /proc:/host/proc:ro + - /sys:/host/sys:ro + - /:/host:ro + networks: + - plum-local + restart: unless-stopped + + # Prometheus - 메트릭 수집 + prometheus: + image: prom/prometheus:v2.45.0 + container_name: prometheus-local + ports: + - "9090:9090" + volumes: + - ./prometheus-local.yml:/etc/prometheus/prometheus.yml:ro + - prometheus-data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.retention.time=7d' + - '--storage.tsdb.path=/prometheus' + - '--web.enable-lifecycle' + networks: + - plum-local + extra_hosts: + - "host.docker.internal:host-gateway" + depends_on: + - node-exporter + - redis-exporter + restart: unless-stopped + + # Loki - 로그 저장소 + loki: + image: grafana/loki:2.9.0 + container_name: loki-local + ports: + - "3100:3100" + volumes: + - ./loki-local-config.yaml:/etc/loki/loki-config.yaml:ro + - loki-data:/loki + command: -config.file=/etc/loki/loki-config.yaml + networks: + - plum-local + restart: unless-stopped + + # Promtail - 로그 수집 (백엔드 로그 → Loki) + promtail: + image: grafana/promtail:2.9.0 + container_name: promtail-local + volumes: + - ./apps/backend/logs:/app/logs:ro + - ./apps/backend/promtail.yaml:/etc/promtail/config.yml:ro + environment: + - LOKI_URL=http://loki:3100 + - NODE_ENV=development + command: -config.file=/etc/promtail/config.yml -config.expand-env=true + networks: + - plum-local + depends_on: + - loki + restart: unless-stopped + + # Grafana - 시각화 대시보드 + grafana: + image: grafana/grafana:10.2.0 + container_name: grafana-local + ports: + - "3001:3000" # 백엔드가 3000 사용중이라 3001로 + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=admin + - GF_USERS_ALLOW_SIGN_UP=false + - GF_SERVER_ROOT_URL=http://localhost:3001 + - GF_ANALYTICS_REPORTING_ENABLED=false + volumes: + - grafana-data:/var/lib/grafana + networks: + - plum-local + depends_on: + - prometheus + - loki + restart: unless-stopped + +volumes: + redis-data: + driver: local + prometheus-data: + driver: local + loki-data: + driver: local + grafana-data: + driver: local + +networks: + plum-local: + driver: bridge diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 90d15e77..1a276bdd 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -61,7 +61,7 @@ services: environment: - LOKI_URL=http://${LOKI_HOST}:3100 # Prod 모니터링 서버 Loki 주소 (환경변수 필수) - NODE_ENV=production - command: -config.file=/etc/promtail/config.yml + command: -config.file=/etc/promtail/config.yml -config.expand-env=true networks: - plum-network depends_on: diff --git a/loki-local-config.yaml b/loki-local-config.yaml new file mode 100644 index 00000000..58685277 --- /dev/null +++ b/loki-local-config.yaml @@ -0,0 +1,33 @@ +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: + kvstore: + store: inmemory + +schema_config: + configs: + - from: 2026-01-01 + store: boltdb-shipper + object_store: filesystem + schema: v11 + index: + prefix: index_ + period: 24h + +limits_config: + reject_old_samples: false # 과거 샘플 거부 비활성화 + reject_old_samples_max_age: 168h # 7일 이전 데이터도 허용 + creation_grace_period: 12h # 미래 타임스탬프 12시간까지 허용 (한국시간 +9시간 커버) + ingestion_rate_mb: 16 + ingestion_burst_size_mb: 32 + unordered_writes: true # 순서 없는 쓰기 허용 (로컬 개발용) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fbb9cc60..e605cd19 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ importers: nest-winston: specifier: ^1.10.2 version: 1.10.2(@nestjs/common@10.4.20(reflect-metadata@0.2.2)(rxjs@7.8.2))(winston@3.19.0) + prom-client: + specifier: ^15.1.0 + version: 15.1.3 reflect-metadata: specifier: ^0.2.2 version: 0.2.2 @@ -5669,6 +5672,10 @@ packages: resolution: {integrity: sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA==} engines: {node: '>=10'} + prom-client@15.1.3: + resolution: {integrity: sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==} + engines: {node: ^16 || ^18 || >=20} + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -13848,6 +13855,11 @@ snapshots: dependencies: tdigest: 0.1.2 + prom-client@15.1.3: + dependencies: + '@opentelemetry/api': 1.9.0 + tdigest: 0.1.2 + prompts@2.4.2: dependencies: kleur: 3.0.3 diff --git a/prometheus-local.yml b/prometheus-local.yml new file mode 100644 index 00000000..d1741a9e --- /dev/null +++ b/prometheus-local.yml @@ -0,0 +1,37 @@ +# 로컬 개발용 Prometheus 설정 +# docker-compose.local.yml에서 사용 + +global: + scrape_interval: 15s + evaluation_interval: 15s + external_labels: + cluster: 'local' + environment: 'development' + +scrape_configs: + # Prometheus 자체 메트릭 + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + + # 로컬 시스템 메트릭 Node Exporter + - job_name: 'node-exporter-local' + static_configs: + - targets: ['node-exporter:9100'] + labels: + server: 'local-host' + + # 로컬 백엔드 애플리케이션 메트릭 prom-client + - job_name: 'backend-app-local' + static_configs: + - targets: ['host.docker.internal:3000'] + labels: + server: 'local-backend' + metrics_path: '/metrics' + + # 로컬 Redis 메트릭 Redis Exporter + - job_name: 'redis-local' + static_configs: + - targets: ['redis-exporter:9121'] + labels: + server: 'local-redis'