-
[운영] 모니터링, 옵저버빌리티카테고리 없음 2026. 7. 11. 01:45
최근에 성능 개선에 관심이 생겼다. 성능 개선을 하기 위해서 CPU 자원을 얼마나 사용 중인지 어디서 병목현상이 발생했는지 알아야 한다.
그래서 그것을 알기 위한 모니터링, 옵저버빌리티의 개념과 메트릭을 수집할 수 있는 Micrometer에 대해서 알아보자.
1. 옵저버빌리티(Observability)
1.1 모니터링과 옵저버빌리티
1. 모니터링 (Monitoring) • 초점: 시스템에 이상이 생겼는가? (What) • 대상: 미리 예측 가능한 영역의 문제 (Known Unknowns) • 동작: 대시보드에 미리 설정해 둔 수치(예: CPU > 80%, 500 에러 발생률 > 5%)를 감시하고, 기준을 벗어나면 시스템이 개발자에게 알람을 전송합니다. 수동적이고 상태 중심적입니다. 2. 옵저버빌리티 (Observability, 관측성) • 초점: 왜 그런 이상 현상이 발생했는가? (Why) • 대상: 사전에 전혀 예측하지 못한 비정상적인 동작이나 장애 (Unknown Unknowns) • 동작: 시스템 내부의 유기적인 데이터 흐름을 통해 외부에서 내부의 상세 상태를 추론하는 시스템의 속성입니다.모니터링은 "시스템에 문제가 생겼는가?(What)"를 아는 것이고, 옵저버빌리티(Observability, 관측성)는 "그 문제가 왜 발생했는가?(Why)"를 파악할 수 있는 시스템의 능력이다.
모니터링은 "무엇을 볼지" 미리 정한다. 하지만 옵저버빌리티는 시스템의 모든 상태를 수집해 두고, 문제가 발생했을 때 "왜"를 추적할 수 있게 한다.
1.2 옵저버빌리티의 3가지 축
┌─────────────────────────────────────────────────────────┐ │ Observability │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Metrics │ │ Logs │ │ Traces │ │ │ │ (메트릭) │ │ (로그) │ │ (트레이스) │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ │ "무엇이" "무슨 일이" "어디서" │ │ "얼마나" "일어났는가" "어떤 경로로" │ │ │ └─────────────────────────────────────────────────────────┘축 설명 도구 예시 Metrics 수치화된 시계열 데이터 Prometheus, Datadog Logs 이벤트 기록 ELK Stack, Loki Traces 요청 흐름 추적 Jaeger, Zipkin 세 가지를 조합하면 장애 원인을 빠르게 파악할 수 있다:
- Metrics: 시스템의 상태나 성능을 숫자로 요약하여 집계한 데이터
- 예시: "현재 웹 서버의 CPU 사용률이 85%이고, 초당 500건의 요청을 처리하고 있습니다." (문제 발생 감지)
- Logs: 특정 시간에 발생한 개별 이벤트의 상세 내용을 기록한 텍스트 데이터
- 예시: "2026-07-11 00:10:05 [ERROR] 데이터베이스 연결 시간 초과(Timeout)가 발생했습니다." (상세 내용)
- Traces: 하나의 요청이 여러 서비스나 시스템을 거쳐가는 전체 이동 경로와 소요 시간을 추적한 데이터
- 예시: "사용자의 결제 요청이 API 게이트웨이(10ms)를 거쳐 인증 서비스(50ms)와 결제 서비스(200ms)를 순차적으로 통과하여 총 260ms 만에 완료되었습니다." (병목 지점)
1.3 실제 장애 대응
장애 발생 시나리오: 오전 10:05 - 사용자 "결제가 안 돼요" 신고 옵저버빌리티 없이: ├── "로그 파일 어디있지?" ├── "어떤 서버에서 발생했지?" ├── "재현이 안 되는데..." └── 3시간 후 원인 파악 옵저버빌리티 있으면: ├── 메트릭: 10:03부터 결제 API 지연 급증 ├── 로그: PaymentService에서 timeout 에러 ├── 트레이스: 외부 PG사 API 응답 5초 └── 15분 만에 원인 파악
2. Prometheus & Micrometer
2.1 Prometheus

특징:
- Pull 방식: Prometheus가 앱에서 메트릭을 가져감
- 시계열 DB: 시간에 따른 메트릭 변화 저장
- PromQL: 강력한 쿼리 언어
Pull 방식의 장점:
- 수집의 주도권을 중앙 서버가 가지고 있음 (안정성 up, 결합도 down)
- 애플리케이션은 메트릭을 노출만 하면 됨
- Prometheus가 중앙에서 스크래핑 대상 관리
- 애플리케이션의 독립성 유지
2.2 Micrometer
Micrometer = 메트릭의 SLF4J (추상화 계층) ┌─────────────────────────────────────────────────────────┐ │ Application Code │ │ │ │ meterRegistry.counter("orders").increment()│ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ Micrometer │ │ │ │ (추상화 계층) │ │ │ └────────┬────────┘ │ │ │ │ │ ┌──────────────────┼──────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ │ Prometheus │ │ Datadog │ │ CloudWatch │ │ │ │ Registry │ │ Registry │ │ Registry │ │ │ └────────────┘ └────────────┘ └────────────┘ │ └─────────────────────────────────────────────────────────┘로깅을 할 때 특정 프레임워크(Logback 등)에 종속되지 않기 위해 SLF4J 같은 공통 인터페이스를 사용하는 것과 같은 역할을 '메트릭 수집' 영역에서 수행한다.
1. 모니터링 툴에 종속되지 않는 유연성 (Vendor-neutral, 벤더 중립적)
2. 모니터링 시스템 변경 시 코드 수정 불필요
3. Spring Boot Actuator와 자동 통합
4. 프로메테우스 pull 방식과 연계 좋음
5. 다양한 측정 도구 제공 (Counter, Gauge, Timer, Distribution Summary)
3. 메트릭 유형
3.1 4가지 기본 메트릭 타입
┌─────────────────────────────────────────────────────────┐ │ 1. Counter (카운터) │ │ - 증가만 가능 (감소 불가) │ │ - 재시작 시 0으로 리셋 │ │ - 예: 총 요청 수, 총 에러 수 │ │ │ │ 0 → 1 → 2 → 3 → 4 → 5 → ... │ └─────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ 2. Gauge (게이지) │ │ - 증가/감소 모두 가능 │ │ - 현재 상태를 나타냄 │ │ - 예: 현재 메모리 사용량, 활성 스레드 수 │ │ │ │ 50 → 70 → 45 → 80 → 30 → ... │ └─────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ 3. Timer (타이머) │ │ - 이벤트 지속 시간 + 발생 횟수 │ │ - 예: API 응답 시간, 쿼리 실행 시간 │ │ │ │ count: 100, sum: 5.2s, max: 0.5s │ └─────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ 4. Histogram (히스토그램) │ │ - 값의 분포를 버킷으로 측정 │ │ - 예: 응답 시간 분포 (0-100ms: 50%, 100-500ms: 40%) │ │ │ │ bucket_0.1: 50, bucket_0.5: 90, bucket_1.0: 98 │ └─────────────────────────────────────────────────────────┘측정 대상 메트릭 타입 예시 누적 개수 Counter 주문 수, 에러 수 현재 상태 Gauge 메모리 사용량, 활성 연결 수 소요 시간 Timer API 응답 시간 값 분포 Histogram 응답 시간 백분위 1. Counter (카운터) : 오직 증가만 하는 누적 지표
카운터는 이름 그대로 횟수를 세는 역할을 하며, 오직 양수 단위로 '증가'만 가능한 단일 수치이다.- 핵심 특징: 절대 줄어들지 않으며, 서버가 재시작될 때만 0으로 초기화된다.
- 활용 방법: 모니터링 시스템(예: 프로메테우스)에서 이 누적 데이터를 가져가 초당 발생률(Rate)이나 특정 구간의 증가폭을 계산하는 데 주로 사용한다.
- 주의점: 스레드 풀의 활성 스레드 수처럼 값이 올라갔다 내려갈 수 있는 지표에는 절대 사용하면 안 된다.
- 실무 예시: 누적 API 호출 횟수, 누적 주문 생성 건수, 예외(Exception) 발생 총합
2. Gauge (게이지) : 특정 시점의 현재 상태
게이지는 자동차의 속도계처럼 특정 시점의 '현재 상태 값(스냅샷)'을 나타내는 지표이다. 카운터와 달리 값의 증가와 감소가 모두 가능하다.- 핵심 특징: 데이터를 수집하는 바로 그 찰나의 값을 보여준다.
- 활용 방법: 지속적으로 변동하는 시스템의 리소스 상태나 대기열의 크기를 파악할 때 유용하다.
- 주의점: 측정하는 순간의 값만 기록하므로, 메서드 실행 시간처럼 아주 짧은 찰나에 발생하는 이벤트를 기록하는 데는 적합하지 않다.
- 실무 예시: 현재 활성화된 DB 커넥션 수, 큐(Queue)에 쌓여있는 대기 메시지 수, 현재 JVM 메모리 사용량
3. Timer (타이머) : 시간과 빈도를 동시에 측정
타이머는 짧은 시간 동안 발생하는 이벤트의 '지연 시간(Latency)'과 '발생 빈도(Frequency)'를 동시에 측정하기 위한 타입이다.
- 핵심 특징: 타이머를 하나만 세팅해도 내부적으로 이벤트 발생 횟수(Count), 총 소요 시간(Total Time), 최대 소요 시간(Max)을 한 번에 수집해 준다.
- 활용 방법: 애플리케이션의 성능 병목을 찾거나 API 응답 속도를 모니터링할 때 필수적으로 사용한다.
- 주의점: 백그라운드 배치 작업처럼 실행 시간이 매우 긴 작업을 측정할 때는 일반 Timer가 아닌 LongTaskTimer라는 전용 타입을 사용해야 한다.
- 실무 예시: HTTP API 엔드포인트별 응답 시간, JPA DB 쿼리 실행 소요 시간
4. Distribution Summary (분포 요약) : 데이터 크기와 양의 분포
분포 요약은 타이머와 동작 방식이 거의 같지만, 측정하는 단위가 '시간'이 아니라 특정 이벤트의 '크기나 양'이라는 점에서 차이가 있다.- 핵심 특징: 타이머처럼 발생 횟수, 총합, 최댓값을 제공하며, 데이터가 어떤 형태로 분포되어 있는지(예: 상위 95%의 크기)를 추적한다.
- 활용 방법: 단순 평균값으로는 파악하기 힘든 데이터 사이즈의 편차나 패턴을 분석하고 싶을 때 유용하다.
- 실무 예시: 사용자 요청(Request Payload)의 바이트(Byte) 크기 분포, 한 번에 업로드되는 파일 사이즈 통계, DB에서 한 번에 조회해 오는 레코드 건수
3.2 Counter, Gauge 선택 기준
Counter를 써야 할 때: - "지금까지 총 몇 건?" → 주문 수, 에러 수 - 절대 감소하지 않는 값 - rate() 함수로 "초당 증가율" 계산 가능 Gauge를 써야 할 때: - "지금 현재 몇 개?" → 활성 연결 수, 큐 크기 - 증가/감소가 모두 가능한 값 - 순간 상태가 중요한 값 - Metrics: 시스템의 상태나 성능을 숫자로 요약하여 집계한 데이터