본문 바로가기

TIL

Prometheus + Grafana 모니터링 스택 구축

1. Prometheus vs Grafana 역할

  Prometheus Grafana
역할 메트릭 수집 + 저장 시각화 대시보드
기능 각 서비스에서 주기적으로 메트릭 scrape, 시계열 DB에 저장 Prometheus 데이터를 쿼리해서 그래프/차트로 표시, 알림 설정\

데이터 흐름:

[모니터링 대상 서비스] → (메트릭 노출 /metrics) → [Prometheus 수집/저장] → [Grafana 시각화]

모니터링 대상 서비스는 /metrics 엔드포인트를 열어줘야 Prometheus가 긁어갈 수 있다.

- Spring Boot: spring-boot-starter-actuator + micrometer-registry-prometheus

- Node.js: prom-client


2. Docker Compose 기본 구성

services:
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "127.0.0.1:9090:9090"  # 외부 노출 차단, 로컬에서만 접근
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - "--storage.tsdb.retention.time=15d"  # 데이터 보관 기간
      - "--web.enable-lifecycle"

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
      - ./grafana/dashboards:/var/lib/grafana/dashboards

3. Prometheus 설정 (prometheus.yml)

scrape_interval - 타겟에서 메트릭을 수집하는 주기

evaluation_interval - alerting rule을 평가하는 주기

 

나머지는 읽으면 일해할 수 있다.

scrape_configs

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: "pocat-backend"
    metrics_path: /actuator/prometheus  # Spring Boot 기본 경로
    scrape_interval: 30s                # job별 개별 설정 가능
    scrape_timeout: 10s
    basic_auth:                         # /metrics 인증이 필요한 경우
      username: prometheus
      password_file: /etc/prometheus/secrets/password
    static_configs:
      - targets: ["pocat-backend:8080"]
        labels:
          env: production              # 메트릭에 태그 추가
          team: backend

job 이름으로 서비스 구분

job_name이 메트릭의 job 라벨로 저장되어 Grafana에서 필터링/그룹핑에 활용된다.

# pocat-backend의 JVM 힙 메모리
jvm_memory_used_bytes{job="pocat-backend", area="heap"}

# 서비스별 상태 확인
up{job="pocat-backend"}  # 1: 정상, 0: 다운

4. 이슈 : Docker 네트워크

이 문제는 로컬 환경에서 정상적으로 동작하는가? 를 확인할때 다른 compose로띄울경우 서로 다른 네트워크이기 때문에 통신이 안된다.

그럼 같은 compose에서 띄우면? 같은 프로젝트 내에서 동작하기때문에 우리는 저장소 자체를 분리해서 방법이 될 수 없다고 생각했다.

사실 운영단계에서는 상관없다 그냥 애초에 서로 다른 서버에 웹서버와 모니터링 서버가 동작하는거니까,

해결 : 공유 네트워크로 compose 간 통신

여러 docker-compose가 같은 서버에서 실행될 때 컨테이너 이름으로 통신하려면 공유 네트워크가 필요하다.

# 네트워크 생성 (최초 1회)
docker network create pocat-net
# 각 docker-compose.yml에 추가
services:
  my-service:
    networks:
      - pocat-net

networks:
  pocat-net:
    external: true  # 외부에서 만든 네트워크 참조

 


5. 운영 환경 아키텍처

권장 구조 (Private Subnet + ALB)

인터넷 → ALB → Private Subnet
                 ├── pocat-backend:8080
                 ├── pocat-batch:8081
                 ├── DB
                 └── Prometheus + Grafana
  • Prometheus는 Private Subnet 안에서 서비스들을 직접 scrape
  • /metrics 엔드포인트는 외부에 절대 노출하지 않음
  • Grafana만 ALB에 연결해서 외부 접근 허용

보안 레이어

하나만 믿으면 뚫렸을 때 끝이므로 여러 레이어를 겹쳐야 한다.

VPC 내부망 제한 (Security Group)
  + VPN (WireGuard 등)
  + basic_auth
방법 특징
Security Group으로 VPC 내부만 허용 외부 인터넷 차단
특정 IP만 허용 IP 탈취 시 우회 가능, 단독으로 쓰기엔 부족
basic_auth 네트워크 뚫려도 인증 한 겹 추가

6. Grafana Provisioning

Provisioning이 필요한 이유

볼륨 마운트만 사용하면 docker compose down -v 또는 서버 교체 시 대시보드가 날아간다. Provisioning은 대시보드와 데이터소스를 코드(파일)로 관리해서 서버가 새로 띄워져도 자동 복원된다.

 

즉 대시보드를 GUI 형태가 아니라 코드베이스로 관리가 가능해진다.

디렉토리 구조

grafana/
├── provisioning/
│   ├── datasources/
│   │   └── prometheus.yml   ← 데이터소스 자동 연결
│   └── dashboards/
│       └── dashboard.yml    ← 대시보드 파일 위치 지정
└── dashboards/
    └── pocat-overview.json  ← 대시보드 정의

데이터소스 설정 (provisioning/datasources/prometheus.yml)

apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090  # 같은 네트워크면 컨테이너 이름 사용
    isDefault: true
    editable: false

대시보드 프로바이더 설정 (provisioning/dashboards/dashboard.yml)

apiVersion: 1

providers:
  - name: pocat
    folder: POCAT
    type: file
    disableDeletion: true       # UI에서 실수로 삭제 방지
    updateIntervalSeconds: 30   # 파일 변경 시 자동 반영
    options:
      path: /var/lib/grafana/dashboards

Grafana UI에서 대시보드를 만든 후 JSON으로 export해서 dashboards/ 디렉토리에 저장하면 Git으로 버전 관리가 가능하다.