부록 A: 메트릭 데이터 수집 및 저장 구조
개요
FlowKat은 3계층 아키텍처로 메트릭 데이터를 수집하고 저장합니다:
- 에이전트 계층: Host Agent, Java Agent에서 데이터 수집
- 전송 계층: V3 TCP(Netty) 프로토콜로 Collector Server로 전송
- 저장 계층: Collector Server에서 파일 DB로 저장
1. 에이전트 계층: 데이터 수집
1.1 @Counter 애노테이션 기반 수집 시스템
수집 관리자가 1초 간격으로 등록된 수집 작업을 반복 실행합니다.
수집된 메트릭은 **데이터 바구니(CounterBasket)**에 오브젝트별·시간별로 분류되어 저장됩니다.
| 분류 기준 | 설명 | 예시 |
|---|---|---|
| 오브젝트 이름 | 모니터링 대상의 고유 이름 | was-prod-01, host-db-01 |
| 시간 타입 | 수집 주기 단위 | 실시간(1초), 5분 집계, 시간 집계 |
같은 오브젝트의 같은 시간 타입 메트릭은 하나의 바구니에 모아서 한 번에 전송합니다. 이를 통해 네트워크 효율을 높이고 데이터 정합성을 유지합니다.
1.2 Host Agent: OS 레벨 메트릭 수집
Host Agent는 운영체제의 시스템 정보를 1초마다 읽어옵니다.
수집 항목 예시:
| 읽어오는 정보 | 계산 방법 | 저장되는 메트릭 |
|---|---|---|
| CPU 유휴(Idle) 비율 | (1 - 유휴 비율) × 100 | CPU 사용률 (%) |
| 메모리 사용 비율 | 운영체제에서 직접 제공 | 메모리 사용률 (%) |
1.3 Java Agent: JVM 및 애플리케이션 메트릭
Java Agent는 JVM 내부의 관리 도구(MBean)를 통해 애플리케이션 통계를 읽습니다.
수집 항목:
| 읽어오는 정보 | 저장되는 메트릭 | 의미 |
|---|---|---|
| 총 처리 시간 | 응답 시간 | 트랜잭션 평균 처리 시간 |
| 처리 건수 | 서비스 건수 | 누적 처리 트랜잭션 수 |
| 초당 처리량 계산 | TPS(초당 트랜잭션 수) | 1초당 처리한 트랜잭션 수 |
| 에러 비율 계산 | 에러율 | 전체 요청 대비 에러 비율 |
| 실행 중 서비스 수 | 액티브 서비스 | 현재 처리 중인 트랜잭션 수 |
1.4 데이터 수집의 신뢰성
왜 1초마다 수집할까요?
FlowKat은 1초 간격으로 메트릭을 수집합니다. 이 간격은 시스템 모니터링에 충분합니다.
과학적 근거:
- 표본화 이론: 1초 간격 샘플링은 대부분의 시스템 변화를 포착할 수 있습니다
- 단조 증가 카운터: 데이터 순서가 바뀌어도 정확도가 보장됩니다
- 포아송 과정: 트래픽 패턴을 통계적으로 모델링할 수 있습니다
상세 증명: 수학적으로 알고 싶으신가요? 부록 C: 수학적 기초를 확인하세요.
2. 트랜잭션 수집: XLOG 추적
2.1 ASM 바이트코드 수정 (Bytecode Instrumentation)
Java Agent는 애플리케이션 코드가 실행되기 전에 바이트코드 수정(Instrumentation) 기술로 추적 로직을 자동으로 주입합니다. 개발자가 소스 코드를 수정할 필요가 없습니다.
동작 원리:
| 단계 | 설명 |
|---|---|
| 1. 패턴 매칭 | 에이전트 설정에 지정된 패턴과 일치하는 클래스/메서드를 찾습니다 |
| 2. 시작 추적 주입 | 메서드 진입 시 "트랜잭션 시작" 코드를 자동 삽입합니다 |
| 3. 종료 추적 주입 | 메서드 반환/예외 시 "트랜잭션 종료" 코드를 자동 삽입합니다 |
| 4. 예외 안전 | 예외가 발생해도 반드시 종료 추적이 실행되도록 보장합니다 |
2.2 HTTP 트랜잭션 추적 시작점
HTTP 요청이 수신되면 자동으로 트랜잭션 추적이 시작됩니다.
추출하는 요청 정보:
| 정보 | 설명 | 활용 |
|---|---|---|
| HTTP 메서드 | GET, POST 등 | 서비스 분류 |
| URL 경로 | 요청 경로 | 서비스 식별 |
| 클라이언트 IP | 요청 발신 IP | 트래픽 분석 |
| 분산 추적 헤더 | 글로벌/로컬 트랜잭션 ID | 서비스 간 호출 추적 |
2.3 SQL 실행 추적
SQL이 실행되면 자동으로 프로파일 스텝이 기록됩니다.
| 단계 | 동작 | 기록 내용 |
|---|---|---|
| 1 | 현재 트랜잭션 컨텍스트 확인 | 추적 중인 트랜잭션인지 확인 |
| 2 | SQL 프로파일 스텝 생성 | SQL 쿼리 텍스트, 시작 시간 기록 |
| 3 | 프로파일 스택에 추가 | 트랜잭션의 실행 경로에 SQL 단계 추가 |
| 4 | SQL 실행 완료 후 | 경과 시간 기록, 에러 여부 확인 |
여기서 기록된 SQL 스텝이 XLOG 프로파일 분석에서 주황색 SQL 스텝으로 표시됩니다. SQL 쿼리 텍스트와 바인드 변수를 상세 팝업에서 확인할 수 있습니다.
2.4 트레이스 컨텍스트 라이프사이클
트랜잭션 추적 컨텍스트는 스레드 전용 저장소에서 관리됩니다. 각 요청은 독립된 스레드에서 처리되므로, 동시에 여러 트랜잭션이 추적되어도 서로 간섭하지 않습니다.
XLOG에 포함되는 정보:
| 항목 | 설명 |
|---|---|
| 글로벌 트랜잭션 ID | 서비스 간 호출을 연결하는 고유 식별자 |
| 트랜잭션 ID | 개별 트랜잭션 고유 식별자 |
| 서비스 해시 | 요청 URL/경로의 빠른 검색용 해시 |
| 응답 시간 | 트랜잭션 시작~종료 경과 시간 (ms) |
| 프로파일 | SQL, 외부 호출, 메서드 등 단계별 실행 기록 |
3. 전송 계층: V3 TCP 데이터 전송
3.1 V3 TCP 전송 과정
FlowKat V3부터 에이전트-수집 서버 간 데이터 전송은 TCP 기반으로 전환되었습니다.
전송 실패 시: 연결 끊김 감지 → 로그 기록 → 자동 재연결 시도
V3 TCP 주요 특성:
- 신뢰성: TCP 연결 기반 전송으로 패킷 손실 없음
- 배치 전송: 최대 60KB 단위로 Pack을 묶어 전송 (
net_tcp_batch_max_bytes) - 자동 재연결: 연결 끊김 시 지수 백오프(1초→30초→300초) 후 재연결
- Keep-Alive: 5초 주기 하트비트로 연결 유지
V2까지 사용되었던 UDP 전송은 V3에서 TCP로 전면 대체되었습니다. UDP의 패킷 손실 허용 설계가 대규모 환경에서 메트릭 유실을 유발하였고, TCP의 신뢰성 있는 배치 전송이 더 적합하다는 판단에 따른 전환입니다.
3.2 Pack 데이터 구조
| Pack 타입 | 설명 | 주요 필드 |
|---|---|---|
Perf카운터 데이터 | 성능 카운터 | objName, time, timeType, data (Map<String, Value>) |
X로그 데이터 | 트랜잭션 로그 | gxid, txid, service, xType, elapsed, profile |
텍스트 데이터 | 에러 로그 | type, hash, text, line |
알림 데이터 | 알림 | level, title, message |
InteractionPerf카운터 데이터 | 서비스간 호출 메트릭 | from, to, counter, value |
4. 저장 계층: Collector Server
4.1 데이터 처리 파이프라인
4.2 Perf카운터 데이터 처리 흐름
성능 카운터 데이터는 시간 타입에 따라 다르게 처리됩니다.
실시간 데이터 처리 흐름:
| 시간 타입 | 저장 위치 | 처리 방식 |
|---|---|---|
| 실시간 (1초) | 파일 DB + 메모리 캐시 | 플러그인 → 파일 → 캐시 → 알림 |
| 5분 이상 집계 | 일일 집계 DB | 날짜(YYYYMMDD) + 시간(HHMM) 기준 저장 |
4.3 캐시 구조
메모리 캐시는 시간 타입별로 다른 보존 시간을 가집니다. 오래된 데이터는 자동으로 만료되어 메모리를 효율적으로 사용합니다.
| 시간 타입 | 캐시 보존 시간 | 설명 |
|---|---|---|
| 실시간 | 10초 | 가장 최근 데이터만 유지 |
| 1분 집계 | 약 1분 | 1분 단위 집계 데이터 |
| 5분 집계 | 약 5분 | 추세 분석용 |
| 10분 집계 | 약 10분 | 중기 추세 분석용 |
| 1시간 집계 | 약 1시간 | 장기 추세 분석용 |
보존 시간이 지나면 데이터는 메모리에서 자동으로 제거됩니다. 이미 파일 DB에 저장되어 있으므로 데이터가 유실되지 않습니다. 성능 조회 페이지에서 과거 데이터를 조회하면 파일 DB에서 읽어옵니다.
5. 파일 DB 저장 구조
5.1 RealtimeCounterDB
저장 경로: \{DB_ROOT\}/\{yyyymmdd\}/counter/
파일 구조:
realtime/
├── header # 메트릭 이름 → ID 매핑
├── data # 실제 메트릭 값 (바이너리)
└── index # (objHash, time, offset) 인덱스
저장 과정:
| 단계 | 동작 | 설명 |
|---|---|---|
| 1 | 데이터 직렬화 | 메트릭 데이터를 바이트로 변환 |
| 2 | 데이터 파일 기록 | 변환된 바이트를 데이터 파일에 순차 기록 |
| 3 | 인덱스 파일 기록 | (오브젝트, 시간, 위치) 인덱스를 기록하여 빠른 검색 지원 |
5.2 DailyCounterDB (5분 집계)
저장 경로: \{DB_ROOT\}/\{yyyymmdd\}/counter/5m*
파일 구조:
5mdata # 5분 단위 집계 데이터
5mindex # (date, key) → offset 인덱스
6. 수집 메트릭 요약
6.1 Host Agent 메트릭 개요
Host Agent는 운영체제 수준의 리소스 사용량을 수집합니다.
CPU 메트릭:
- CPU 사용률: 전체 CPU 활용도 (정상: 0~70%, 주의: 70~90%, 위험: 90%↑)
- 시스템 CPU: 커널 모드 실행 비율 (I/O 대기 포함)
- 사용자 CPU: 애플리케이션 실행 비율
메모리 메트릭:
- 메모리 사용률: 실제 사용 중인 메모리 비율 (정상: 0~70%, 위험: 85%↑)
- 전체 메모리: 물리 메모리 총량
- 가용 메모리: 즉시 할당 가능한 메모리
스왑 메트릭:
- 스왑 사용률: 가상 메모리 사용 비율 (>10%이면 물리 메모리 부족)
네트워크 메트릭:
- 수신/송신 패킷: 초당 패킷 수 (pps)
- 수신/송신 바이트: 초당 바이트 수 (Bps)
디스크 I/O 메트릭:
- 디스크 읽기/쓰기: 초당 디스크 바이트 수
6.2 Java Agent 메트릭 개요
Java Agent는 JVM 및 애플리케이션 성능을 수집합니다.
Service 메트릭:
**TPS (Transactions Per Second)**란?
TPS는 1초당 처리한 트랜잭션 수를 나타냅니다.
TPS = 처리 건수 / 경과 시간 (초)
예시: 1000건을 10초 동안 처리
TPS = 1000 / 10 = 100
왜 중요할까요?
- 시스템 처리량을 직접적으로 나타냅니다
- 성능 병목을 조기에 발견할 수 있습니다
- SLA (서비스 수준 협약) 준수 여부를 판단할 수 있습니다
응답 시간 vs 지연 시간:
- 응답 시간 (Response Time): 요청부터 응답까지 전체 시간 (서버 처리 포함)
- 지연 시간 (Latency): 요청이 처리되기까지 대기 시간 (네트워크 지연)
| 메트릭 | 설명 | 주의 기준 |
|---|---|---|
| TPS | 초당 트랜잭션 처리량 | - |
| 응답 시간 | 평균 응답 시간 (ms) | >SLA×1.5: 주의 |
| P90 응답시간 | 상위 10% 제외 응답 시간 | >SLA×2: 위험 |
| 서비스 건수 | 총 처리 서비스 수 | - |
| 에러율 | 에러 응답 비율 | >1%: 주의 |
| 활성 서비스 | 현재 처리 중인 서비스 수 | >스레드풀 크기: 대기 |
Heap 메트릭:
Heap이란 무엇인가요?
Heap은 JVM이 객체를 저장하는 동적 메모리 영역입니다.
JVM 메모리 구조:
┌─────────────────────────────────┐
│ Heap 영역 │
│ ┌─────────┐ ┌─────────┐ │
│ │ Young │ │ Old │ │
│ │ (Eden) │ │ │ │
│ └─────────┘ └─────────┘ │
└─────────────────────────────────┘
Young: 새로 생성된 객체 (자주 GC)
Old: 장기 생존 객체 (가끔 GC)
왜 모니터링해야 할까요?
- 메모리 누수를 조기에 발견할 수 있습니다
- GC 튜닝의 기준이 됩니다
- OutOfMemoryError를 방지할 수 있습니다
| 메트릭 | 설명 | 주의 기준 |
|---|---|---|
| Heap 사용량 | 힙 영역 사용량 (MB) | - |
| Heap 전체 | 힙 최대 용량 (MB) | - |
| Heap 사용률 | 힙 사용 비율 | >85%: 위험 |
GC 메트릭:
**GC (Garbage Collection)**란?
GC는 더 이상 사용하지 않는 객체를 자동으로 회수하는 JVM의 기능입니다.
GC 동작:
1. 객체 생성 → Young 영역 할당
2. Young 영역 꽉 차면 Young GC 실행
3. 생존 객체 → Old 영역 이동
4. Old 영역 꽉 차면 Full GC 실행
왜 중요할까요?
- GC 실행 중에는 애플리케이션이 멈춥니다 (Stop-The-World)
- 과도한 GC는 응답 시간 저하를 유발합니다
- GC 튜닝으로 성능을 개선할 수 있습니다
| 메트릭 | 설명 | 주의 기준 |
|---|---|---|
| GC 횟수 | 1분당 GC 실행 횟수 | >60: 과도 GC |
| GC 시간 | 1분당 GC 총 시간(ms) | >5000ms: STW 지연 |
Process 메트릭:
| 메트릭 | 설명 | 주의 기준 |
|---|---|---|
| 프로세스 CPU | JVM 프로세스 CPU 사용률 | >80%: CPU 병목 |
| 파일 디스크립터 | 열린 FD 수 | >ulimit-100: 부족 |
6.3 Interaction 메트릭 개요
Interaction 메트릭은 서비스 간 호출 추적합니다.
| 메트릭 | 설명 |
|---|---|
| API 송신 | 외부 API 호출 건수 |
| API 수신 | HTTP 요청 수신 건수 |
| DB 호출 | DB 쿼리 실행 건수 |
| Redis 호출 | Redis 명령어 실행 횟수 |
| Kafka 호출 | Kafka Producer 전송 건수 |
| RabbitMQ 호출 | RabbitMQ publish 건수 |
| Elasticsearch 호출 | ES 요청 건수 |
| MongoDB 호출 | MongoDB 명령 실행 건수 |
상세 분석: 부록 B: 메트릭 상세 분석를 확인하세요.
7. 데이터 갱신 주기 및 타입별 저장
7.1 TimeTypeEnum별 저장 전략
| TimeTypeEnum | 코드 | 저장 주기 | 보존 기간 | 저장 형식 | 사용 목적 |
|---|---|---|---|---|---|
| REALTIME | 1 | 1초 | 10초 (메모리) | RealtimeCounterDB | 실시간 모니터링 |
| ONE_MIN | 2 | 1분 | 1분 3초 (메모리) | DailyCounterDB | 1분 집계 |
| FIVE_MIN | 3 | 5분 | 장기 (파일) | DailyCounterDB | 5분 집계, 추세 분석 |
| TEN_MIN | 4 | 10분 | 장기 (파일) | DailyCounterDB | 10분 집계 |
| HOUR | 5 | 1시간 | 장기 (파일) | DailyCounterDB | 1시간 집계 |
| DAY | 6 | 1일 | 장기 (파일) | DailyCounterDB | 일일 집계 |
7.2 메모리 vs 디스크 저장
| 저장 위치 | 데이터 타입 | 보존 정책 |
|---|---|---|
| 메모리 (CounterCache) | REALTIME | 10초 후 자동 만료 (clearExpiredItems()) |
| 디스크 (RealtimeCounterDB) | REALTIME | 날짜별로 보관, 자동 삭제 정책에 따름 |
| 디스크 (DailyCounterDB) | FIVE_MIN+ | 장기 보관, 압축 저장 가능 |
8. 성능 지표 기술용어
8.1 응답 시간 관련
| 용어 | 정의 | APM 활용 |
|---|---|---|
| Response Time | 요청부터 응답까지 전체 시간 | 서버 처리 성능 측정 |
| Latency | 요청이 처리되기까지 대기 시간 | 네트워크 지연 측정 |
| TPS | Transactions Per Second | 시스템 처리량 측정 |
| QPS | Queries Per Second | DB 처리량 측정 |
8.2 Percentile (백분위수)
| Percentile | 의미 | SLA 설정 기준 |
|---|---|---|
| P50 (Median) | 중앙값 | 평균 사용자 경험 |
| P95 | 상위 5% | 대부분 사용자 경험 |
| P99 | 상위 1% | SLA 목표 설정 시 사용 |
8.3 JVM 전문 용어
| 용어 | 정의 | 엔지니어 점검 포인트 |
|---|---|---|
| Stop-The-World (STW) | GC 실행 중 모든 스레드 멈춤 | STW 시간 길면 사용자 응답 지연 |
| Heap 영역 | Young/Old Gen으로 구분 | Old Gen이 계속 증가하면 메모리 누수 |
9. 실무자를 위한 분석 가이드
9.1 성능 이상 발생 시 확인 순서
1. 응답 시간 확인
→ P99가 급증했는가?
2. 리소스 확인
→ CPU/메모리/디스크/네트워크 중 병목 확인
3. JVM 확인 (Java)
→ Heap 사용량, GC 빈도, 스레드 수 확인
4. 트랜잭션 분석
→ 느린 요청의 경로 추적 (XLOG)
5. SQL 분석
→ Slow Query 확인
6. 외부 연동 확인
→ API 호출 시간 확인
9.2 정상 상태 기준값
| 항목 | 정상 | 주의 | 위험 |
|---|---|---|---|
| CPU 사용률 | 0~70% | 70~90% | 90~100% |
| 메모리 사용률 | 0~70% | 70~85% | 85~100% |
| Swap 사용률 | 0% | 0~10% | 10%↑ |
| Heap 사용률 | 0~70% | 70~85% | 85~100% |
| GC 시간/분 | 0~5% | 5~10% | 10%↑ |
| P99 응답 시간 | < SLA | SLA × 1.5 | SLA × 2↑ |
10. 아키텍처 구성 요소
10.1 에이전트 구성
| 구성 요소 | 역할 | 수집 대상 |
|---|---|---|
| Host Agent 수집 스케줄러 | 1초 간격으로 OS 메트릭 수집 | CPU, 메모리, 디스크, 네트워크 |
| Host Agent 성능 수집기 | 운영체제 리소스 상태 읽기 | 시스템 전반 |
| Java Agent 수집 스케줄러 | 1초 간격으로 JVM 메트릭 수집 | TPS, 응답 시간, Heap |
| Java Agent 서비스 수집기 | 애플리케이션 성능 통계 읽기 | 트랜잭션 처리량 |
| 바이트코드 변환기 | 트랜잭션 추적 코드 자동 주입 | HTTP 요청, SQL 실행 |
| 데이터 전송기 | V3 TCP로 수집 서버에 전송 | 모든 수집 데이터 |
10.2 수집 서버 구성
| 구성 요소 | 역할 | 처리 대상 |
|---|---|---|
| 성능 카운터 처리기 | 실시간/집계 카운터 분류 저장 | CPU, TPS 등 성능 지표 |
| XLOG 처리기 | 트랜잭션 로그 전처리 및 저장 | 트랜잭션 실행 기록 |
| 실시간 저장소 | 실시간 메트릭 파일 DB 저장 | 1초 단위 데이터 |
| 일일 집계 저장소 | 5분 이상 집계 데이터 저장 | 장기 보관 데이터 |
| 메모리 캐시 | 최근 데이터 빠른 조회 지원 | 대시보드 실시간 표시용 |