티스토리 뷰
✏️ 목차
- 배경 및 목표
- Prometheus + Grafana 직접 구축을 포기한 이유
- 최종 아키텍처
- 구축 단계
- 1단계 - Actuator 보안 강화
- 2단계 - Management 포트 분리
- 3단계 - Docker 포트 Publish
- 4단계 - Grafana Alloy EC2 설치
- 5단계 - Alloy 설정
- 트러블슈팅
- 결과
- 정리 요약
✏️ 배경 및 목표
EAT-SSU 서버는 EC2 t3.micro (1GB RAM) 위에서 Docker로 운영 중이다.
운영 관측성(observability)을 높이기 위해 아래 지표들을 실시간으로 시각화하고 싶었다.
- JVM 힙 메모리 사용량
- API 응답 시간 (HTTP Statistics)
- DB 커넥션풀 (HikariCP)
- CPU 사용률 / Load Average
- JVM GC 현황
이미 아래 의존성은 추가되어 있었고, /actuator/prometheus 엔드포인트에서 메트릭을 노출하고 있었다.
문제는 이 메트릭을 수집하고 시각화할 스택이 없다는 것이었다.
spring-boot-starter-actuator
micrometer-registry-prometheus
✏️ Prometheus + Grafana 직접 구축을 포기한 이유
처음에는 EC2에 Prometheus + Grafana 컨테이너를 직접 올리려 했는데 EC2 스펙이슈로 메모리 부족한 상태
Prometheus + Grafana를 추가하면 OOM(Out of Memory) 이 날 게 뻔한 상황.
결론: EC2 직접 구축 포기 → Grafana Cloud 무료 플랜을 사용하자

| 항목 | 상태 |
| 가용 메모리 (available) | 369MB |
| 이미 사용 중인 Swap | 501MB |
| Prometheus 예상 사용량 | ~150MB |
| Grafana 예상 사용량 | ~200MB |
✏️ 최종 아키텍처
Grafana Alloy를 선택하였다. Alloy는 Go 바이너리 단일 실행 파일이다.
컨테이너 없이 systemd 서비스로 실행하면 메모리 사용량이 약 72MB에 불과해 t3.micro에서도 충분히 감당할 수 있다.
Spring App (port 9000 / 9001)
│
│ scrape (localhost:9001/actuator/prometheus)
▼
Grafana Alloy ← EC2 설치, systemd 서비스, ~72MB
│
│ remote_write (HTTPS)
▼
Grafana Cloud Mimir ← 무료 플랜, 외부 저장소
│
│ query
▼
Grafana Dashboard ← 실시간 시각화
✏️ 구축 단계
1단계 - Actuator 보안 강화
기존 SecurityConfig의 AUTH_WHITELIST를 확인해 보니 /actuator/**가 통째로 열려 있었다. 이는 /actuator/env, /actuator/metrics 같은 민감한 정보까지 외부에 노출되는 문제가 있다.
# Before
"/actuator/**"
# After
# 불필요한 Actuator 엔드포인트는 인증 없이 외부 접근이 불가하도록 화이트리스트를 최소화했다.
"/actuator/health",
"/actuator/prometheus"
2단계 - Management 포트 분리
Actuator 엔드포인트를 앱 포트(9000)와 분리하여 외부에서 직접 접근할 수 없도록 별도 포트(9001)에 바인딩했다.
address: 127.0.0.1로 설정하면 컨테이너 내부 루프백에만 바인딩된다.
EC2 호스트에서 포트 포워딩(-p 127.0.0.1:9001:9001)을 받으려면 컨테이너가 0.0.0.0 에 바인딩되어 있어야 한다.
이를 127.0.0.1로 놓으면 Spring Boot 로그에는 정상 기동으로 찍혀도, 호스트에서 접속이 안 된다.
# application-dev.yml
management:
server:
port: 9001
address: 0.0.0.0 # ⚠️ Docker 컨테이너 특성상 반드시 0.0.0.0
metrics:
tags:
application: eatssu-dev # Grafana 대시보드 필터용 레이블
endpoints:
web:
exposure:
include: health, info, metrics, prometheus
3단계 - Docker 포트 Publish
deploy.yml의 docker run 명령에 9001 포트를 추가했다.
127.0.0.1:9001:9001로 바인딩하면 EC2 내부(Alloy)에서만 접근 가능하고 외부 인터넷에서는 접근이 차단된다.
# .github/workflows/deploy.yml
sudo docker run -d --name eatssu-dev \
--restart unless-stopped \
-p 9000:9000 \
-p 127.0.0.1:9001:9001 \ # ← 추가: EC2 내부에서만 접근 가능
...
| 포트 | 바인딩 | 역할 |
| 9000 | 0.0.0.0:9000 | 앱 API (외부 공개) |
| 9001 | 127.0.0.1:9001 | Actuator / Prometheus (EC2 내부 전용) |
4단계 - Grafana Alloy EC2 설치
Docker 컨테이너 대신 systemd 서비스로 설치하여 메모리 오버헤드를 최소화했다.
# 1. Grafana APT 저장소 추가
sudo apt-get install -y apt-transport-https software-properties-common wget
sudo mkdir -p /etc/apt/keyrings/
wget -q -O - <https://apt.grafana.com/gpg.key> \
| gpg --dearmor \
| sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] <https://apt.grafana.com> stable main" \
| sudo tee /etc/apt/sources.list.d/grafana.list
# 2. Alloy 설치
sudo apt-get update
sudo apt-get install alloy -y
5단계 - Alloy 설정
Alloy 설정 파일(/etc/alloy/config.alloy)을 작성한다.
# /etc/alloy/config.alloy
# 1. Spring App의 /actuator/prometheus 스크랩
prometheus.scrape "spring_app" {
targets = [{
__address__ = "localhost:9001",
__metrics_path__ = "/actuator/prometheus",
}]
forward_to = [prometheus.remote_write.metrics_hosted_prometheus.receiver]
}
# 2. Grafana Cloud Mimir로 remote_write
prometheus.remote_write "metrics_hosted_prometheus" {
endpoint {
name = "hosted-prometheus"
url = "<https://prometheus-prod-49-prod-ap-northeast-0.grafana.net/api/prom/push>"
basic_auth {
username = "YOUR_USERNAME" # Grafana Cloud에서 발급
password = "YOUR_API_TOKEN" # Grafana Cloud API Token
}
}
}
설정 완료 후 서비스 활성화 및 시작:
sudo systemctl enable alloy
sudo systemctl start alloy
# 상태 확인
sudo systemctl status alloy
✏️ 트러블슈팅
문제: 9001 포트에 접속이 안 된다
배포 후 Alloy가 메트릭을 수집하지 못했다. 직접 확인해 보니 Spring Boot 로그에는 아무런 이상이 없었다.
Tomcat started on port(s): 9001 (http) with context path ''
원인 분석
Docker 컨테이너 네트워크는 격리된 네임스페이스를 사용하기 때문에, 컨테이너 내부의 127.0.0.1 은 호스트의 127.0.0.1과 다르다. EC2 호스트에서 포트 포워딩이 동작하려면 컨테이너가 0.0.0.0에 바인딩되어 있어야 한다.
| 설정 | 동작 |
| management.server.address: 127.0.0.1 | 컨테이너 내부 루프백(127.0.0.1)에만 바인딩 |
| -p 127.0.0.1:9001:9001 (호스트) | EC2 호스트 → 컨테이너로 포워딩 시도 |
| 결과 | 컨테이너가 0.0.0.0에 안 열려 있으므로 포워딩 불가 |
해결
이렇게 하면 컨테이너는 모든 인터페이스에서 수신하되,
EC2 호스트 측에서는 루프백으로만 노출되어 보안을 유지하면서 Alloy가 접근할 수 있다.
# application-dev.yml
management:
server:
address: 0.0.0.0 # 127.0.0.1 → 0.0.0.0 으로 변경
# deploy.yml
-p 127.0.0.1:9001:9001 # EC2 호스트 측은 127.0.0.1 유지 (외부 차단)
✏️ 결과
Grafana Cloud 대시보드(Dashboard ID: 19004 - Spring Boot 3.x Statistics)에서 아래 지표들이 실시간으로 수집되고 있다.

✏️ 정리 요약
| 항목 | 내용 |
| 수집 에이전트 | Grafana Alloy (systemd 서비스, ~72MB) |
| 메트릭 저장소 | Grafana Cloud Mimir (무료 플랜) |
| 시각화 | Grafana Cloud Dashboard (ID: 19004) |
| Actuator 보안 | 화이트리스트 최소화 (/health, /prometheus 만 공개) |
| 포트 보안 | management port 9001, EC2 내부(127.0.0.1)에서만 접근 가능 |
| 비용 | 무료 |
✏️ 알게된 점
- t3.micro에서 Prometheus + Grafana 직접 운영은 무리다. Grafana Cloud 무료 플랜을 활용하자.
- Grafana Alloy는 경량 Go 바이너리다. Docker 대신 systemd 서비스로 실행하면 메모리 부담이 크게 줄어든다.
- Docker 컨테이너에서 127.0.0.1 바인딩은 함정이다. 호스트 포트 포워딩을 쓰려면 컨테이너는 0.0.0.0, 호스트는 127.0.0.1로 노출하는 패턴을 써야 한다.
- t3.micro처럼 메모리가 빡빡한 환경에서도 Grafana Cloud + Alloy 조합으로 충분히 프로덕션 수준의 운영 모니터링을 구축할 수 있다.
'Back-End > 잇슈' 카테고리의 다른 글
| [EAT-SSU / 잇슈] - CI/CD 개선 작업 정리 #1 (0) | 2026.07.05 |
|---|---|
| [EAT-SSU / 잇슈] OAuth 토큰 로그 노출 방지 작업 정리 (0) | 2026.06.23 |
| [EAT-SSU / 잇슈] Docker 배포 스크립트 개선 - 전체 컨테이너 삭제 방지 (0) | 2026.06.23 |
| [EAT-SSU / 잇슈] GitHub Actions 빌드 시간 줄이기 (0) | 2026.06.22 |
| [EAT-SSU / 잇슈] RDS 데이터 다른 AWS 계정으로 이전하기 (0) | 2026.05.03 |
- Total
- Today
- Yesterday
- 알고리즘
- DFS
- 반복문
- 유니온 파인드
- 카운팅 정렬
- 세그먼트 트리
- 자바스크립트
- 스프링 부트 crud 게시판 구현
- 백준 풀이
- 스택
- Do it!
- 우선순위 큐
- 백준
- html
- C++ Stack
- HTML5
- 자료구조
- 유클리드 호제법
- 이분 매칭
- java
- C++
- c++ string
- js
- BFS
- 알고리즘 공부
- 에라토스테네스의 체
- DP
- CSS
- 투 포인터
- 자바
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |