해커톤에서 Prometheus와 Grafana를 활용해 모니터링을 도입해 본 경험이 있다
하지만 당시에는 시간이 촉박해 구현에 집중하느라, 모니터링 시스템의 구조나 구현 과정을 체계적으로 정리하지 못했다
이후 AWS 환경에서 Spring Boot 기반 서비스를 배포하고 운영하면서 AWS의 모니터링 시스템만으로 측정하는 것에 한계가 느껴졌다
- 현재 API 응답 시간이 안정적인가?
- JVM 메모리는 충분한가?
- 특정 시간대에 요청이 집중되고 있는가?
기존 클라우드 환경에서 제공하는 기본 모니터링(CPU,메모리 사용량 등)만으로는 애플리케이션 내부 상태까지 파악하기 어려웠다
이번 글에서는 Spring Boot + Docker + AWS 환경에서 Prometheus와 Grafana를 활용하여
운영 환경 모니터링 시스템을 구축하는 과정을 정리해보려 한다
기존 클라우드 모니터링과 차이
AWS 기본 모니터링은 다음과 같은 정보를 제공한다
CPU 사용량,네트워크 트래픽,인스턴스 상태 등 기본적인 측정값을 제공하지만 애플리케이션 내부 지표까지 확인하기 어렵다

Prometheus + Grafana 환경 모니터링을 도입하게 되면 더욱 자세한 지표를 확인할 수 있게 된다
HTTP 상태 코드 분포,ERROR 발생,JVM Memory 등 서비스가 정상적으로 동작하고 있는지 판단할 수 있는 환경을 제공한다

실제 운영 서비스를 안정적으로 유지하기 위해서는 모니터링은 선택이 아닌 필수라고 생각한다
모니터링 환경 구성
모니터링 시스템을 도입할 서비스는 Spring Boot + Docker + AWS 배포 환경에서 운영되고 있으며
애플리케이션 내부 메트릭을 수집하기 위해 다음과 같은 구조로 모니터링 환경을 구성했다

Spring Boot 애플리케이션 내부에서 메트릭을 수집하고, 이를 외부로 노출시켜 Prometheus가 주기적으로 수집하여 저장하며
Grafana를 통해서 시각화하는 모니터링 구조를 설계 했다
1. Micrometer - 애플리케이션 메트릭 수집
Spring Boot 내부에서 Micrometer를 통해 다음과 같은 메트릭을 수집한다
- HTTP 요청 수
- 응답 시간
- JVM Heap / Non-Heap 메모리 사용량
- CPU 사용률
Micrometer는 애플리케이션 실행 중 발생하는 다양한 상태 정보를 내부적으로 집계하는 역할을 한다
이를 통해 더 자세한 서버 상태를 확인할 수 있게 된다
2. Actuator - 메트릭 외부 노출
Micrometer가 수집한 메트릭을 Spring Boot Actuator를 통해 외부로 노출시킨다
/actuator/prometheus 엔드포인트를 활성화하여 Prometheus가 메트릭을 수집할 수 있도록 구성해 준다
Actuator가 메트릭을 노출(expose) 하여 메트릭에 접근할 수 있게 된다
3. Prometheus - Pull 기반 수집 및 저장
Prometheus는 Pull 방식을 사용한다 설정된 scrape-interval (15초) 마다 /actuator/prometheus 호출하여 메트릭을 수집한다
수집된 데이터는 Prometheus 내부의 시계열 DB(Time-Series Database)에 저장된다
이를 통해 시간 흐름에 따른 서버 상태 변화를 추적할 수 있다
4. Grafana - 시각화
Grafana는 Prometheus에 저장된 데이터를 PromQL을 통해 조회(Query)하고 이를 대시보드 형태로 시각화한다
단순 수치 확인을 넘어 운영 상태를 직관적으로 파악할 수 있는 환경을 구성해 준다
기본적으로 다양한 공식 템플릿을 제공하여 Spring Boot + Micrometer 환경에 맞는 대시보드를 Import 하여 사용할 예정이다
EC2는 Public Subnet에 위치하고 있으며 RDS는 Private Subnet에 위치하고 있다
RDS는 EC2의 Security Group을 통해서만 접근 가능한 상태로 DB는 외부에서 직접 접근이 불가능하도록 분리한 상태
Spring Boot + Prometheus + Grafana 모니터링 설정 방법
1-1. Actuator + Micrometer 의존성 추가
Gradle
모니터링 시스템을 구성하기 위해서 다음과 같은 의존성을 추가하였다
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'
}
Actuator: 애플리케이션 상태를 외부에 노출
Micrometer: 메트릭을 수집하고 집계
micrometer-registry-prometheus: Prometheus 포맷으로 변환

해당 프로젝트에서 사용 중인 의존성 목록이다
1-2. application.yml 설정
management:
endpoints:
web:
exposure:
include: health, info, prometheus
endpoint:
health:
show-details: when-authorized
HTTP로 접근 가능한 Actuator 엔드포인트를 지정한다
Spring Boot는 보안상 기본적으로 모든 엔드포인트를 열어두지 않으며 명시적으로 include 해야 외부에서 접근이 가능하다
| 엔드포인트 | 역할 |
| /actuator/health | 서버 상태 확인 |
| /actuator/info | 서비스 정보 표시 |
| /actuator/prometheus | Prometheus 수집용 메트릭 노출 |
show-details: when-authorized
/actuator/health의 상세 정보 노출 조건을 의미한다
현재 인증 + 인가된 사용자만 상세 정보를 노출하도록 설정하였다
DB,Redis 상태까지 상세하게 조회되기 때문에 불특정 다수가 요청을 보냈을 때 노출되지 않도록 할 필요가 있었다
SecurityFilterChain에 엔드포인트 설정을 해줘야 정상적으로 동작한다
1-3. 엔드포인트 응답 확인
애플리케이션 실행 후 다음 URL에 접속 시 메트릭 텍스트가 출력되면 정상
http://localhost:8080/actuator/prometheus
show-details: when-authorized 설정으로 권한을 가진 유저로 로그인한 경우만 응답이 가능한 상태로 동작한다

2-1. prometheus.yml 작성
다음으로 Prometheus가 수집할 대상을 지정하기 위해 prometheus.yml 파일을 생성해 준다
global:
scrape_interval: 5s
scrape_configs:
- job_name: 'spring-boot'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['(docker-compose 서비스 명:8080'] # docker-compose 내부 서비스명 사용
- scrape_interval: 5s: 5초마다 메트릭을 수집하도록 설정
- job_name: 수집 대상을 구분하기 위한 이름
- metrics_path: Spring Boot에서 노출하는 Prometheus 메트릭 경로
- targets: Docker 내부에서 접근할 Spring 컨테이너 주소
Docker 환경이기 때문에 localhost 대신 docker-compose 서비스명을 사용했다
2-2. docker-compose에 Prometheus 컨테이너 추가
이후 docker-compose.yml에 Prometheus 서비스를 추가하였다
prometheus:
image: prom/prometheus
container_name: prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
- image: 공식 Prometheus 이미지 사용
- volumes: 로컬의 prometheus.yml 파일을 컨테이너 내부 설정 파일로 마운트
- ports: 9090 포트를 외부로 노출하여 웹 UI 접근 가능하도록 설정
애플리케이션을 다시 빌드하고 실행 후 다음 주소로 접속하면 Prometheus UI에 접근할 수 있다
http://localhost:9090
Prometheus UI에 접속한 뒤, 상단 메뉴바 Status - Target health 메뉴로 이동하면 현재 등록된 타깃의 연결 상태를 확인할 수 있다
대시보드를 구성하기 전에 먼저 해당 메뉴에서 Spring Boot 애플리케이션이 UP 상태인지 메트릭이 정상적으로 수집되고 있는지 확인했다

연결 상태는 양호한 것으로 나왔다
만약 Target 상태가 UP이 아닌 DOWN으로 표시된다면, Prometheus가 해당 애플리케이션에 정상적으로 접근하지 못하고 있다는 의미다
이 경우에는 주로 Docker 컨테이너 이름이나 포트 설정 문제이 가능성이 높다

현재 그래프는 Prometheus에서 다음 메트릭을 조회한 화면이다
http_server_requests_seconds_count
http_server_requests_seconds_count
Spring Boot 애플리케이션 처리한 HTTP 요청의 누적 개수를 의미한다
이를 통해 Prometheus가 Spring Boot 애플리케이션의 메트릭을 정상적으로 수집하고 있음을 확인했다
3-1. docker-compose에 Grafana 컨테이너 추가
대시보드 형태로 시각화하여 모니터링하기 위해서 Grafana를 함께 사용할 예정이다
- Prometheus: 메트릭 저장 & 조회(쿼리)용 타임 시리즈 데이터베이스
- Grafana: Prometheus를 포함한 여러 데이터 소스를 연결하여 대시보드,차트,패널을 자유롭게 구성하는 시각화 도구
기존에 서비스가 정의된 docker-compose.yml에 아래와 같이 Grafana 컨테이너를 하나 더 추가해 줬다
services:
backend-1:
# ...(생략: Spring Boot 컨테이너 설정)
prometheus:
# ...(생략: Prometheus 컨테이너 설정)
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
depends_on:
- prometheus
# 필요하다면 대시보드/설정을 유지하기 위해 볼륨을 추가할 수 있다.
# volumes:
# - ./grafana-data:/var/lib/grafana
- image: 공식 Grafana 이미지 사용
- ports: 호스트의 localhost:3000으로 접속하면 Grafana 웹 UI를 접근할 수 있다
- depends_on: prometheus: Prometheus가 먼저 올라온 뒤 Grafana가 실행되도록 의존성을 명시
- volumes: 로컬 디렉터리를 /ver/lib/grafana에 마운트 하면, 컨테이너를 재시작해도 생성한 대시보드와 설정이 유지된다
3-2. Grafana 접속 및 로그인
브라우저에서 http://localhost:3000에 접속하면 Grafana 로그인 화면을 볼 수 있다

최초 로그인을 성공하면 password를 변경 페이지가 출력되며 비밀번호 변경을 요구한다

접속 후 프로필에 가보면 Language가 있는데 한국어도 지원한다 (저도 처음 알았습니다)
한국어로 변경하여 사용해 보겠습니다

3-3. Grafana 데이터 소스 추가
Grafana에서 Prometheus 메트릭을 사용하기 위해서 먼저 데이터 소스(Data Source)를 등록해야 합니다
왼쪽 메뉴의 연결 - 데이터 소스 화면에서 데이터 소스 추가를 눌러 진행한다

시계열 데이터베이스를 보면 최상단에 Prometheus가 있는 걸 볼 수 있다
클릭하여 데이터 소스를 추가해 줍니다

데이터 소스 설정 창이 나오는데 여기서 이름과 Prometheus server URL을 설정해줘야 한다
- Docker 환경에서는 Prometheus server URL는 localhost가 아닌 Docker 서비스 이름을 사용해야 한다
- Grafana와 Prometheus를 둘 다 docker-compose로 실행하고 있기 때문에 Grafana 컨테이너 입장에서 Prometheus는 localhost:9090이 아니라 같은 Docker 네트워크에 있는 prometheus:9090으로 접근해야 한다.

설정을 완료하면 하단의 저장 및 테스트를 눌러 데이터 소스 등록이 정상적으로 완료되었는지 확인할 수 있다

3-4. Grafana 대시보드 추가
Prometheus 데이터 소스를 등록했다면 이제 실제로 메트릭을 시각화하기 위해 대시보드를 생성해야 한다
왼쪽 메뉴에서 대시보드를 선택하면 "시각화 추가하여 새 대시보드 시작하기" 화면이 나타난다
여기서 여러 방식으로 대시보드를 구성할 수 있다
먼저 기본 대시보드를 사용하여 시각화를 해보고 이후 공식 커뮤니티 대시보드 템플릿을 적용하여 대시보드를 구성해보려고 합니다

시각화 추가 버튼을 누르면 다음과 같은 화면이 출력되는데 이미 데이터 소스를 등록했기 때문에
Prometheus 데이터 소스가 나오게 된다 그대로 클릭하여 대시보드를 생성해 준다

생성 직후에는 아무런 데이터가 보이지 않는다 쿼리를 통해서 커스텀으로 시각화를 설정해 볼 수 있다

쿼리를 등록하여 시각화를 설정한다면 다음과 같은 화면이 나오게 된다
간단하게 3가지를 쿼리로 등록하여 시각화를 진행해 봤다
- 초당 요청 수 (RPS)
- 평균 응답 시간
- p95 응답 시간

3-5. Grafana 템플릿 대시보드 적용
Grafana는 단순히 직접 패널을 만드는 것뿐만 아니라 Grafana 공식 커뮤니티에서 제공하는 대시보드 템플릿을 적용하여 사용할 수 있다
템플릿을 사용하면 다음과 같은 장점이 존재한다
- Spring Boot 메트릭을 빠르게 시각화할 수 있다
- 운영 환경과 유사한 모니터링 구성을 손쉽게 생성 가능
- 대시보드 구성 시간을 줄여줌
그래서 Spring Boot Micrometer 공식 대시보드 템플릿을 적용하여 사용하려고 합니다
JVM (Micrometer) | Grafana Labs
JVM (Micrometer) A dashboard for Micrometer instrumented applications (Java, Spring Boot, Micronaut). Features JVM memoryProcess memory (provided by micrometer-jvm-extras)CPU-Usage, Load, Threads, Thread States, File Descriptors, Log EventsJVM Memory Pools
grafana.com
대시보드 - 대시보드 가져오기를 눌러 템플릿을 사용할 수 있다
대시보드 ID를 입력하여 불러오기 버튼을 눌러준다 (4701)

다음과 같이 템플릿이 등록된 걸 볼 수 있다
DS_PROMETHEUS에서 데이터 소스를 선택만 해주면 됩니다
여기서는 등록했던 prometheus를 선택하고 import를 눌러줍니다

3-6. Grafana 템플릿 대시보드 적용 결과


템플릿이 적용되면 자동으로 다음과 같은 패널들이 구성된다
- HTTP 요청 수 (RPS)
- 평균 응답 시간
- p95 응답 시간
- 4xx / 5xx 에러 비율
- JVM Heap / Non-Heap 메모리 사용량
- GC 횟수 및 시간
- Thread 상태
- Tomcat/Netty 지표
단순 요청 수 확인을 넘어 애플리케이션 내부 상태까지 종합적으로 관측할 수 있는 대시보드가 완성된다
공식 커뮤니티 템플릿을 활용하면 단순 메트릭 수집을 넘어 실제 운영 환경에 가까운 모니터링 환경을 빠르게 구성할 수 있다
이번 모니터링 시스템 도입기를 통해서 모니터링 시스템을 어떻게 도입하는지 작성하게 되었는데 많이 배워가는 것 같습니다
이전에는 개념도 모르고 도입했기 때문에 어떤 식으로 동작하는지 몰라 삽질을 많이 했던 것 같습니다
'Back-end > Spring' 카테고리의 다른 글
| [Spring] Spring Data JPA Native Query 사용법 및 매핑 방법 (0) | 2026.04.05 |
|---|---|
| [Spring] @Transactional 내부 동작 원리 (0) | 2026.03.10 |
| [Spring] Spring Boot JPA 비관적 락(Pessimistic Lock)을 활용한 동시성 제어 방법 (0) | 2026.02.15 |
| [Spring] Spring Boot JPA 낙관적 락(Optimistic Lock)을 활용한 동시성 제어 방법 (0) | 2026.02.13 |
| [Spring] Spring Boot(Log) Slf4j, Logback 로깅 레벨 관리하는 방법 (0) | 2026.02.12 |