-
Notifications
You must be signed in to change notification settings - Fork 0
[CL] 클라우드 v1 모니터링 구축 문서화
v1은 GCP 단일 VM 환경에서 다음과 같은 구조로 운영되었다.
-
서비스 구성
- BE: Spring Boot, MySQL
- FE: Next.js
- AI: FastAPI + ChromaDB
-
운영 구조
- 단일 Ubuntu VM에서 PM2 기반 서비스 구동
- Shell Script 기반의 수동 배포 프로세스 운영 (deploy.sh, rollback.sh 등 사용)
모니터링 도입 전에는 개발 혹은 운영 환경에서 문제가 발생해도 에러 로그 확인 외에는 별도의 알림이나 지표 확인 수단이 없었다. 특히 다음과 같은 상황에서 모니터링 도입의 필요성이 명확히 드러났다.
- 에러 발생 시 로그만으로는 정확한 원인과 영향을 받은 사용자를 파악하기 어려움
- 서버 CPU, 메모리, 디스크 등의 자원 이상 징후를 사후에야 인지함
- 특정 API의 속도 저하나 느린 DB 쿼리 등 애플리케이션 성능 저하 현상을 실시간으로 탐지할 수 없음
- 배포 이후 서비스가 정상적으로 구동되고 있는지 확인하기 위해 사람이 수동으로 접근해야 했음
이러한 문제들을 해결하고, 안정적인 운영 환경을 구축하기 위해 다음과 같은 목표로 모니터링 체계를 도입하였다.
- 서비스 운영의 안정성 확보
- 서비스 로그의 순환, 저장, 실시간 확인 체계화
- 배포 및 장애 발생 시 담당자에게 즉각적인 알림 제공
- 실시간으로 애플리케이션 성능 및 시스템 자원(CPU, Memory 등) 상태 모니터링
- 에러 발생 시 사용자 정보 및 요청 맥락을 포함한 정확한 원인 추적 및 분석
-
도입 이유
초기에는 서비스마다 실행 방식이 달라, 전체 서비스를 통합적으로 관리하기 어려웠다.
이를 해결하기 위해 Node 기반의 프로세스 매니저인 PM2를 도입하여 모든 서비스를 일관된 방식으로 실행할 수 있도록 구성하였다.
또한
pm2 logs
명령어는 실시간 로그만 확인 가능해, 장애 발생 시 이전 로그를 추적하기 어려웠다. 로그 적재 및 보존을 위해pm2-logrotate
를 함께 도입하여 운영 편의성을 개선하였다. -
주요 역할
- 서비스별 프로세스 관리 및 시작
- 로그 실시간 모니터링 (
pm2 logs
,pm2 monit
) - 서버에 로그 적재
- 로그 순환 및 디스크 관리 (
pm2-logrotate
설정 기반)
-
구성 방식
pm2 install pm2-logrotate pm2 set pm2-logrotate:max_size 10M # 10MB 초과 시 자동으로 logrotate 작동하여 파일 전환 pm2 set pm2-logrotate:retain 7 # 로그 파일 최대 7개까지만 유지 ~/.pm2/logs/nemo-backend-out.log ~/.pm2/logs/nemo-backend-error.log # pm2 monit or pm2 nemo-backend log로도 확인 가능
-
도입 이유
운영 초기에는 VM의 스펙(CPU, 메모리 등)을 얼마나 할당해야 적절한지 판단이 어려웠다.
이에 따라 실제 사용량을 시각화하고, 이상 징후를 빠르게 확인할 수 있는 도구가 필요했다.
v1 규모에서는 Grafana + Prometheus 같은 표준 스택은 오버헤드가 크다고 판단했으며,
Netdata는 간단한 설치로 시스템 자원 모니터링과 Discord 알림 연동까지 가능한 경량 대안
으로 적합하다고 판단하여 도입하였다.
-
주요 역할
- 실시간 CPU, Memory, Disk 사용량 시각화
- 웹 기반 경량 대시보드 제공
- Discord Webhook 연동을 통한 이상 감지 알림
-
구성 방식
# 1. Netdata 설치 bash <(curl -Ss https://my-netdata.io/kickstart.sh) netdata -v # 2. Netdata 시작 sudo systemctl start netdata # 서비스 시작 sudo systemctl enable netdata # 부팅 시 자동 시작 설정 sudo systemctl status netdata # 상태 확인 # 3. 방화벽 포트 개방 in GCP (19999) # 4. 접속: http://<서버 ip>:19999 # 5. 디스코드 연동 (디스코드 웹 후크 생성 후 URL 복사) cd /etc/netdata sudo ./edit-config health_alarm_notify.conf
-
도입 이유
과거에는 개발 파트에서 배포 요청이 오면, 클라우드 파트가 수동으로 배포를 수행하고 구두 또는 채팅으로 배포 성공 여부를 알리는 비효율적인 커뮤니케이션 구조였다.
이를 개선하기 위해, 개발자는 Push만 수행하면 자동으로 배포가 이루어지고, 배포 결과가 실시간으로 Discord에 전송되도록 구성하였다.
또한
cron
기반 주기적 헬스체크를 통해 서비스 이상 징후가 감지되었을 때도, 운영자에게 즉시 알림이 전달될 수 있도록 Discord Webhook 연동을 확장하였다. -
주요 역할
- 배포 성공 / 실패 알림
- 롤백 성공 / 실패 알림
- 헬스체크 실패 시 알림
- Webhook 분리 운영
- 파트 전용 Webhook: 배포 이벤트 중심
- 클라우드 Webhook: 배포 + 헬스체크 통합 모니터링
-
구성 방식
-
각 스크립트(
deploy.sh
,rollback.sh
,healthcheck.sh
)에서curl
명령어로 Discord Webhook 호출 -
전송 메시지 형식
✅ [배포 성공: develop] nemo-ai 배포 완료! ❌ [배포 실패: develop] nemo-ai 배포 실패! ❌ [롤백 실패: develop] nemo-backend 롤백 실패! (Rollback Point: 20250516-2231) ✅ [롤백 성공: develop] nemo-backend 롤백 완료! (Rollback Point: 20250516-2231) 🚨 [헬스체크 실패: develop] nemo-backend 비정상 상태 감지!
-
메시지 형식 규칙
- 서비스명 및 환경(branch) 명시:
nemo-backend
,develop
- 결과 상태에 따라 이모지 및 접두어 구분: ✅ / ❌ / 🚨
- 롤백 알림에는 Rollback Point 포함
- 모든 메시지는
nemo-cloud-bot
을 통해 단일 채널로 전송됨
- 서비스명 및 환경(branch) 명시:
-
-
도입 이유
초기에는 로그 기반으로만 에러를 추적하고 있었기 때문에, 정확한 발생 위치나 원인을 확인하기 어려웠다.
또한 전체 서비스에 걸쳐 최소한의 트레이싱과 APM 역할을 수행할 수 있는 도구가 필요했으며, 당시에는 Prometheus, Grafana 등 표준 스택을 도입하기엔 과도하다고 판단하였다.
이에 따라경량 설치가 가능하고 모든 서비스에 적용 가능한 Sentry를 도입하였다.
-
주요 역할
- 예외 및 에러 자동 수집
- 간단한 성능 추적 및 트랜잭션(Span) 기반 트레이싱
- 배포 릴리즈와 연동하여 버전별 오류 추적
-
구성 방식
-
Backend (Spring Boot)
-
sentry-spring-boot-starter
라이브러리 사용 -
application.yml
에 설정 추가 -
SENTRY_RELEASE
,SENTRY_ENV
,SENTRY_DSN
을.env
로 주입
-
-
AI (FastAPI)
-
sentry-sdk
+ASGIIntegration
,LoggingIntegration
사용 -
sentry_sdk.init()
에 설정 포함 (traces_sample_rate
,profile_sample_rate
,release
등) -
.env
를 통해 환경 변수로 설정 관리
-
-
Backend (Spring Boot)
-
도입 이유
기존 모니터링 구조는 에러, 시스템, APM 지표가 도구별로 분산되어 있어 통합적인 분석이 어려웠고, Sentry 기반의 APM 분석도 정밀도와 범위 측면에서 한계를 가졌다.
이에 따라 OTEL(OpenTelemetry) 기반의 SigNoz를 도입하여 보다 정밀한 API 및 시스템 리소스 추적이가능하고, 대시보드를 통해 전체 지표를 통합 분석할 수 있는 환경을 마련하였다.
v1에서는 백엔드(Spring Boot) 서비스에만 적용되었으며, v2에서는 전체 서비스로 점진적 확대 예정이다.
-
주요 역할
템플릿 기반 대시보드를 활용하여 다음과 같은 모니터링 구성
- APM Metrics: 응답 시간, 처리량, 오류율
- Host Metrics: CPU, Memory, Disk I/O, Network I/O
- JVM Metrics: Heap, GC, Thread 상태 등
- Key Operations: 주요 API별 트레이싱
- DB Class Monitoring: 쿼리 실행 시간, 커넥션 수
- HTTP API Monitoring: 상태 코드 분포, 지연 구간 확인
-
구성 방식
- SigNoz 서버는 Docker Compose 기반 별도 구성
- BE(Spring Boot)에 OTLP Exporter 설정 추가 (
application.yml
) - Collector에 hostfs 마운트 및 환경변수 설정 (GCP VM 기준)
범주 | 주요 지표 항목 | 도구 | 비고 |
---|---|---|---|
API 성능 | 응답 시간 (p95/p99), 처리량, 오류율 | SigNoz, Sentry | API별 지연 구간 및 상태코드 분석 |
시스템 리소스 | CPU 사용률, 메모리 사용량, 디스크 I/O | Netdata, SigNoz | 실시간 대시보드 + 히스토리 확인 가능 |
애플리케이션 | 예외 발생 건수, 트레이스 스팬 정보 | Sentry, SigNoz | 사용자 기반 오류 추적 포함 |
DB 성능 | 쿼리 실행 시간, 연결 수, 슬로우 쿼리 비율 | SigNoz | JDBC 기준 쿼리별 응답 시간 확인 |
서비스 상태 | 헬스체크 성공/실패 여부 | Cron Job + Discord | 배포 후 상태 검증 자동화 |
-
문제 인지 속도 향상
- 배포 실패, 헬스체크 실패, 에러 발생 등 운영 이슈를 즉시 감지 및 대응 가능
- 예:
healthcheck.sh
실패 시 Discord 알림 → 실시간 롤백 판단
-
로그 관리 체계화
-
pm2-logrotate
도입으로 로그 누락/디스크 폭주 문제 해결 - 장애 분석 시점에도 과거 로그 추적 가능성 확보
-
-
에러 추적 및 사용자 기반 디버깅 가능
- Sentry를 통해 어떤 사용자, 어떤 버전, 어떤 요청에서 에러 발생했는지 명확히 파악
- QA 단계에서 재현 어려운 버그도 스택 트레이스 + 요청 맥락 기반으로 분석 가능
-
시스템 병목 사전 탐지
- Netdata와 SigNoz 대시보드를 통해 CPU, Memory, DB 지연 등 병목 요소를 실시간 시각화
-
통합된 운영 인프라 기반 확립
- Shell Script, Sentry, SigNoz, Discord Webhook 등 도구들을 유기적으로 연계
- 별도 관제 없이도 단일 VM 환경에서 충분한 운영 안정성 확보
4.2. 운영 사례: Netdata 알림 기반 NGINX 이상 탐지 (이슈 링크)
-
발생 일시: 2025년 5월 15일 오후 7시 40분
-
문제 내용
Netdata에서 Web Log 이상 알림 수신 (
success = 0%
,bad requests = 100%
).env
파일 등 민감 경로에 대한 해외 IP로 요청 다수 발생 → 자동화 스캔 공격 의심 -
조치 내역
-
Discord Webhook을 통해 실시간 이상 감지
-
NGINX Access Log 확인 (
tail -n 100
) -
.env
접근 차단 설정 추가location ~ /\.env { deny all; return 404; }
-
-
**결과
-
.env
파일 외부 노출 방지 - Netdata + Discord 조합으로 2분 내 실시간 대응 완료
-
-
모니터링 적용 범위의 불균형
현재는 백엔드(Spring Boot) 서비스에만 모니터링 체계가 완전하게 적용되어 있으며,
**프론트엔드(Next.js)**와 AI(FastAPI) 서비스에는 일부 도구(Sentry, SigNoz 등)만 부분 적용된 상태
→ 서비스 전반으로의 확대 적용이 필요하다.
-
알림 체계의 제한
현재 Discord Webhook 기반 알림은 배포 / 롤백 / 헬스체크에만 적용되어 있으며,
리소스 이상 감지, 에러 급증, 지표 기반 경고 등은 다소 부족한 상태
→ 향후 v2에서는 체계적인 알림 시스템 도입 예정
-
장애 대응 시나리오 구성의 어려움
실서비스 트래픽이 많지 않아, 실제 장애 상황을 재현하거나 대응 시나리오를 구성하는 데 한계가 존재
→ v2 정식 배포 전 체계적인 부하 테스트를 통해 장애 유발 및 대응 체계 확립이 필요
- 배포는 시작일 뿐, 진짜 중요한 건 운영이었다.
- 단일 GCP VM 환경에서도 PM2, Sentry, Netdata, SigNoz, Discord Webhook 등을 연계해 경량이지만 실질적인 모니터링과 알림 체계를 구축할 수 있었다.
- 이상 감지 → 알림 → 분석 → 대응까지 연결되는 운영 루프를 갖춘 초기 체계가 정착되었고, 이는 향후 인프라 확장 시 기초적인 운영 기반이 될 것으로 예상한다.
-
v2
-
SigNoz의 Alert Rule을 활용한 이상 감지 알림 시스템 도입
-
프론트엔드, AI 서비스까지 OTEL 기반 계측 확대
-
현재는 단일 VM에 서비스와 모니터링 리소스가 혼재되어 있음
→ 향후 모니터링 전용 VM을 분리하여 리소스 경합 해소 및 운영 분리 예정
-
체계적인 부하 테스트 시나리오를 통해 장애 시나리오 기반 대응 체계 수립
-
-
build.gradle.kts
plugins { id ("io.sentry.jvm.gradle") version "5.6.0" } repositories { mavenCentral() } dependencies { implementation("io.sentry:sentry-spring-boot-starter-jakarta:8.12.0") } sentry { // Generates a JVM (Java, Kotlin, etc.) source bundle and uploads your source code to Sentry. // This enables source context, allowing you to see your source // code as part of your stack traces in Sentry. includeSourceContext = true org = "glenn-bn" projectName = "java-spring-boot" authToken = System.getenv("SENTRY_AUTH_TOKEN") }
-
application.yml
sentry: dsn: ${SENTRY_DSN} environment: ${SENTRY_ENV} release: ${SENTRY_RELEASE} traces-sample-rate: ${SENTRY_SAMPLE_RATE:0.1} send-default-pii: ${SENTRY_SEND_PII:false} enable-tracing: true
-
.env
# Sentry SENTRY_AUTH_TOKEN=<SENTRY_AUTH_TOKEN> SENTRY_DSN=<SENTRY_DSN> SENTRY_ENV=dev SENTRY_RELEASE=1.0.2 SENTRY_SAMPLE_RATE=1.0 SENTRY_SEND_PII=true SPRING_PROFILES_ACTIVE=dev
-
OpenTelementry Java Agent 설치
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar -P ~/
-
.env
구성# Signoz JAVA_TOOL_OPTIONS="-javaagent:$HOME/opentelemetry-javaagent.jar" OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 OTEL_EXPORTER_OTLP_PROTOCOL=grpc OTEL_SERVICE_NAME=backend-service OTEL_RESOURCE_ATTRIBUTES="deployment.environment=dev,team=nemo" OTEL_METRICS_EXPORTER=otlp OTEL_LOGS_EXPORTER=otlp # (선택) 로그 수집을 원할 경우 OTEL_INSTRUMENTATION_RUNTIME_METRICS_ENABLED=true # ✅ JVM GC, Heap, Thread 등 활성화 OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED=true # (선택) 표준 instrumentation 전체 활성화
-
/signoz/deploy/docker/docker-compose.yaml
signoz: ports: - "3301:8080" # 8080은 Spring 사용 중이기 때문에 3301로 변경
-
/signoz/deploy/docker/otel-collector-config.yaml
receivers: hostmetrics: collection_interval: 60s scrapers: cpu: {} load: {} memory: {} disk: {} filesystem: {} network: {} processors: batch: send_batch_size: 10000 send_batch_max_size: 11000 timeout: 10s resourcedetection: detectors: [env, system] timeout: 2s system: hostname_sources: [os] pipelines: traces: receivers: [otlp] processors: [signozspanmetrics/delta, batch] exporters: [clickhousetraces] metrics: receivers: [otlp] processors: [batch] exporters: [clickhousemetricswrite, signozclickhousemetrics] metrics/prometheus: receivers: [prometheus] processors: [batch] exporters: [clickhousemetricswrite/prometheus, signozclickhousemetrics] metrics/hostmetrics: receivers: [hostmetrics] processors: [resourcedetection] exporters: [clickhousemetricswrite, signozclickhousemetrics] logs: receivers: [otlp] processors: [batch] exporters: [clickhouselogsexporter]
-
Docker Compose 재시작
# SigNoz 설치 디렉토리로 이동 cd ~/signoz/deploy # 변경된 설정 반영을 위해 docker-compose 재시작 docker compose down docker compose up -d