🎯 목표
Spring Boot 기반 서버의 메트릭 데이터를 Prometheus로 수집하고, Grafana를 통해 시각화 및 대시보드를 구성합니다. 본 구축기는 도커 컴포즈(Docker Compose)를 사용하여 Prometheus + Grafana를 빠르게 구축하는 과정을 다룹니다.
✅ 시스템 구성 개요
구성 요소 역할
| Prometheus | Spring Boot 애플리케이션의 메트릭을 수집 및 저장 (Pull 방식) |
|---|---|
| Grafana | Prometheus에서 수집된 메트릭을 시각화하는 대시보드 툴 |
| Spring Boot | /actuator/prometheus 엔드포인트를 통해 메트릭을 노출 |
⚙️ Docker Compose 설정
docker-compose.yml
version: '3.3'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./prometheus-entrypoint.sh:/prometheus-entrypoint.sh
entrypoint: ["/prometheus-entrypoint.sh"]
environment:
- BASIC_AUTH_USERNAME=${PROMETHEUS_USERNAME}
- BASIC_AUTH_PASSWORD=${PROMETHEUS_PASSWORD}
- PROMETHEUS_RETENTION_TIME=${PROMETHEUS_RETENTION_TIME}
extra_hosts:
- "host.docker.internal:host-gateway"
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
- ./grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:-ssafy}
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-ssafypw}
- GF_USERS_ALLOW_SIGN_UP=false
depends_on:
- prometheus
volumes:
grafana-data: {}
🔧 Prometheus 설정 파일
prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'spring-boot'
metrics_path: '/actuator/prometheus'
basic_auth:
username: '********'
password: '********'
static_configs:
- targets: ['host.docker.internal:8080']
- scrape_interval: 메트릭 수집 주기
- spring-boot job에서는 Spring Boot Actuator 엔드포인트로부터 메트릭을 가져옵니다.
📁 Grafana 데이터 소스 설정
grafana-datasources.yml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
🛠 Spring Boot 설정 방법
Spring Boot에서 Prometheus가 메트릭을 수집할 수 있도록 하기 위해 다음과 같은 설정이 필요합니다.
1. 의존성 추가
build.gradle
dependencies {
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
2. application.yml 설정
management:
endpoints:
web:
exposure:
include: health,info,prometheus
endpoint:
prometheus:
enabled: true
metrics:
export:
prometheus:
enabled: true
server:
port: 8080
보안 설정이 필요한 경우 Spring Security 또는 nginx 리버스 프록시에서 기본 인증을 적용할 수 있습니다.
🚀 실행 방법
docker-compose up -d
- http://localhost:9090 → Prometheus 접속
- http://localhost:3000 → Grafana 접속
🔍 대시보드 구성 예시
Grafana 접속 후:
- 좌측 메뉴 → Dashboards → New Dashboard
- Panel 추가 → PromQL 쿼리 입력 예시:
- rate(http_server_requests_seconds_count[1m])
- jvm_memory_used_bytes{area="heap"}
❌ 문제 발생 과정 및 해결 정리
1. ❗ Prometheus에서 /actuator/prometheus 요청 → 401 Unauthorized
- 원인: Spring Boot에 Spring Security Basic 인증이 설정되어 있어 인증 필요
2. JWT 필터 충돌
- 원인: JwtAuthenticationFilter가 모든 요청에 적용되고 있었음
- 설명: Prometheus 요청은 Basic Auth를 사용하지만, JWT 필터가 먼저 실행되면서 인증 실패 처리됨
- 해결 방법: 필터의 예외 처리 조건 추가
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
String path = request.getRequestURI();
String authHeader = request.getHeader("Authorization");
return path.equals("/actuator/prometheus") ||
(authHeader != null && authHeader.startsWith("Basic "));
}
🔄 문제 해결 흐름 정리 (정확한 순서 기반)
단계 문제 또는 원인 조치 또는 해결 방법
| 1 | JWT 필터가 Basic 인증 요청까지 가로챔 | JwtAuthenticationFilter.shouldNotFilter()에서 /actuator/prometheus 또는 Basic 헤더 요청을 우회 처리 |
|---|---|---|
| 2 | Spring Security의 BCrypt 인코딩 비밀번호와 Prometheus 평문이 일치하지 않음 | securepassword를 BCrypt로 인코딩하여 .env에 등록하고 application.yml에서 환경변수로 사용 |
| 3 | 인증 성공 | curl -u admin:securepassword http://localhost:8080/actuator/prometheus → 200 OK 확인. Prometheus 정상 수집, Grafana 시각화 정상 작동 |
✅ 학습 정리
- Prometheus는 Exporter가 노출하는 메트릭을 Pull 방식으로 수집합니다.
- Grafana는 다양한 시각화 Panel과 경고(Alert)를 구성할 수 있는 강력한 도구입니다.
- 도커 컴포즈를 활용하면 Prometheus와 Grafana 환경을 빠르게 구축하고 관리할 수 있습니다.
📊 Prometheus & Grafana 모니터링 시스템 구축기
🎯 목표
Spring Boot 기반 서버의 메트릭 데이터를 Prometheus로 수집하고, Grafana를 통해 시각화 및 대시보드를 구성합니다. 본 구축기는 도커 컴포즈(Docker Compose)를 사용하여 Prometheus + Grafana를 빠르게 구축하는 과정을 다룹니다.
✅ 시스템 구성 개요
구성 요소 역할
| Prometheus | Spring Boot 애플리케이션의 메트릭을 수집 및 저장 (Pull 방식) |
|---|---|
| Grafana | Prometheus에서 수집된 메트릭을 시각화하는 대시보드 툴 |
| Spring Boot | /actuator/prometheus 엔드포인트를 통해 메트릭을 노출 |
⚙️ Docker Compose 설정
docker-compose.yml
version: '3.3'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml.template:/etc/prometheus/prometheus.yml.template
- ./prometheus-entrypoint.sh:/prometheus-entrypoint.sh
entrypoint: ["/prometheus-entrypoint.sh"]
environment:
- BASIC_AUTH_USERNAME=${PROMETHEUS_USERNAME}
- BASIC_AUTH_PASSWORD=${PROMETHEUS_PASSWORD}
- PROMETHEUS_RETENTION_TIME=${PROMETHEUS_RETENTION_TIME}
extra_hosts:
- "host.docker.internal:host-gateway"
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
- ./grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:-ssafy}
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-ssafypw}
- GF_USERS_ALLOW_SIGN_UP=false
depends_on:
- prometheus
volumes:
grafana-data: {}
🔧 Prometheus 설정 파일 (템플릿)
prometheus.yml.template
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'spring-boot'
metrics_path: '/actuator/prometheus'
basic_auth:
username: '********'
password: '********'
static_configs:
- targets: ['host.docker.internal:8080']
- scrape_interval: 메트릭 수집 주기
- spring-boot job에서는 Spring Boot Actuator 엔드포인트로부터 메트릭을 가져옵니다.
📁 Grafana 데이터 소스 설정
grafana-datasources.yml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
🛠 Spring Boot 설정 방법
Spring Boot에서 Prometheus가 메트릭을 수집할 수 있도록 하기 위해 다음과 같은 설정이 필요합니다.
1. 의존성 추가
build.gradle
dependencies {
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
2. application.yml 설정
management:
endpoints:
web:
exposure:
include: health,info,prometheus
endpoint:
prometheus:
enabled: true
metrics:
export:
prometheus:
enabled: true
server:
port: 8080
보안 설정이 필요한 경우 Spring Security 또는 nginx 리버스 프록시에서 기본 인증을 적용할 수 있습니다.
🚀 실행 방법
docker-compose up -d
- http://localhost:9090 → Prometheus 접속
- http://localhost:3000 → Grafana 접속 (기본 계정: ******** / ********)
🔍 대시보드 구성 예시
Grafana 접속 후:
- 좌측 메뉴 → Dashboards → New Dashboard
- Panel 추가 → PromQL 쿼리 입력 예시:
- rate(http_server_requests_seconds_count[1m])
- jvm_memory_used_bytes{area="heap"}
❌ 문제 발생 과정 및 해결 정리
1. ❗ Prometheus에서 /actuator/prometheus 요청 → 401 Unauthorized
- 원인: Spring Boot에 Spring Security Basic 인증이 설정되어 있어 인증 필요
2. ❗ JWT 필터 충돌로 Prometheus 요청이 인증 실패
- 원인: Spring Boot에 JwtAuthenticationFilter가 전역 설정되어 있음
- 문제: Prometheus는 Basic Auth를 사용하지만, Authorization 헤더가 Bearer 토큰이 아니라서 JWT 필터에서 인증 실패 처리됨
- 해결 방법: JwtAuthenticationFilter에서 Basic 요청을 예외 처리
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
String path = request.getRequestURI();
String authHeader = request.getHeader("Authorization");
return path.equals("/actuator/prometheus") ||
(authHeader != null && authHeader.startsWith("Basic "));
}
❗ 3. BCrypt 비밀번호 미스매치로 인증 실패
📌 문제 요약
Prometheus에서 /actuator/prometheus 엔드포인트에 Basic 인증을 시도했지만 401 Unauthorized 응답이 발생함.
🔍 원인 상세
문제의 핵심은 Spring Boot의 기본 설정 때문이 아닌, BCryptPasswordEncoder를 명시적으로 등록했기 때문.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // ⬅ 이 설정
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
}
}
Spring Boot는 기본적으로 DelegatingPasswordEncoder를 사용하여 {noop}, {bcrypt} 등의 prefix를 인식합니다.
하지만 위처럼 BCryptPasswordEncoder를 직접 설정하면 해시된 비밀번호만 허용하고 평문은 무조건 실패하게 됩니다.
즉, .env에 securepassword처럼 평문 비밀번호를 넣으면 인증 실패가 발생합니다.
✅ 해결 방법
1. securepassword를 BCrypt 해시로 인코딩
System.out.println(new BCryptPasswordEncoder().encode("securepassword"));
2. .env에 인코딩된 해시값 등록
PROMETHEUS_PASSWORD=$2a$10$QzMOCvObdKx34ovliGn4qO9EcRMLMpFGJBaZKD5UsO2ZfJP5GABcu
3. application.yml에서 환경 변수 참조
spring:
security:
user:
name: ${PROMETHEUS_USERNAME}
password: ${PROMETHEUS_PASSWORD}
roles: PROMETHEUS
🔁 Prometheus 설정은 그대로 유지
basic_auth:
username: 'admin'
password: 'securepassword' # 평문 그대로 유지 (Prometheus는 해시를 모름)
🔍 정상 동작 확인
Prometheus가 securepassword를 평문으로 전송하면,
Spring Boot에서 BCryptPasswordEncoder.matches()를 통해 해시와 비교하여 인증 성공.
curl -u admin:securepassword http://localhost:8080/actuator/prometheus
# 응답: 200 OK
✅ 이렇게 하면 Spring Boot에서 Prometheus의 Basic 인증 요청을 올바르게 처리할 수 있습니다.
🧾 최종 마무리 및 운영 팁
- Prometheus는 Pull 방식으로 /actuator/prometheus에서 메트릭을 수집하므로, 해당 엔드포인트는 반드시 외부 접근 가능해야 하고, 인증도 적절히 처리되어야 합니다 .
- Spring Security를 사용하는 경우, Basic 인증과 JWT 인증 필터의 우선순위 충돌을 명확히 제어해야 합니다 .
- BCryptPasswordEncoder를 명시적으로 사용하는 경우, Prometheus 쪽은 평문, Spring Boot 쪽은 해시를 사용해야 한다는 점을 반드시 구분해서 관리해야 합니다.
- docker-compose, .env, application.yml, prometheus.yml 설정들이 모두 맞물려 있어야 인증과 메트릭 수집이 원활하게 동작합니다 .
'CS공부 > 인프라' 카테고리의 다른 글
| VS Code 원격 개발 환경 구성: SSH 키로 원격 서버 접속하기 (1) | 2025.09.01 |
|---|---|
| 모니터링 시스템 비교: ELK vs Prometheus & Grafana (0) | 2025.05.08 |
| 동기 vs 비동기 & 메시지 브로커 개념 정리 (0) | 2025.05.01 |
| OAuth2 인증 로직 및 Google 기반 구현 (0) | 2025.04.27 |
| GitHub 웹훅 기반 Jenkins CI/CD 파이프라인 설계 (0) | 2025.04.23 |