PLT-NFR-010 분산 추적 구현 가이드
OpenTelemetry 기반 포괄적 분산 추적 시스템
📋 목차
- 개요
- 아키텍처 설계
- 구현 솔루션
- OpenTelemetry Tracing SDK 통합
- Jaeger 백엔드 설정
- 모니터링 시스템
- 구체적 구현 단계
- 배포 가이드
- 운영 절차
- 문제 해결
- 성능 메트릭
- 다음 단계
1. 개요
1.1 PLT-NFR-010 요구사항
시스템은 OpenTelemetry 표준을 준수하는 포괄적인 분산 추적 기능을 제공해야 한다.
비즈니스 임팩트 (DTx 플랫폼)
- 성능 최적화: 요청 흐름 전체 가시성으로 병목 지점 식별
- 장애 대응: 분산 시스템 오류 근본 원인 분석
- 사용자 경험 개선: 엔드투엔드 지연시간 모니터링
- 규제 준수: 의료 기기 소프트웨어 성능 추적 및 감사
- 개발 효율성: 마이크로서비스 간 의존성 이해
1.2 기술적 목표
| 목표 | 측정 지표 | 목표값 |
|---|---|---|
| 트레이싱 커버리지 | 모든 핵심 서비스 계측 | 100% |
| 엔드투엔드 가시성 | 요청 전체 경로 추적 | 100% |
| 지연시간 오버헤드 | 트레이싱으로 인한 성능 영향 | < 5% |
| 트레이스 정확성 | 손실되지 않은 트레이스 비율 | > 99.5% |
| 장애 감지 시간 | 분산 오류 감지까지 시간 | < 30초 |
1.3 구현 범위
| 환경 | 샘플링 비율 | 보관 기간 | Jaeger UI | 비용 최적화 |
|---|---|---|---|---|
| Dev | 10% | 3일 | ❌ | 90% 절감 |
| Stage | 30% | 14일 | ✅ | 70% 절감 |
| Prod | 100% | 90일 | ✅ | 완전 수집 |
1.4 관련 문서
1.5 🚀 빠른 시작 가이드
전체 구현 순서 (요약)
# 1. 기존 Collector 확장 (트레이스 파이프라인 추가)
cd apps/dta-wide-otel-collector
./deploy.sh dev
# 2. 인프라 배포
cd infrastructure/terragrunt/dev/distributed-tracing
terragrunt apply
# 3. 애플리케이션 SDK 통합
cd apps/dta-wide-api
npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-trace-otlp-http
# src/tracing.ts 파일 생성 및 main.ts 수정
# 4. 검증
curl "https://dta-wide-api-dev.run.app/health"
# Cloud Trace 콘솔에서 트레이스 확인
⚠️ 중요 포인트
- OpenTelemetry Collector는 1개만 사용 (기존 메트릭 Collector 확장)
- 최소한의 SDK 설정으로 시작 (복잡한 예시 코드 X)
- Dev → Stage → Prod 순서로 단계적 배포
2. 아키텍처 설계
2.1 분산 추적 원칙
CNCF OpenTelemetry 표준 준수
- Trace: 요청의 전체 생명주기
- Span: 개별 작업 단위
- Context Propagation: 서비스 간 추적 컨텍스트 전달
- Semantic Conventions: 일관된 스팬 속성 및 이름
의료 시스템 특화 고려사항
// 의료 데이터 추적 시 중요 속성들
interface MedicalTraceAttributes {
patient_id?: string; // 환자 식별자 (마스킹 필요)
session_id: string; // 세션 식별자
operation_type: 'read' | 'write' | 'delete'; // 데이터 작업 유형
data_sensitivity: 'low' | 'medium' | 'high'; // 데이터 민감도
compliance_context: 'GDPR' | 'HIPAA' | 'DiGA'; // 규제 컨텍스트
audit_required: boolean; // 감사 필요 여부
}
2.2 시스템 아키텍처
2.3 트레이스 데이터 플로우
2.3.1 요청 추적 흐름
사용자 요청
↓ (HTTP/gRPC)
dta-wide-api (Root Span 생성)
↓ (Service Call)
dta-wide-mcp (Child Span 생성)
↓ (Database Query)
PostgreSQL (Database Span)
↓ (Cache Lookup)
Redis (Cache Span)
↓ (OTLP 프로토콜)
OpenTelemetry Collector
↓ (분산 저장)
Cloud Trace + Jaeger + BigQuery
2.3.2 컨텍스트 전파
// HTTP 헤더를 통한 트레이스 컨텍스트 전파
const traceHeaders = {
'traceparent': '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
'tracestate': 'vendor1=value1,vendor2=value2'
};
// 서비스 간 호출 시 자동 컨텍스트 전파
await fetch('https://dta-wide-mcp.run.app/api/analysis', {
headers: {
...traceHeaders,
'Content-Type': 'application/json'
}
});
2.4 핵심 구성 요소
OpenTelemetry Collector 설정
# otel-collector-tracing-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 1s
send_batch_size: 1024
# 환경별 샘플링
probabilistic_sampler:
sampling_percentage: ${TRACE_SAMPLING_RATE}
# 민감 데이터 마스킹
attributes:
actions:
- key: "user.id"
action: hash # 사용자 ID 해시화
- key: "patient.id"
action: delete # 환자 ID 완전 제거
- key: "password"
action: delete
# 트레이스 필터링
filter:
traces:
span:
- 'attributes["http.route"] == "/health"' # 헬스체크 제외
- 'attributes["grpc.method"] == "Health/Check"'
exporters:
# Google Cloud Trace
googlecloud:
project: ${PROJECT_ID}
# Jaeger (선택적)
jaeger:
endpoint: ${JAEGER_ENDPOINT}
tls:
insecure: false
# BigQuery (장기 분석)
googlepubsub:
project: ${PROJECT_ID}
topic: otel-traces-${ENVIRONMENT}
service:
pipelines:
traces:
receivers: [otlp]
processors: [attributes, filter, probabilistic_sampler, batch]
exporters: [googlecloud, jaeger, googlepubsub]
3. 구현 솔루션
3.1 Terraform 모듈 활용
기본 배포 (Dev 환경)
# terragrunt/dev/distributed-tracing/terragrunt.hcl
terraform {
source = "../../../../infrastructure/terraform/modules/distributed-tracing"
}
include "root" {
path = find_in_parent_folders()
}
inputs = {
project_id = "dta-cloud-de-dev"
environment = "dev"
region = "europe-west3"
# Dev 환경 비용 최적화 설정
custom_tracing_config = {
sampling_rate_override = 0.1 # 10% 샘플링
retention_days_override = 3 # 3일 보관
enable_detailed_tracing_override = false # 기본 트레이싱만
enable_jaeger_backend_override = false # Jaeger 비활성화
enable_cost_optimization = true # 비용 최적화 활성화
}
# 트레이스 내보내기 대상
trace_exporters = {
cloud_trace = true # Google Cloud Trace 활성화
jaeger = false # Jaeger 비활성화 (비용 절감)
bigquery = true # BigQuery 장기 저장
stdout = false # 디버그 출력 비활성화
}
# 샘플링 전략
sampling_config = {
default_rate = 0.1 # 10% 기본 샘플링
error_rate = 0.5 # 50% 오류 트레이스 샘플링
slow_request_rate = 0.3 # 30% 느린 요청 샘플링
slow_threshold_ms = 2000 # 2초 이상을 느린 요청으로 분류
}
notification_channels = [
dependency.monitoring.outputs.notification_channel_email_id,
dependency.monitoring.outputs.notification_channel_slack_id
]
}
dependency "monitoring" {
config_path = "../monitoring"
}
3.2 환경별 구성
| 설정 | Dev | Stage | Prod |
|---|---|---|---|
| 샘플링 비율 | 10% | 30% | 100% |
| 보관 기간 | 3일 | 14일 | 90일 |
| Jaeger UI | 비활성화 | 활성화 | 활성화 |
| 상세 트레이싱 | 비활성화 | 활성화 | 활성화 |
| 크로스 리전 백업 | 비활성화 | 비활성화 | 활성화 |
| 알림 임계값 | 관대 | 보통 | 엄격 |
3.3 비용 최적화 전략
환경별 예상 비용
# 비용 계산기 실행
./scripts/tracing-cost-calculator.sh
# 출력 예시:
# ==========================================
# 분산 추적 시스템 비용 분석
# ==========================================
#
# Dev 환경: $15/월 (90% 최적화)
# Stage 환경: $45/월 (70% 최적화)
# Prod 환경: $200/월 (완전 수집)
#
# 총 예상 비용: $260/월
4. OpenTelemetry Tracing SDK 통합
4.1 NestJS 애플리케이션 통합
OpenTelemetry SDK 설정
// src/tracing.ts
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
// 환경별 설정
const isProduction = process.env.NODE_ENV === 'production';
const environment = process.env.DEPLOY_ENV || 'dev';
// 트레이스 Exporter 설정
const traceExporter = new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||
`https://otel-collector-${environment}.europe-west3.run.app/v1/traces`,
headers: {
'Content-Type': 'application/json',
}
});
// OpenTelemetry SDK 초기화
const sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'dta-wide-api',
[SemanticResourceAttributes.SERVICE_VERSION]: process.env.SERVICE_VERSION || '1.0.0',
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: environment,
[SemanticResourceAttributes.SERVICE_INSTANCE_ID]: process.env.HOSTNAME || 'local',
}),
instrumentations: [
getNodeAutoInstrumentations({
// HTTP 요청 자동 계측
'@opentelemetry/instrumentation-http': {
enabled: true,
ignoreIncomingRequestHook: (req) => {
// 헬스체크 및 메트릭 엔드포인트 제외
return req.url?.includes('/health') ||
req.url?.includes('/metrics') ||
req.url?.includes('/favicon.ico');
},
// 응답 후크로 추가 속성 설정
responseHook: (span, response) => {
span.setAttributes({
'http.response.size': response.getHeader('content-length') || 0,
'http.response.type': response.getHeader('content-type') || 'unknown'
});
}
},
// Express 프레임워크 자동 계측
'@opentelemetry/instrumentation-express': {
enabled: true
},
// 데이터베이스 자동 계측
'@opentelemetry/instrumentation-pg': {
enabled: true,
// SQL 쿼리 매개변수 마스킹
enhancedDatabaseReporting: false // 민감한 데이터 보호
},
// Redis 자동 계측
'@opentelemetry/instrumentation-redis': {
enabled: true,
// Redis 명령어 매개변수 제한
dbStatementSerializer: (cmdName, cmdArgs) => {
// 민감한 데이터가 포함될 수 있는 값들 마스킹
return cmdName;
}
},
// gRPC 자동 계측 (마이크로서비스 간 통신)
'@opentelemetry/instrumentation-grpc': {
enabled: true
},
// 불필요한 계측 비활성화 (성능 및 비용 최적화)
'@opentelemetry/instrumentation-fs': {
enabled: false
},
'@opentelemetry/instrumentation-dns': {
enabled: false
}
})
],
traceExporter,
});
// SDK 시작
export function initializeTracing(): void {
sdk.start();
console.log('✅ OpenTelemetry Tracing initialized successfully');
}
// 애플리케이션 종료 시 정리
process.on('SIGTERM', () => {
sdk.shutdown()
.then(() => console.log('OpenTelemetry Tracing terminated'))
.catch((error) => console.log('Error terminating OpenTelemetry Tracing', error))
.finally(() => process.exit(0));
});
메인 애플리케이션 통합
// src/main.ts
import { initializeTracing } from './tracing';
// ⚠️ 중요: OpenTelemetry를 가장 먼저 초기화해야 함
initializeTracing();
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app/app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 기존 부트스트랩 코드...
await app.listen(port);
console.log(`🚀 Application is running on: http://localhost:${port}/v1`);
}
bootstrap();
4.2 커스텀 트레이싱 구현
비즈니스 로직 트레이싱
// src/tracing/business-tracing.service.ts
import { Injectable } from '@nestjs/common';
import { trace, context, SpanStatusCode, SpanKind } from '@opentelemetry/api';
@Injectable()
export class BusinessTracingService {
private readonly tracer = trace.getTracer('dta-wide-business-logic', '1.0.0');
// 수면 로그 분석 트레이싱
async traceSleepAnalysis<T>(
userId: string,
operation: () => Promise<T>
): Promise<T> {
return await this.tracer.startActiveSpan(
'sleep.analysis',
{
kind: SpanKind.INTERNAL,
attributes: {
'user.id': this.hashUserId(userId),
'operation.type': 'sleep_analysis',
'service.component': 'sleep-service'
}
},
async (span) => {
try {
const result = await operation();
span.setAttributes({
'analysis.success': true,
'analysis.result_type': typeof result
});
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
span.recordException(error);
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message
});
throw error;
} finally {
span.end();
}
}
);
}
// 설문 응답 처리 트레이싱
async traceQuestionnaireProcessing<T>(
userId: string,
questionnaireType: string,
operation: () => Promise<T>
): Promise<T> {
return await this.tracer.startActiveSpan(
'questionnaire.processing',
{
kind: SpanKind.INTERNAL,
attributes: {
'user.id': this.hashUserId(userId),
'questionnaire.type': questionnaireType,
'operation.type': 'questionnaire_processing',
'compliance.context': 'medical_assessment'
}
},
async (span) => {
try {
const startTime = Date.now();
const result = await operation();
const processingTime = Date.now() - startTime;
span.setAttributes({
'questionnaire.processing_time_ms': processingTime,
'questionnaire.success': true,
'result.status': 'completed'
});
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
span.recordException(error);
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message
});
// 오류 트레이스에 추가 컨텍스트
span.setAttributes({
'error.type': error.constructor.name,
'error.recoverable': this.isRecoverableError(error)
});
throw error;
} finally {
span.end();
}
}
);
}
// 외부 서비스 호출 트레이싱
async traceExternalServiceCall<T>(
serviceName: string,
operation: string,
call: () => Promise<T>
): Promise<T> {
return await this.tracer.startActiveSpan(
`external.${serviceName}.${operation}`,
{
kind: SpanKind.CLIENT,
attributes: {
'service.name': serviceName,
'operation.name': operation,
'call.type': 'external_service'
}
},
async (span) => {
try {
const result = await call();
span.setAttributes({
'call.success': true,
'response.type': typeof result
});
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
span.recordException(error);
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message
});
throw error;
} finally {
span.end();
}
}
);
}
// 사용자 ID 해시화 (개인정보 보호)
private hashUserId(userId: string): string {
// 실제 구현에서는 적절한 해시 함수 사용
return Buffer.from(userId).toString('base64').substring(0, 8);
}
// 복구 가능한 오류 판단
private isRecoverableError(error: any): boolean {
const recoverableErrors = [
'TimeoutError',
'ConnectionError',
'RateLimitError'
];
return recoverableErrors.includes(error.constructor.name);
}
}
트레이싱 데코레이터 구현
// src/decorators/trace.decorator.ts
import { trace, context, SpanStatusCode, SpanKind } from '@opentelemetry/api';
interface TraceOptions {
operationName?: string;
spanKind?: SpanKind;
attributes?: Record<string, string | number | boolean>;
recordArguments?: boolean;
recordResult?: boolean;
}
export function Trace(options: TraceOptions = {}) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
const tracer = trace.getTracer('dta-wide-custom-tracing', '1.0.0');
descriptor.value = async function (...args: any[]) {
const operationName = options.operationName ||
`${target.constructor.name}.${propertyKey}`;
return await tracer.startActiveSpan(
operationName,
{
kind: options.spanKind || SpanKind.INTERNAL,
attributes: {
'method.class': target.constructor.name,
'method.name': propertyKey,
'method.args_count': args.length,
...options.attributes
}
},
async (span) => {
try {
// 메서드 인자 기록 (민감하지 않은 경우만)
if (options.recordArguments) {
span.setAttributes({
'method.arguments': JSON.stringify(args.slice(0, 3)) // 처음 3개만
});
}
const result = await originalMethod.apply(this, args);
// 결과 기록
if (options.recordResult && result !== undefined) {
span.setAttributes({
'method.result_type': typeof result,
'method.success': true
});
}
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
span.recordException(error);
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message
});
span.setAttributes({
'error.type': error.constructor.name,
'method.success': false
});
throw error;
} finally {
span.end();
}
}
);
};
return descriptor;
};
}
// 사용 예시
@Injectable()
export class SleepLogService {
@Trace({
operationName: 'sleep.log.creation',
spanKind: SpanKind.INTERNAL,
attributes: {
'service.component': 'sleep-service',
'operation.type': 'create'
},
recordArguments: false, // 민감한 데이터 보호
recordResult: true
})
async createSleepLog(
userId: string,
sleepData: CreateSleepLogDto
): Promise<SleepLogResponseDto> {
// 비즈니스 로직 구현
return result;
}
@Trace({
operationName: 'sleep.analysis.pattern',
attributes: {
'analysis.type': 'pattern_recognition'
}
})
async analyzeSleepPattern(userId: string): Promise<AnalysisResult> {
// 패턴 분석 로직
return analysis;
}
}
5. Jaeger 백엔드 설정
5.1 Jaeger UI 접근 및 활용
Jaeger 대시보드 기능
- 트레이스 검색: 서비스, 작업, 태그별 트레이스 검색
- 트레이스 타임라인: 요청 전체 흐름 시각화
- 서비스 의존성: 마이크로서비스 간 의존 관계 맵
- 성능 분석: 지연시간 히스토그램 및 추이
주요 사용 사례
# 1. 특정 사용자의 트레이스 검색
Tags: user.id="user_hash_12345" service="dta-wide-api"
# 2. 오류가 발생한 트레이스 검색
Tags: error=true http.status_code=500
# 3. 느린 요청 분석
Operation: "POST /api/sleep-logs" Min Duration: 2s
# 4. 특정 기간 성능 분석
Time Range: Last 1 hour, Service: dta-wide-mcp
5.2 Jaeger 환경별 설정
Stage 환경 (베타 테스트용)
# jaeger-stage-config.yaml
query:
max-traces: 5000
default-lookback: 24h
collector:
queue-size: 2000
num-workers: 50
storage:
type: memory
memory:
max-traces: 10000
ui:
basePath: /jaeger
staticAssets: /static
Prod 환경 (완전한 기능)
# jaeger-prod-config.yaml
query:
max-traces: 20000
default-lookback: 168h # 7일
collector:
queue-size: 10000
num-workers: 100
storage:
type: elasticsearch # 영속적 저장
elasticsearch:
server-urls: https://elasticsearch.internal:9200
ui:
dependencies:
menuEnabled: true
search:
maxLookback: 720h # 30일
5.3 Jaeger 고급 기능
서비스 성능 맵
// Jaeger에서 자동 생성되는 서비스 의존성 맵 예시
const serviceDependencies = {
'dta-wide-api': {
dependencies: [
'dta-wide-mcp',
'postgresql',
'redis'
],
calls_per_minute: 150,
error_rate: 0.02,
avg_latency_ms: 45
},
'dta-wide-mcp': {
dependencies: [
'external-ml-service',
'postgresql'
],
calls_per_minute: 80,
error_rate: 0.01,
avg_latency_ms: 120
}
};
6. 모니터링 시스템
6.1 SLI/SLO 정의
분산 추적 SLI
-- 트레이스 완성도 SLI (BigQuery)
SELECT
TIMESTAMP_TRUNC(timestamp, HOUR) as time_window,
COUNT(*) as total_spans,
COUNT(DISTINCT trace_id) as total_traces,
-- 완전한 트레이스 비율 (Root span 포함)
COUNTIF(parent_span_id IS NULL) as root_spans,
SAFE_DIVIDE(
COUNTIF(parent_span_id IS NULL),
COUNT(DISTINCT trace_id)
) * 100 as trace_completeness_percent
FROM `{PROJECT_ID}.traces.otel_traces`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR)
AND environment = 'prod'
GROUP BY time_window
ORDER BY time_window DESC;
엔드투엔드 지연시간 SLI
-- 서비스별 P95 지연시간 SLI
WITH trace_latencies AS (
SELECT
trace_id,
service_name,
MIN(timestamp) as trace_start,
MAX(timestamp) as trace_end,
TIMESTAMP_DIFF(MAX(timestamp), MIN(timestamp), MILLISECOND) as total_latency_ms
FROM `{PROJECT_ID}.traces.otel_traces`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)
AND environment = 'prod'
AND parent_span_id IS NULL -- Root spans only
GROUP BY trace_id, service_name
)
SELECT
service_name,
PERCENTILE_CONT(total_latency_ms, 0.95) OVER (PARTITION BY service_name) as p95_latency_ms,
PERCENTILE_CONT(total_latency_ms, 0.50) OVER (PARTITION BY service_name) as p50_latency_ms,
COUNT(*) as trace_count
FROM trace_latencies
GROUP BY service_name, total_latency_ms
ORDER BY p95_latency_ms DESC;
6.2 알림 정책
트레이스 수집 장애 알림
# Cloud Monitoring Alert Policy
displayName: "분산 추적 수집 중단 (환경: {ENVIRONMENT})"
documentation:
content: |
분산 추적 시스템에서 트레이스 수집이 중단되었습니다.
트러블슈팅 가이드:
1. OpenTelemetry Collector 상태 확인
2. 애플리케이션 SDK 설정 확인
3. Google Cloud Trace 서비스 상태 확인
4. 네트워크 연결 및 방화벽 설정 확인
에스컬레이션: 10분 내 해결되지 않으면 개발팀 호출
conditions:
- displayName: "트레이스 수집률 급락"
conditionThreshold:
filter: 'resource.type="cloud_trace"'
comparison: COMPARISON_LESS_THAN
thresholdValue: 100 # 시간당 100개 미만 시 알림
duration: "300s" # 5분 지속
aggregations:
- alignmentPeriod: "60s"
perSeriesAligner: ALIGN_RATE
crossSeriesReducer: REDUCE_SUM
alertStrategy:
autoClose: "3600s" # 1시간 후 자동 닫힘
높은 에러율 알림
displayName: "분산 추적 높은 오류율 (환경: {ENVIRONMENT})"
conditions:
- displayName: "트레이스 오류율 임계값 초과"
conditionThreshold:
filter: 'metric.type="custom.googleapis.com/traces/error_rate"'
comparison: COMPARISON_GREATER_THAN
thresholdValue: 0.05 # 5% 오류율 초과
duration: "180s"
- displayName: "P95 지연시간 임계값 초과"
conditionThreshold:
filter: 'metric.type="custom.googleapis.com/traces/latency_p95"'
comparison: COMPARISON_GREATER_THAN
thresholdValue: 2000 # P95 2초 초과
duration: "300s"
6.3 대시보드 구성
분산 추적 개요 대시보드
{
"displayName": "분산 추적 시스템 개요 ({ENVIRONMENT})",
"mosaicLayout": {
"tiles": [
{
"width": 6,
"height": 4,
"widget": {
"title": "트레이스 수집률 (시간당)",
"xyChart": {
"dataSets": [
{
"timeSeriesQuery": {
"timeSeriesFilter": {
"filter": "resource.type=\"cloud_trace\"",
"aggregation": {
"alignmentPeriod": "3600s",
"perSeriesAligner": "ALIGN_RATE",
"crossSeriesReducer": "REDUCE_SUM"
}
}
},
"plotType": "LINE"
}
]
}
}
},
{
"width": 6,
"height": 4,
"xPos": 6,
"widget": {
"title": "서비스별 평균 지연시간",
"xyChart": {
"dataSets": [
{
"timeSeriesQuery": {
"timeSeriesFilter": {
"filter": "metric.type=\"custom.googleapis.com/traces/duration\"",
"aggregation": {
"alignmentPeriod": "300s",
"perSeriesAligner": "ALIGN_MEAN",
"crossSeriesReducer": "REDUCE_MEAN",
"groupByFields": ["resource.labels.service_name"]
}
}
}
}
]
}
}
},
{
"width": 12,
"height": 4,
"yPos": 4,
"widget": {
"title": "오류율 추이",
"xyChart": {
"dataSets": [
{
"timeSeriesQuery": {
"timeSeriesFilter": {
"filter": "metric.type=\"custom.googleapis.com/traces/error_rate\"",
"aggregation": {
"alignmentPeriod": "300s",
"perSeriesAligner": "ALIGN_RATE",
"crossSeriesReducer": "REDUCE_MEAN"
}
}
}
}
],
"thresholds": [
{
"value": 5.0,
"color": "RED",
"direction": "ABOVE",
"label": "Error Rate SLO (5%)"
}
]
}
}
}
]
}
}
7. 구체적 구현 단계
7.1 개요: 분산 추적 시스템 구축 로드맵
OpenTelemetry 기반 분산 추적 시스템을 구축하기 위한 6단계 실행 계획입니다.
분산 추적의 핵심 개념
📊 Trace: 사용자 요청의 전체 여정
- Root Span: 요청의 시작점 (예: HTTP 요청)
- Child Span: 하위 작업들 (DB 쿼리, 외부 API 호출)
- Context Propagation: 서비스 간 추적 정보 전달
🎯 중요: 분산 추적은 서비스 간 의존성과 성능 병목을 시각화합니다!
- Cloud Trace: Google의 관리형 분산 추적 서비스
- Jaeger: 오픈소스 분산 추적 UI 및 분석 도구
- BigQuery: 장기 트레이스 분석 및 패턴 발견
7.2 단계 1: 기존 OpenTelemetry Collector 확장
⚠️ 중요: Collector 통합 사용
기존 메트릭 수집용 Collector(apps/dta-wide-otel-collector)에 트레이스 파이프라인을 추가합니다.
별도의 Collector를 만들지 마세요!
1.1 Collector 설정 확인
기존 설정 파일이 이미 업데이트되었는지 확인:
# 트레이스 파이프라인이 있는지 확인
grep -A 5 "traces:" apps/dta-wide-otel-collector/otel-collector-config.yaml
1.2 Collector 재배포 (트레이스 지원)
# 기존 Collector에 트레이스 기능 추가 배포
cd apps/dta-wide-otel-collector
./deploy.sh dev
# 배포 상태 확인
curl "https://otel-collector-dev.europe-west3.run.app/health"
7.3 단계 2: 인프라 설정 및 배포
2.1 Terraform 모듈 배포
# Dev 환경 분산 추적 인프라 배포
cd infrastructure/terragrunt/dev/distributed-tracing
terragrunt init
terragrunt plan
terragrunt apply
2.2 배포 검증
# BigQuery traces 데이터셋 생성 확인
bq ls --project_id=dta-cloud-de-dev traces
# Cloud Trace 서비스 활성화 확인
gcloud services list --filter="name:cloudtrace.googleapis.com" --enabled
# Pub/Sub otel-traces 토픽 생성 확인
gcloud pubsub topics list --filter="name:otel-traces-dev"
7.4 단계 3: 애플리케이션 OpenTelemetry SDK 통합
3.1 패키지 설치
cd apps/dta-wide-api
# 필수 OpenTelemetry 패키지만 설치
npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-trace-otlp-http
3.2 최소 트레이싱 설정
src/tracing.ts 생성:
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
const environment = process.env.DEPLOY_ENV || 'dev';
const traceExporter = new OTLPTraceExporter({
url: `https://otel-collector-${environment}.europe-west3.run.app/v1/traces`,
});
const sdk = new NodeSDK({
traceExporter,
instrumentations: [getNodeAutoInstrumentations()],
});
export function initializeTracing(): void {
sdk.start();
console.log('✅ OpenTelemetry Tracing initialized');
}
src/main.ts 수정:
// ⚠️ 첫 줄에 추가
import { initializeTracing } from './tracing';
initializeTracing();
// 기존 imports
import { NestFactory } from '@nestjs/core';
// ...
3.3 환경 변수 설정
# .env 또는 Cloud Run 환경 변수
DEPLOY_ENV=dev
OTEL_SERVICE_NAME=dta-wide-api
7.5 단계 4: 트레이스 검증
4.1 기본 트레이스 확인
# API 엔드포인트 몇 번 호출
API_URL="https://dta-wide-api-dev.europe-west3.run.app"
curl "$API_URL/health"
curl "$API_URL/api/users/me"
# Cloud Trace에서 확인
echo "Cloud Trace: https://console.cloud.google.com/traces/list?project=dta-cloud-de-dev"
4.2 BigQuery 트레이스 데이터 확인
# 5분 후 BigQuery에서 데이터 확인
bq query --use_legacy_sql=false --project_id=dta-cloud-de-dev '
SELECT service_name, COUNT(*) as span_count
FROM `dta-cloud-de-dev.traces.otel_traces`
WHERE timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 10 MINUTE)
GROUP BY service_name'
7.6 단계 5: Jaeger UI 활성화 (Stage/Prod만)
⚠️ 중요: Jaeger UI는 별도 설치 불필요!
Terraform 모듈에 이미 포함되어 있으며, 환경 설정으로 켜고 끕니다.
5.1 환경별 Jaeger UI 상태
| 환경 | Jaeger UI | 이유 |
|---|---|---|
| Dev | ❌ 비활성화 | 비용 절감 |
| Stage | ✅ 활성화 | 개발팀 분석 |
| Prod | ✅ 활성화 | 완전 관찰가능성 |
5.2 Stage 환경 배포 (Jaeger UI 자동 포함)
# Stage 환경 배포 (Jaeger UI 자동 활성화됨)
cd infrastructure/terragrunt/stage/distributed-tracing
terragrunt apply
# Jaeger UI URL 확인
terragrunt output jaeger_service_url
# 출력 예시: https://jaeger-stage-k7qxagdfka-ew.a.run.app
5.3 Dev 환경에서 Jaeger UI 활성화 (테스트용)
만약 Dev에서도 Jaeger UI가 필요하면:
# infrastructure/terragrunt/dev/distributed-tracing/terragrunt.hcl
custom_tracing_config = {
enable_jaeger_backend_override = true # 👈 true로 변경
}
5.4 검증 완료 ✅
| 항목 | 확인 방법 | 상태 |
|---|---|---|
| 트레이스 수집 | Cloud Trace 콘솔 | ✅ |
| 데이터 저장 | BigQuery 쿼리 | ✅ |
| 애플리케이션 | 로그에서 "Tracing initialized" | ✅ |
8. 배포 가이드
8.1 사전 요구사항
필수 권한
# 분산 추적에 필요한 IAM 역할
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:terraform@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/cloudtrace.agent"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:terraform@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/run.admin"
API 활성화
gcloud services enable \
cloudtrace.googleapis.com \
bigquery.googleapis.com \
pubsub.googleapis.com \
run.googleapis.com \
--project=$PROJECT_ID
8.2 환경별 배포 전략
Dev 환경 (최소 비용)
- 10% 샘플링으로 트레이스 볼륨 최소화
- 3일 보관으로 스토리지 비용 절감
- Jaeger 비활성화로 Cloud Run 비용 제거
Stage 환경 (실전 테스트)
- 30% 샘플링으로 충분한 데이터 확보
- Jaeger UI로 개발팀이 트레이스 분석
- 14일 보관으로 버그 추적 가능
Prod 환경 (완전한 관찰가능성)
- 100% 샘플링으로 모든 요청 추적
- 고성능 Jaeger로 실시간 분석
- 90일 보관으로 규제 요구사항 충족
9. 운영 절차
9.1 일일 체크리스트
분산 추적 상태 점검
#!/bin/bash
# scripts/daily-tracing-check.sh
echo "=== 분산 추적 시스템 일일 점검 ==="
echo "점검 시간: $(date)"
echo
# 1. 트레이스 수집률 확인
echo "📊 지난 24시간 트레이스 수집 현황"
bq query --use_legacy_sql=false --format=table '
SELECT
environment,
COUNT(DISTINCT trace_id) as trace_count,
COUNT(*) as span_count,
COUNT(DISTINCT service_name) as service_count,
MAX(timestamp) as latest_trace
FROM `'$PROJECT_ID'.traces.otel_traces`
WHERE timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR)
GROUP BY environment
ORDER BY environment'
echo
# 2. 오류 트레이스 확인
echo "🚨 오류 트레이스 요약"
bq query --use_legacy_sql=false --format=table '
SELECT
service_name,
COUNT(*) as error_span_count,
COUNT(DISTINCT trace_id) as error_trace_count,
ARRAY_AGG(error_message LIMIT 3) as sample_errors
FROM `'$PROJECT_ID'.traces.otel_traces`
WHERE timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR)
AND status_code = "ERROR"
GROUP BY service_name
ORDER BY error_span_count DESC'
echo
# 3. 성능 이상 감지
echo "⚡ 성능 이상 감지 (P95 > 2초)"
bq query --use_legacy_sql=false --format=table '
WITH service_latencies AS (
SELECT
service_name,
operation_name,
PERCENTILE_CONT(duration_ms, 0.95) OVER (PARTITION BY service_name, operation_name) as p95_ms
FROM `'$PROJECT_ID'.traces.otel_traces`
WHERE timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)
)
SELECT DISTINCT
service_name,
operation_name,
p95_ms
FROM service_latencies
WHERE p95_ms > 2000
ORDER BY p95_ms DESC'
echo "✅ 일일 점검 완료"
9.2 주간 성능 분석
트레이스 패턴 분석
-- weekly-trace-analysis.sql
-- 주간 분산 추적 성능 분석
-- 1. 서비스별 성능 추이
WITH daily_performance AS (
SELECT
DATE(timestamp) as date,
service_name,
COUNT(DISTINCT trace_id) as daily_traces,
AVG(duration_ms) as avg_duration_ms,
PERCENTILE_CONT(duration_ms, 0.95) OVER (PARTITION BY DATE(timestamp), service_name) as p95_duration_ms,
COUNTIF(status_code = 'ERROR') as error_count,
SAFE_DIVIDE(COUNTIF(status_code = 'ERROR'), COUNT(*)) as error_rate
FROM `{PROJECT_ID}.traces.otel_traces`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
GROUP BY DATE(timestamp), service_name
)
SELECT
date,
service_name,
daily_traces,
ROUND(avg_duration_ms, 2) as avg_duration_ms,
ROUND(p95_duration_ms, 2) as p95_duration_ms,
error_count,
ROUND(error_rate * 100, 2) as error_rate_percent
FROM daily_performance
ORDER BY date DESC, service_name;
-- 2. 느린 트레이스 TOP 10
SELECT
trace_id,
service_name,
operation_name,
duration_ms,
timestamp,
error_message
FROM `{PROJECT_ID}.traces.otel_traces`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
AND parent_span_id IS NULL -- Root spans only
ORDER BY duration_ms DESC
LIMIT 10;
-- 3. 서비스 의존성 분석
WITH service_calls AS (
SELECT
a.service_name as caller_service,
b.service_name as called_service,
COUNT(*) as call_count,
AVG(b.duration_ms) as avg_call_duration_ms
FROM `{PROJECT_ID}.traces.otel_traces` a
JOIN `{PROJECT_ID}.traces.otel_traces` b
ON a.trace_id = b.trace_id
AND a.span_id = b.parent_span_id
WHERE a.timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
GROUP BY caller_service, called_service
)
SELECT
caller_service,
called_service,
call_count,
ROUND(avg_call_duration_ms, 2) as avg_call_duration_ms
FROM service_calls
WHERE call_count > 100 -- 주간 100회 이상 호출
ORDER BY call_count DESC;
9.3 월간 최적화 검토
샘플링 비율 최적화
#!/bin/bash
# scripts/monthly-sampling-optimization.sh
echo "=== 분산 추적 월간 최적화 검토 ==="
echo "검토 기간: $(date -d '1 month ago' '+%Y-%m') ~ $(date '+%Y-%m')"
echo
# 환경별 트레이스 볼륨 분석
echo "📊 환경별 트레이스 볼륨 분석"
bq query --use_legacy_sql=false '
SELECT
environment,
COUNT(DISTINCT trace_id) as monthly_traces,
COUNT(*) as monthly_spans,
ROUND(COUNT(*) / COUNT(DISTINCT trace_id), 2) as avg_spans_per_trace,
ROUND(AVG(duration_ms), 2) as avg_span_duration_ms,
COUNTIF(status_code = "ERROR") as error_spans,
ROUND(COUNTIF(status_code = "ERROR") / COUNT(*) * 100, 2) as error_rate_percent
FROM `'$PROJECT_ID'.traces.otel_traces`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY environment
ORDER BY monthly_traces DESC'
echo
echo "💰 비용 최적화 권장사항:"
echo " 1. Dev 환경 샘플링 비율 재조정 검토"
echo " 2. 불필요한 트레이스 속성 제거"
echo " 3. 보관 기간 정책 검토"
echo " 4. Jaeger 리소스 사용량 최적화"
10. 문제 해결
10.1 일반적인 문제
문제 1: 트레이스가 수집되지 않음
증상:
- Cloud Trace에 트레이스가 표시되지 않음
- BigQuery 테이블이 비어있음
- Jaeger UI에 데이터 없음
진단:
# 1. 애플리케이션 트레이싱 초기화 확인
curl -s https://dta-wide-api-dev.run.app/health
gcloud logging read "resource.type=cloud_run_revision AND
resource.labels.service_name=dta-wide-api AND
textPayload:OpenTelemetry" --limit=10
# 2. OpenTelemetry Collector 상태 확인
gcloud logging read "resource.type=cloud_run_revision AND
resource.labels.service_name=otel-collector-dev AND
severity>=WARNING" --limit=50
# 3. OTLP 엔드포인트 연결 테스트
curl -v -X POST "https://otel-collector-dev.europe-west3.run.app/v1/traces" \
-H "Content-Type: application/json" \
-d '{"resourceSpans": []}'
해결책:
- SDK 초기화 확인:
main.ts에서 tracing이 첫 번째로 초기화되는지 확인 - 환경 변수 설정:
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT설정 확인 - 네트워크 연결: Collector 엔드포인트 접근 가능성 확인
- 서비스 계정 권한: Cloud Trace 쓰기 권한 확인
문제 2: 높은 트레이싱 오버헤드
증상:
- 애플리케이션 응답 시간 증가
- CPU/메모리 사용량 급증
- 트레이스 데이터 볼륨 과다
진단:
-- 트레이싱 오버헤드 분석
SELECT
service_name,
COUNT(*) as span_count_per_hour,
AVG(
CASE
WHEN attributes LIKE '%instrumentation.name%'
THEN duration_ms
END
) as avg_instrumentation_overhead_ms
FROM `{PROJECT_ID}.traces.otel_traces`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)
GROUP BY service_name
ORDER BY span_count_per_hour DESC;
해결책:
- 샘플링 조정: 개발/스테이지 환경 샘플링 비율 감소
- 계측 범위 축소: 불필요한 자동 계측 비활성화
- 배치 크기 최적화: Collector 배치 설정 조정
- 속성 필터링: 고용량 속성 제거
10.2 성능 최적화
OpenTelemetry SDK 최적화
// 성능 최적화된 트레이싱 설정
const sdk = new NodeSDK({
// 리소스 속성 최소화
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'dta-wide-api',
[SemanticResourceAttributes.SERVICE_VERSION]: process.env.SERVICE_VERSION || '1.0.0',
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: environment,
// 불필요한 속성 제거
}),
instrumentations: [
getNodeAutoInstrumentations({
// HTTP 계측 최적화
'@opentelemetry/instrumentation-http': {
enabled: true,
// 큰 요청/응답 body 제외
ignoreIncomingRequestHook: (req) => {
return req.url?.includes('/health') ||
req.url?.includes('/metrics') ||
req.headers['content-length'] > 1000000; // 1MB 이상 제외
},
// 민감한 헤더 제외
ignoredHeaders: ['authorization', 'cookie', 'x-api-key']
},
// 데이터베이스 계측 최적화
'@opentelemetry/instrumentation-pg': {
enabled: true,
// SQL 쿼리 매개변수 제외 (성능 및 보안)
enhancedDatabaseReporting: false
},
// 파일 시스템 계측 비활성화 (불필요한 노이즈)
'@opentelemetry/instrumentation-fs': {
enabled: false
}
})
],
// 배치 처리 최적화
spanProcessor: new BatchSpanProcessor(traceExporter, {
maxQueueSize: 1000,
exportTimeoutMillis: 30000,
scheduledDelayMillis: 5000 // 5초마다 배치 전송
})
});
Collector 성능 최적화
# otel-collector-optimized.yaml
processors:
# 메모리 효율적인 배치 처리
batch:
timeout: 10s
send_batch_size: 2048
send_batch_max_size: 4096
# 메모리 제한 강화
memory_limiter:
limit_mib: 512
spike_limit_mib: 128
check_interval: 1s
# 트레이스 필터링 최적화
filter:
traces:
span:
- 'attributes["http.route"] == "/health"'
- 'attributes["http.route"] == "/metrics"'
- 'duration < 1000000' # 1ms 미만 스팬 제외
# 속성 정리 및 최적화
attributes:
actions:
# 고용량 속성 제거
- key: "http.request.body"
action: delete
- key: "http.response.body"
action: delete
# 긴 URL 경로 단축
- key: "http.url"
action: extract
pattern: "^(https?://[^/]+/[^?]*)"
service:
extensions: [memory_ballast]
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, filter, attributes, batch]
exporters: [googlecloud, googlepubsub]
11. 성능 메트릭
11.1 핵심 KPI
분산 추적 성능
| 지표 | Dev | Stage | Prod | 측정 방법 |
|---|---|---|---|---|
| 트레이스 완성도 | > 90% | > 95% | > 99% | Root span 비율 |
| 엔드투엔드 지연시간 P95 | < 3초 | < 2초 | < 1초 | 전체 트레이스 기간 |
| 트레이싱 오버헤드 | < 10% | < 5% | < 3% | 계측 전후 성능 비교 |
| 데이터 정확성 | > 95% | > 98% | > 99.5% | 손실된 스팬 비율 |
비용 효율성
| 환경 | 목표 월간 비용 | 실제 비용 | 트레이스당 비용 | 최적화율 |
|---|---|---|---|---|
| Dev | $15 | - | $0.0001 | 90% |
| Stage | $45 | - | $0.0003 | 70% |
| Prod | $200 | - | $0.0005 | 기준 |
11.2 성능 모니터링 쿼리
실시간 성능 대시보드
-- 분산 추적 성능 실시간 모니터링
WITH trace_metrics AS (
SELECT
TIMESTAMP_TRUNC(timestamp, MINUTE) as minute,
environment,
service_name,
COUNT(DISTINCT trace_id) as trace_count,
COUNT(*) as span_count,
AVG(duration_ms) as avg_span_duration_ms,
PERCENTILE_CONT(duration_ms, 0.95) OVER (
PARTITION BY TIMESTAMP_TRUNC(timestamp, MINUTE), environment, service_name
) as p95_span_duration_ms,
COUNTIF(status_code = 'ERROR') as error_spans,
-- 완전한 트레이스 식별 (Root span 존재)
COUNTIF(parent_span_id IS NULL) as root_spans
FROM `{PROJECT_ID}.traces.otel_traces`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)
GROUP BY minute, environment, service_name
),
trace_completeness AS (
SELECT
minute,
environment,
service_name,
trace_count,
span_count,
ROUND(avg_span_duration_ms, 2) as avg_span_duration_ms,
ROUND(p95_span_duration_ms, 2) as p95_span_duration_ms,
error_spans,
root_spans,
ROUND(SAFE_DIVIDE(root_spans, trace_count) * 100, 2) as trace_completeness_percent,
ROUND(SAFE_DIVIDE(error_spans, span_count) * 100, 2) as error_rate_percent
FROM trace_metrics
)
SELECT
minute,
environment,
service_name,
trace_count,
span_count,
avg_span_duration_ms,
p95_span_duration_ms,
trace_completeness_percent,
error_rate_percent,
CASE
WHEN trace_completeness_percent < 90 THEN 'POOR'
WHEN trace_completeness_percent < 95 THEN 'FAIR'
WHEN trace_completeness_percent < 99 THEN 'GOOD'
ELSE 'EXCELLENT'
END as completeness_grade
FROM trace_completeness
ORDER BY minute DESC, environment, service_name;
서비스 의존성 성능 분석
-- 서비스 간 호출 성능 분석
WITH service_dependencies AS (
SELECT
parent.service_name as caller_service,
child.service_name as called_service,
COUNT(*) as call_count,
AVG(child.duration_ms) as avg_call_duration_ms,
PERCENTILE_CONT(child.duration_ms, 0.95) OVER (
PARTITION BY parent.service_name, child.service_name
) as p95_call_duration_ms,
COUNTIF(child.status_code = 'ERROR') as failed_calls,
SAFE_DIVIDE(COUNTIF(child.status_code = 'ERROR'), COUNT(*)) as failure_rate
FROM `{PROJECT_ID}.traces.otel_traces` parent
JOIN `{PROJECT_ID}.traces.otel_traces` child
ON parent.trace_id = child.trace_id
AND parent.span_id = child.parent_span_id
WHERE parent.timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR)
GROUP BY parent.service_name, child.service_name
)
SELECT
caller_service,
called_service,
call_count,
ROUND(avg_call_duration_ms, 2) as avg_call_duration_ms,
ROUND(p95_call_duration_ms, 2) as p95_call_duration_ms,
failed_calls,
ROUND(failure_rate * 100, 2) as failure_rate_percent,
CASE
WHEN failure_rate > 0.1 THEN 'CRITICAL'
WHEN failure_rate > 0.05 THEN 'WARNING'
WHEN failure_rate > 0.01 THEN 'WATCH'
ELSE 'HEALTHY'
END as health_status
FROM service_dependencies
WHERE call_count > 10 -- 최소 10회 이상 호출
ORDER BY call_count DESC, failure_rate DESC;
12. 다음 단계
12.1 단기 개선 사항 (1-2주)
-
애플리케이션 통합 완료
- 모든 서비스에 OpenTelemetry SDK 적용
- 핵심 비즈니스 로직 커스텀 계측 추가
- 민감한 데이터 마스킹 정책 적용
-
성능 최적화
- 트레이싱 오버헤드 최소화
- 샘플링 전략 정밀 조정
- Collector 리소스 효율성 개선
-
모니터링 강화
- SLO 기반 알림 정책 세부 조정
- Jaeger 대시보드 활용 교육
- 트레이스 기반 장애 대응 프로세스 수립
12.2 중기 계획 (1-2개월)
-
고급 분석 기능
- 분산 트레이스 기반 성능 이상 탐지
- 서비스 의존성 자동 모니터링
- 트레이스 패턴 기반 용량 계획
-
규제 준수 강화
- 의료 기기 소프트웨어 감사 요구사항 대응
- 트레이스 데이터 거버넌스 정책 수립
- 개인정보 보호 강화 (자동 마스킹)
-
개발자 경험 개선
- IDE 통합 트레이싱 플러그인
- 로컬 개발 환경 트레이싱 지원
- 트레이스 기반 디버깅 도구
12.3 장기 비전 (3-6개월)
-
AI/ML 기반 분석
- 트레이스 패턴 기반 성능 예측
- 자동 이상 탐지 및 근본 원인 분석
- 지능형 샘플링 (동적 샘플링 비율 조정)
-
통합 관찰가능성
- 메트릭, 로그, 트레이스 통합 분석
- 분산 트레이스 기반 SLO 관리
- 종합적인 서비스 헬스 스코어
-
자동화 및 자가 치유
- 트레이스 기반 자동 스케일링
- 성능 이상 시 자동 대응
- 무인 트레이싱 시스템 운영
12.4 확장 가능성
다른 관찰가능성 도구와의 통합
📚 관련 문서
- OpenTelemetry 공식 문서
- Google Cloud Trace 문서
- Jaeger 공식 문서
- BigQuery 트레이스 분석 가이드
- 인프라 감사 보고서
- 메트릭 수집 구현 가이드
문서 버전: 1.0.0
최종 업데이트: 2025-01-22
문서 승인: DTA-Wide 인프라팀
다음 검토 예정: 2025-02-22
변경 이력
| 버전 | 날짜 | 작성자 | 변경 내용 |
|---|---|---|---|
| 0.1.0 | 2025-08-13 | bok@weltcorp.com | 최초 작성 |