본문으로 건너뛰기

부록 B: 메트릭 상세 분석 (심화 학습)

대상 독자: APM 엔지니어, DevOps 엔지니어, 시스템 성능 튜닝 담당자

이 문서는 메트릭 수집의 상세 기술 내용을 다룹니다. 일반 사용자는 부록 A: 메트릭 데이터 수집를 먼저 읽으시길 권장합니다.


개요

이 문서는 FlowKat이 수집하는 모든 메트릭의 상세 정의, 데이터 원본, 계산 방법, 엔지니어 해석 가이드를 제공합니다.


1. Host Agent 메트릭 상세

1.1 CPU 메트릭

1.1.1 메트릭 정의

메트릭 명상수데이터 원본계산 방법실무 의미주의 기준
CPU 사용률HOST_CPU/proc/stat100 × (1 - idle / total)전체 CPU 활용도, 코어별 평균>80%: 주의, >90%: 위험
시스템 CPUHOST_SYSCPU/proc/stat100 × system / total커널 모드 실행 비율 (시스템 콜, I/O)>30%: I/O 병목 의심
사용자 CPUHOST_USERCPU/proc/stat100 × user / total애플리케이션 실행 비율지속 >70%: 프로세스 최적화 필요

1.1.2 데이터 원본 분석

**/proc/stat** 파일 포맷:

cpu 2255 34 2290 22625563 6290 127 456 0 0 0 0
^user ^nice ^system ^idle ^iowait ^irq ^softirq

필드 설명:

  • user: 사용자 모드 실행 시간 (애플리케이션)
  • system: 커널 모드 실행 시간 (시스템 콜)
  • idle: 유휴 시간
  • iowait: I/O 대기 시간

CPU 사용률 계산:

CPU 사용률 = 100 × (1 - idle / 전체 합계)
= 100 × (1 - idle / (user + nice + system + idle + iowait + irq + softirq))

1.1.3 엔지니어 해석 가이드

IOWait가 높으면 (20%+):

진단:
→ 디스크 I/O 병목
→ 네트워크 대기 시간
→ 외부 API 호출 지연

확인 방법:
1. iostat -x 1으로 I/O 대기 확인
2. pidstat -d로 프로세스별 I/O 확인
3. netstat로 네트워크 연결 상태 확인

해결 방법:
1. 디스크를 SSD로 교체
2. DB 쿼리 튜닝
3. 캐시 레이어 추가
4. 비동기 I/O로 변경

System CPU가 높으면 (30%+):

진단:
→ 시스템 콜 과다
→ 컨텍스트 스위칭 빈번
→ 스레드가 너무 많음

확인 방법:
1. pidstat -w로 컨텍스트 스위칭 확인
2. ps -eLf로 스레드 수 확인
3. strace로 시스템 콜 추적

해결 방법:
1. 스레드 풀 크기 조정
2. 락 경쟁 감소
3. 시스템 콜 최적화

1.2 메모리 메트릭

1.2.1 메트릭 정의

메트릭 명상수데이터 원본계산 방법실무 의미주의 기준
메모리 사용률HOST_MEM/proc/meminfo100 × (MemTotal - MemAvailable) / MemTotal실제 사용 중인 메모리 비율 (버퍼+캐시 포함)>85%: 위험
전체 메모리HOST_MEM_TOTAL/proc/meminfoMemTotal / 1024 (KB→MB 변환)물리 메모리 총량-
사용 메모리HOST_MEM_USED/proc/meminfo(MemTotal - MemAvailable) / 1024실제 사용량 (anon + file + slab)-
가용 메모리HOST_MEM_AVAILABLE/proc/meminfoMemAvailable / 1024즉시 할당 가능한 메모리<500MB: 부족 경고

1.2.2 데이터 원본 분석

**/proc/meminfo** 주요 필드:

MemTotal: 16384000 kB (16GB)
MemFree: 2048000 kB (2GB, 실제 free)
MemAvailable: 8192000 kB (8GB, 버퍼+캐시 제외한 가용량)
Buffers: 102400 kB
Cached: 5120000 kB
SwapTotal: 8388608 kB (8GB)
SwapFree: 8388608 kB

필드 설명:

  • MemTotal: 물리 메모리 총량
  • MemFree: 완전히 비어 있는 메모리
  • MemAvailable: 버퍼+캐시를 포함한 가용 메모리 (실제 사용 가능량)
  • Buffers: 블록 디바이스 버퍼
  • Cached: 페이지 캐시

1.2.3 엔지니어 해석 가이드

메모리 부족 판단 기준:

기준 1: MemAvailable < 10% MemTotal
→ OOM Kill 위험

기준 2: SwapUsed > 50% SwapTotal
→ 물리 메모리 부족

기준 3: SwapInRate > 0
→ 스와핑 활발 (성능 저하)

확인 방법:
1. free -m으로 메모리 사용량 확인
2. vmstat 1로 스와핑 활동 확인
3. sar -r 1로 메모리 추이 확인

해결 방법:
1. 물리 메모리 증설
2. 메모리 누수 원인 파악 (heap dump 분석)
3. 캐시 크기 조정 (buffer/cache 감소)
4. 애플리케이션 메모리 사용량 최적화

1.3 스왑 메트릭

1.3.1 메트릭 정의

메트릭 명상수데이터 원본계산 방법실무 의미주의 기준
스왑 사용률HOST_SWAP/proc/meminfo100 × (SwapTotal - SwapFree) / SwapTotal가상 메모리 사용 비율>10%: 물리 메모리 부족
전체 스왑HOST_SWAP_TOTAL/proc/meminfoSwapTotal / 1024 (KB→MB)스왑 파티션 총량-
사용 스왑HOST_SWAP_USED/proc/meminfo(SwapTotal - SwapFree) / 1024스왑 사용량-

1.3.2 엔지니어 해석 가이드

스와핑이 발생하면:

영향:
→ 디스크 I/O 급증 (디스크가 RAM보다 1000배 느림)
→ 응답 시간 급격 저하 (페이지 폴트 대기)
→ 스레드가 차단(BLOCKED) 상태로 대기

진단:
1. vmstat 1로 si/so (swap in/out) 확인
2. iostat -x 1로 디스크 I/O 급증 확인
3. ps aux로 대기 상태 스레드 확인

해결 방법:
1. 물리 메모리 증설 (우선)
2. 메모리 누수 원인 파악 (heap dump 분석)
3. 캐시 크기 조정 (buffer/cache 감소)
4. swappiness 설정 조정 (vm.swappiness=10)

1.4 네트워크 메트릭

1.4.1 메트릭 정의

메트릭 명상수데이터 원본계산 방법실무 의미주의 기준
수신 패킷HOST_NET_IN/proc/net/snmpdelta(inReceives) / delta(time)초당 수신 패킷 수 (pps)-
송신 패킷HOST_NET_OUT/proc/net/snmpdelta(outRequests) / delta(time)초당 송신 패킷 수 (pps)-
수신 바이트HOST_NET_RX_BYTES/proc/net/snmpdelta(inOctets) / delta(time)초당 수신 바이트 (Bps)>80% 대역폭: 병목
송신 바이트HOST_NET_TX_BYTES/proc/net/snmpdelta(outOctets) / delta(time)초당 송신 바이트 (Bps)>80% 대역폭: 병목

1.4.2 데이터 원본 분석

**/proc/net/snmp** 주요 필드:

inReceives: 총 수신 패킷 수
inOctets: 총 수신 바이트 수
outRequests: 총 송신 패킷 수
outOctets: 총 송신 바이트 수

1.4.3 엔지니어 해석 가이드

대역폭 사용률 계산:

1Gbps NIC = 125 MB/s 이론 최대
실제 처리량 = RX_BYTES + TX_BYTES (bytes/sec)
사용률 = 실제 처리량 / 125 MB/s × 100

네트워크 병목 징후:

징후 1: 사용률 > 80%
→ 대역폭 증설 고려

징후 2: RX_Errors > 0
→ 케이블/포트 문제

징후 3: RX_Dropped > 0
→ 수신 버퍼 부족

징후 4: Collisions > 0 (half-duplex)
→ full-duplex로 변경

1.5 디스크 I/O 메트릭

1.5.1 메트릭 정의

메트릭 명상수데이터 원본계산 방법실무 의미주의 기준
디스크 읽기HOST_DISK_READ_BYTES/proc/diskstatsdelta(sector_reads) × 512 / delta(time)초당 디스크 읽기 바이트>100MB/s: HDD 한계
디스크 쓰기HOST_DISK_WRITE_BYTES/proc/diskstatsdelta(sector_writes) × 512 / delta(time)초당 디스크 쓰기 바이트-

1.5.2 데이터 원본 분석

**/proc/diskstats** 포맷:

8 0 sda 12345 67890 987 100 11 12 13 14 15
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
| | | | | | | | | | | +-- ioMs (I/O 소요 시간)
| | | | | | | | | | +----- weighted ioMs
| | | | | | | | | +-------- writes completed
| | | | | | | | +----------- sectors written
| | | | | | | +-------------- reads completed
| | | | | | +----------------- sectors read
| | | | | +------------------- writes merged
| | | | +------------------------- read merged
| | | +------------------------------ device name
| | +---------------------------------- minor number
| +-------------------------------------- major number

1.5.3 엔지니어 해석 가이드

디스크 병목 징후:

징후 1: Avg I/O Wait > 10ms
→ HDD 한계 (SSD로 교체 권장)

징후 2: IOPS > 5000 (HDD)
→ 순차 I/O 최적화 필요

징후 3: Queue Length > 1
→ I/O 대기 발생

2. Java Agent 메트릭 상세

2.1 Service 메트릭

2.1.1 메트릭 정의

메트릭 명상수데이터 원본계산 방법실무 의미주의 기준
TPSWAS_TPSMeterServicecount(delta_time) / delta_time초당 트랜잭션 처리량-
응답 시간WAS_ELAPSED_TIMEMeterServicetotal_elapsed / count평균 응답 시간 (ms)>SLA×1.5: 주의
P90 응답시간WAS_ELAPSED_90PCTResponseTimeHistogram90% 백분위수상위 10% 제외 응답 시간>SLA×2: 위험
서비스 건수WAS_SERVICE_COUNTMeterService누적 처리 건수 (단조 증가)총 처리 서비스 수-
에러율WAS_ERROR_RATEMeterServiceerror_count / total_count × 100에러 응답 비율>1%: 주의
활성 서비스WAS_ACTIVE_SERVICETraceContextManageractiveServiceCount.get()현재 처리 중인 서비스 수>스레드풀 크기: 대기

2.1.2 데이터 원본 분석

MeterService 내부 구조:

private long totalCount = 0; // 단조 증가 카운터
private long totalElapsed = 0; // 단조 증가 누적 시간 (ms)
private long errorCount = 0; // 에러 건수
private final AtomicLong activeCount = new AtomicLong(0);

TPS 계산 (1초 윈도우):

tps = (count(t) - count(t-1000)) / 1000ms

응답 시간 계산:

avg_elapsed = (totalElapsed(t) - totalElapsed(t-1000)) / (count(t) - count(t-1000))

활성 서비스 추적:

startService() → activeCount.incrementAndGet()
endService() → activeCount.decrementAndGet()

2.1.3 P90 응답 시간 상세 설명

P90이란 무엇인가?

P90 = 90번째 백분위수 (90th Percentile)
→ 전체 응답 시간을 오름차순 정렬했을 때 90% 위치에 있는 값
→ "100명 중 90명은 이 시간 안에 응답받았다"를 의미

왜 평균 대신 P90을 쓰는가?

평균의 문제: 아웃라이어(특이값)에 취약
┌────────────────────────────────────────────────────────────┐
│ 데이터: [10ms, 10ms, ..., 10ms, 2000ms] (100개 요청) │
│ │
│ 평균: (10ms × 99 + 2000ms) / 100 = 1019ms │
│ → 1개 느린 요청 때문에 평균이 왜곡됨 │
│ │
│ P50 (중앙값): 10ms │
│ P90: 10ms │
│ P99: 2000ms │
│ │
│ → 대부분 사용자(90%)는 10ms에 응답받았음 │
│ → P90가 실제 사용자 경험을 더 정확히 반영 │
└────────────────────────────────────────────────────────────┘

P90 계산 방법:

ResponseTimeHistogram이 구간별 빈도를 저장:

구간 설정 (예: 0~10ms, 10~20ms, ..., 1000ms~):
┌────────┬────────┬────────┬────────┬────────┐
│ 구간 │ 0~10ms │ 10~20ms│ ... │ 1000+ │
├────────┼────────┼────────┼────────┼────────┤
│ 건수 │ 50 │ 30 │ ... │ 5 │
└────────┴────────┴────────┴────────┴────────┘

누적 빈도 계산:
0~10ms: 50건 → 누적 50건 (50%)
0~20ms: 30건 → 누적 80건 (80%)
...
1000ms+: 5건 → 누적 100건 (100%)

P90 추정:
90% 위치 → 20~30ms 구간에 있음
→ 선형 보간으로 P90 ≈ 25ms

P90 해석 가이드:

P90 < 50ms: 매우 빠름
→ 대부분 요청이 50ms 안에 완료

P90 = 200ms: 적절
→ 90% 사용자는 200ms 경험
→ 상위 10%는 더 느릴 수 있음

P90 > 1000ms: 느림
→ 상위 10% 사용자가 1초 이상 대기
→ 원인 파악 필요 (Slow Query, 외부 API 지연)

P90 vs P99 차이:

P90: 상위 10% 제외 → "대부분" 사용자 경험
P99: 상위 1% 제외 → "꼬리" 사용자 경험

P90과 P99 간격이 크면:
→ 응답 시간 분산이 큼 (불균형)
→ 일부 요청이 매우 느림
→ 캐시 미스나 DB Lock 의심

예시:
P90 = 100ms, P99 = 5000ms
→ 90%는 100ms, 1%는 5초 대기
→ "느린 1%"가 무엇인지 분석 필요

2.1.4 엔지니어 해석 가이드

병목 발생 조건:

조건 1: activeCount ≥ threadPoolSize
→ 요청 대기 (큐잉)

조건 2: avg_elapsed 증가 + activeCount 증가
→ 스레드 고갈 (Stuck)

확인 방법:
1. jstack <pid>으로 스레드 덤프 확인
2. 스레드 상태 분석 (RUNNABLE, BLOCKED, WAITING)
3. ThreadPoolExecutor 상태 확인

해결 방법:
1. 스레드 풀 크기 증설
2. 비동기 처리로 변경
3. 병목 지점 최적화

2.2 Heap 메트릭

2.2.1 메트릭 정의

메트릭 명상수데이터 원본계산 방법실무 의미주의 기준
Heap 사용량JAVA_HEAP_USEDjava.lang:type=MemoryHeapMemoryUsage.used / 1024 / 1024힙 영역 사용량 (MB)-
Heap 전체JAVA_HEAP_TOTALjava.lang:type=Memorymax / 1024 / 1024힙 최대 용량 (MB)-
Heap 사용률JAVA_HEAP_PERCENTjava.lang:type=Memory100 × used / max힙 사용 비율>85%: 위험

2.2.2 데이터 원본 분석

JMX MBean: java.lang:type=Memory:

HeapMemoryUsage:
- init: 초기 크기 (Xms)
- used: 현재 사용량
- committed: JVM이 확보한 크기
- max: 최대 크기 (Xmx)

Heap 영역 구조 (G1 GC 기준):

┌─────────────────────────────────┐
│ Young Generation │
│ ┌─────────┐ ┌─────────┐ │
│ │ Eden │ │Survivor│ │
│ └─────────┘ └─────────┘ │
├─────────────────────────────────┤
│ Old Generation │
│ ┌─────────────────────┐ │
│ │ Humongous │ │
│ └─────────────────────┘ │
└─────────────────────────────────┘

2.2.3 엔지니어 해석 가이드

메모리 누수 징후:

징후 1: Old Gen이 계속 증가
→ 객체 참조 미해제

징후 2: GC 후 사용량이 감소하지 않음
→ 메모리 누수

징후 3: OutOfMemoryError: GC overhead limit exceeded
→ GC 과다

분석 방법:
jmap -heap <pid> # Heap 사용량 확인
jmap -histo:live <pid> # 객체별 사용량 확인
jcmd <pid> GC.heap_info # Heap 영역별 사용량

해결 방법:
1. heap dump 분석 (jmap -dump:live,format=b,file=heap.bin <pid>)
2. MAT (Memory Analyzer Tool)로 누수 원인 파악
3. 객체 참조 해제 코드 수정
4. WeakReference 사용 검토

2.3 GC 메트릭

2.3.1 메트릭 정의

메트릭 명상수데이터 원본계산 방법실무 의미주의 기준
GC 횟수JAVA_GC_COUNTjava.lang:type=GarbageCollectordelta(collectionCount) / 60sec1분당 GC 실행 횟수>60: 과도 GC
GC 시간JAVA_GC_TIMEjava.lang:type=GarbageCollectordelta(collectionTime) / 60sec1분당 GC 총 시간(ms)>5000ms: STW 지연

2.3.2 데이터 원본 분석

JMX MBean: java.lang:type=GarbageCollector,name=G1 Young Generation:

- collectionCount: GC 실행 횟수
- collectionTime: 누적 GC 시간 (ms)

GC 시간/분 계산:

gc_time_per_min = (gc_time(t) - gc_time(t-60000)) / 60000ms × 100

Stop-The-World (STW) 영향:

STW 시간 = GC 시간 중 애플리케이션 스레드가 멈춘 시간
STW 비율 = gc_time_per_min / 60000ms × 100

STW 비율 기준:

< 1%: 양호
1~5%: 주의
> 5%: 위험 (사용자 응답 지연)

2.3.3 엔지니어 해석 가이드

GC 튜닝 가이드라인:

문제 1: Young GC 빈도 ↑
→ Eden 영역 확대

문제 2: Full GC 발생
→ Young/Old 비율 조정

문제 3: STW 시간 길다
→ G1 → ZGC 전환 고려

확인 방법:
jstat -gcutil <pid> 1000 # 1초 간격으로 GC 통계 확인
jcmd <pid> VM.flags # JVM 플래그 확인
jcmd <pid> GC.heap_info # Heap 영역별 사용량

튜닝 예시:
1. Eden 영역 확대: -XX:NewRatio=1 (Young:Old = 1:1)
2. G1 GC 전환: -XX:+UseG1GC
3. ZGC 전환: -XX:+UseZGC (Java 15+)

2.4 Process 메트릭

2.4.1 메트릭 정의

메트릭 명상수데이터 원본계산 방법실무 의미주의 기준
프로세스 CPUJAVA_PROCESS_CPUjava.lang:type=OperatingSystem100 × processCpuTime / uptimeJVM 프로세스 CPU 사용률>80%: CPU 병목
파일 디스크립터JAVA_FD_USAGEjava.lang:type=OperatingSystemopenFileDescriptorCount열린 FD 수>ulimit-100: 부족

2.4.2 데이터 원본 분석

JMX MBean: java.lang:type=OperatingSystem:

- processCpuTime: 프로세스 CPU 시간 (ns)
- uptime: JVM 가동 시간 (ns)
- openFileDescriptorCount: 열린 FD 수
- maxFileDescriptorCount: 최대 FD 한도

프로세스 CPU 계산:

cpu_usage = (cpu_time(t) - cpu_time(t-1000)) / 1000000000 / 1sec

2.4.3 엔지니어 해석 가이드

FD 한도 확인:

ulimit -n # 최대 FD 수 확인
lsof -p <pid> | wc -l # 현재 열린 FD 수

FD 부족 원인:

원인 1: 파일/소켓 닫지 않음 (Resource Leak)
원인 2: 너무 많은 연결 (DB Connection Pool 크기)
원인 3: 로그 파일 핸들 누적

해결 방법:

해결 1: close() 호출 확인 (try-with-resources 사용)
해결 2: Connection Pool 최대 크기 축소
해결 3: ulimit -n 65536 (한도 증설)

3. Interaction 메트릭 상세

3.1 메트릭 정의

메트릭 명상수데이터 원본계산 방법실무 의미주의 기준
API 송신INTR_API_OUTGOINGApicallASMcount(startService() - endService())외부 API 호출 건수-
API 수신INTR_API_INCOMINGHttpTraceHTTP 요청 수신 건수-
DB 호출INTR_DB_CALLTraceSQL0PreparedStatement.execute() 호출 횟수DB 쿼리 실행 건수-
Redis 호출INTR_REDIS_CALLJedisProtocolASMRedis 명령어 실행 횟수Redis 오퍼레이션 건수-
Kafka 호출INTR_KAFKA_CALLKafkaProducerASMKafka Producer 전송 건수Kafka 메시지 전송 건수-
RabbitMQ 호출INTR_RABBITMQ_CALLRabbitPublisherASMRabbitMQ publish 건수메시지 발행 건수-
Elasticsearch 호출INTR_ELASTICSEARCH_CALLRestClientASMES 요청 건수인덱싱/검색 건수-
MongoDB 호출INTR_MONGODB_CALLMongoCommandProtocolASMMongoDB 명령 실행 건수CRUD 오퍼레이션 건수-

3.2 엔지니어 해석 가이드

Interaction 추적 원리:

1. ASM Instrumentation이 외부 라이브러리 호출 감지
2. startService() → 호출 전후 타임스탬프 기록
3. endService() → elapsed = end - start 계산

서비스 메시 (Service Map) 구성:

A Service → B Service: INTR_API_OUTGOING(A) = INTR_API_INCOMING(B)

상관 분석 활용:

분석 1: OUTGOING 증가 + INCOMING 감소
→ 다운스트림 병목

분석 2: DB_CALL 증가 + ELAPSED_TIME 증가
→ Slow Query

분석 3: INTR_API_OUTGOING 높음 + INTR_API_INCOMING 낮음
→ 외부 API 지연

네트워크 계층 구조 추론:

Interaction Counter 기반으로 서비스 의존성 그래프 자동 생성
→ Service Mesh (Istio/Linkerd) 없이도 서비스 토폴로지 가능

4. 메트릭별 연관 분석 가이드

4.1 성능 문제 진단 트리

┌─────────────────────────────────────────────────────────────┐
│ 응답 시간 급증 (P99 > SLA×2) │
└─────────────────────────────────────────────────────────────┘

┌───────────────────┼───────────────────┐
│ │ │
┌───▼────┐ ┌───────▼────────┐ ┌────▼───────────┐
│ CPU │ │ Memory │ │ Network │
│ 병목 │ │ 부족 │ │ 지연 │
└───┬────┘ └───────┬────────┘ └────┬───────────┘
│ │ │
HOST_CPU > 90% HOST_MEM > 85% HOST_NET_* 급증
│ │ │
│ ┌─────▼────────┐ │
│ │ Swap 사용 │ │
│ │ (Paging) │ │
│ └─────┬────────┘ │
│ │ │
┌───▼────┐ ┌───────▼────────┐ ┌────▼───────────┐
│ GC │ │ Heap Leak │ │ DB/Redis │
│ 빈도 │ │ (Old Gen↑) │ │ Slow Query │
└─────────┘ └────────────────┘ └────────────────┘

4.2 메트릭 연관 매트릭스

원인 징후1차 지표2차 확인 지표판단 근거
CPU 병목HOST_CPU > 90%HOST_SYSCPU ↑I/O 대기로 시스템 CPU 증가
메모리 부족HOST_MEM > 85%HOST_SWAP_USED ↑스와핑 시작
메모리 누수JAVA_HEAP_PERCENT ↑OLD_GEN 계속 증가GC 후 감소하지 않음
GC 과도JAVA_GC_COUNT ↑JAVA_GC_TIME ↑STW 비율 > 5%
스레드 고갈WAS_ACTIVE_SERVICE ↑WAS_ELAPSED_TIME ↑ThreadPoolSize 도달
DB 병목INTR_DB_CALL ↑WAS_SQL_TIME ↑DB Connection Pool 고갈
외부 API 지연INTR_API_OUTGOING ↑WAS_APICALL_TIME ↑DOWNSTREAM 서버 응답 지연