1. 핵심 컴포넌트 개요
Spring Boot 애플리케이션의 내부 상태를 표준화된 방식으로 외부에 노출하려면 Spring Boot Actuator 모듈을 활용합니다. 이 컴포넌트는 헬스체크 상태, JVM 메모리 할당 현황, 스레드 풀 활성도, HTTP 라우팅 매핑 및 시스템 환경 변수 등 다양한 운영 지표를 자동 수집하여 HTTP 엔드포인트로 제공합니다.
노출된 지표는 일반적으로 Prometheus로 전송되어 저장됩니다. Prometheus는 시계열 데이터를 효율적으로 관리하며, 지표명{라벨="값"} 수치 형식의 메트릭을 기반으로 PromQL 질의, 다차원 필터링, 실시간 알림 규칙 생성을 지원합니다.
2. 애플리케이션 의존성 및 설정 구성
먼저 빌드 설정 파일에 Actuator와 Prometheus 레지스트리 라이브러리를 추가합니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
이후 application.yml에서 외부로 공개할 엔드포인트 목록과 메트릭 라벨 생성 규칙을 정의합니다. 보안상 민감한 정보를 제외하고, HTTP 요청 지표에 URI/Method/Status 코드 태그를 자동으로 부착하도록 구성합니다.
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
exclude: env,beans,configprops,threaddump,shutdown
endpoint:
prometheus:
enabled: true
metrics:
web:
server:
request:
autotime:
enabled: true
tag:
uri: true
method: true
status-code: true
애플리케이션 기동 후 curl http://localhost:8080/actuator/prometheus를 실행하면 Micrometer가 변환한 Prometheus 호환 텍스트 형식의 메트릭 스트림이 출력됩니다.
3. Prometheus 수집 파이프라인 구축
로컬 검증 환경을 위해 Docker 컨테이너로 Prometheus를 실행합니다. 수집 대상(Spring Boot 서비스)의 주소와 스크랩 주기를 prometheus.yml에 명시해야 합니다.
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'springboot-backend'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['host.docker.internal:8080']
labels:
service_name: 'order-service'
environment: 'dev'
- job_name: 'prometheus-infra'
static_configs:
- targets: ['localhost:9090']
labels:
component: 'monitoring-core'
설정 파일 저장 후 다음 명령어로 인스턴스를 실행합니다.
docker run -d --name prom-node \
-p 9090:9090 \
-v $(pwd)/prom_config.yml:/etc/prometheus/prometheus.yml \
prom/prometheus
브라우저에서 http://localhost:9090/targets 페이지에 접근하여 springboot-backend 상태가 UP으로 전환되었는지 확인합니다.
4. Nightingale(N9E) 시각화 및 알림 연동
Prometheus 내장 UI는 단순 확인 용도로 적합하나, 장기적인 대시보드 관리와 복잡한 알림 로직에는 한계가 있습니다. Nightingale(n9e)를 Prometheus 데이터 소스로 연결하면 통합 모니터링 환경을 구성할 수 있습니다.
- 데이터 소스 연결: N9E 관리 콘솔에서 Prometheus 서버의 엔드포인트 URL을 등록하여 연동합니다.
- 대시보드 커스터마이징: 플랫폼 내장 템플릿을 로드하거나 직접 PromQL을 작성하여 JVM 힙 점유율, GC 발생 빈도, HTTP 응답 지연 시간 등의 시각화 위젯을 배열합니다.
- 알림 규칙 정의: 이메일, Slack, Webhook,钉钉/飞书 등 알림 채널을 설정한 후, 임계값(예: 5xx 오류율 3% 초과 또는 평균 응답 시간 1.5초 초과) 기반 발동 조건을 등록합니다. 기업 환경에서는 FlashDuty와 연계하여 알림 통합, 온콜 스케줄링, 이벤트 노이즈 필터링을 고도화할 수 있습니다.
5. 실무 PromQL 활용 패턴
운영 현장에서 자주 요구되는 지표 분석 쿼리는 다음과 같이 구성됩니다.
-- 1분 간격 API 호출 빈도 집계 (QPS)
sum(rate(http_server_requests_seconds_count{status=~"2..|4..|5.."}[1m])) by (uri, method, status)
-- 요청 응답 시간 95 퍼센타일 산출
histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket[1m])) by (le, uri))
-- JVM 힙 메모리 사용량 추적 (Byte 단위)
jvm_memory_used_bytes{area="heap"}
-- 현재 로딩된 클래스 개수 확인
jvm_classes_loaded_classes
6. 모니터링 엔드포인트 보안 강화
/actuator 경로는 시스템 구성 정보 및 내부 메트릭을 포함하므로 외부 접근을 엄격히 통제해야 합니다. Spring의 OncePerRequestFilter를 확장하여 IP 허용 목록과 정적 인증 토큰 기반의 검증 계층을 구현할 수 있습니다.
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.util.List;
import java.util.Set;
@Component
@Order(1)
public class MonitoringAccessGuard extends OncePerRequestFilter {
private static final Set<String> TRUSTED_IP_POOL = Set.of("127.0.0.1", "10.0.0.5", "192.168.1.20");
private static final String VALID_MONITOR_KEY = "SEC_N9E_TOKEN_X9";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String targetPath = request.getRequestURI();
if (!targetPath.startsWith("/actuator")) {
chain.doFilter(request, response);
return;
}
String clientSourceIp = resolveClientAddress(request);
String suppliedToken = request.getHeader("X-N9E-Access");
boolean ipWhitelisted = TRUSTED_IP_POOL.contains(clientSourceIp);
boolean tokenAuthorized = StringUtils.hasText(suppliedToken) && VALID_MONITOR_KEY.equals(suppliedToken);
if (ipWhitelisted && tokenAuthorized) {
chain.doFilter(request, response);
} else {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("Monitoring access denied.");
}
}
private String resolveClientAddress(HttpServletRequest req) {
String forwardedHeader = req.getHeader("X-Forwarded-For");
if (StringUtils.hasText(forwardedHeader)) {
return forwardedHeader.split(",")[0].trim();
}
String proxyHeader = req.getHeader("Proxy-Client-IP");
if (StringUtils.hasText(proxyHeader)) {
return proxyHeader;
}
return req.getRemoteAddr();
}
}
해당 필터는 /actuator/* 경로를 타겟으로 삼으며, 신뢰된 IP 대역에서 발생한 요청이면서 올바른 HTTP 헤더 토큰을攜帶한 경우에만 실제 메트릭 처리 체인으로 라우팅합니다. 조건을 만족하지 않으면 즉시 403 Forbidden 응답을 반환하여 내부 정보 유출을 차단합니다.