-
분산 트레이싱 트러블슈팅: OpenTelemetry 커버리지 확장이코에코(Eco²)/Troubleshooting 2025. 12. 19. 03:00

시스템 컴포넌트 OTEL 적용
개요
시스템 컴포넌트(Istio, ArgoCD)에도 분산 추적을 적용하여
trace.id를 로그에 포함.적용 대상
시스템 OTEL 지원 적용 방법 결과 Istio (Envoy) ✅ EnvoyFilter Access log에 trace.id포함ArgoCD ✅ ConfigMap Jaeger에 트레이스 전송 Calico ❌ 미지원 - Kubernetes ⚠️ 제한적 - 1. Istio Access Log with Trace ID
문제: ext-authz 거부/404 요청에 trace.id 없음
초기 설정에서
%REQ(X-B3-TRACEID)%를 사용했으나, 클라이언트가 헤더를 보내지 않으면 빈 값:# 문제 상황 /api/v1/auth/register → 401 (ext-authz 거부) → trace.id: 없음 /api/v1/nonexistent → 404 (라우팅 실패) → trace.id: 없음 /api/v1/auth/refresh → 401 (앱 도달) → trace.id: 있음해결: 사용
Envoy 내부 변수
%TRACE_ID%를 사용하면 모든 요청에 trace가 자동 생성됨.변수 설명 값 보장 %REQ(X-B3-TRACEID)%클라이언트가 보낸 헤더 ❌ 없으면 빈 값 %TRACE_ID%Envoy 내부 trace ID ✅ 항상 자동 생성 EnvoyFilter 설정 (
workloads/istio/base/envoy-filter-access-log.yaml):apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: enable-access-log namespace: istio-system spec: configPatches: - applyTo: NETWORK_FILTER match: context: ANY listener: filterChain: filter: name: "envoy.filters.network.http_connection_manager" patch: operation: MERGE value: typed_config: "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager" access_log: - name: envoy.access_loggers.file typed_config: "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog" path: "/dev/stdout" log_format: json_format: # ECS 표준 필드명 (dot notation) trace.id: "%TRACE_ID%" # ✅ Envoy 자동 생성 span.id: "%REQ(X-B3-SPANID)%" http.request.method: "%REQ(:METHOD)%" url.path: "%REQ(:PATH)%" http.response.status_code: "%RESPONSE_CODE%" http.response.body.bytes: "%BYTES_SENT%" start_time: "%START_TIME%" duration_ms: "%DURATION%" upstream_host: "%UPSTREAM_HOST%" source.address: "%REQ(X-FORWARDED-FOR)%" request_id: "%REQ(X-REQUEST-ID)%"검증 결과
# %TRACE_ID% 적용 후 /api/v1/auth/register → 401 (ext-authz 거부) → trace.id: e8f47ed65ec9ece3... ✅ /api/v1/test404 → 404 (라우팅 실패) → trace.id: e05895c1b6571707... ✅ /api/v1/auth/refresh → 401 (앱 도달) → trace.id: 4698731e87d0b18b... ✅모든 요청에 trace.id가 포함되어 에러 추적 가능
출력 예시
{ "trace.id": "e8f47ed65ec9ece3d4c629cf2374f680", "http.request.method": "POST", "url.path": "/api/v1/auth/register", "http.response.status_code": 401 }2. ArgoCD OTEL 트레이싱
ConfigMap 설정 (
workloads/argocd/base/otel-config.yaml):apiVersion: v1 kind: ConfigMap metadata: name: argocd-cmd-params-cm namespace: argocd data: otlp.address: jaeger-collector-clusterip.istio-system.svc.cluster.local:4317ArgoCD 작업(sync, refresh 등)이 Jaeger에 트레이스로 표시됨
적용 명령
# Istio 설정 적용 kubectl apply -f workloads/istio/base/ # ArgoCD 설정 적용 후 재시작 kubectl apply -f workloads/argocd/base/ kubectl rollout restart deployment argocd-server -n argocd kubectl rollout restart deployment argocd-repo-server -n argocd kubectl rollout restart statefulset argocd-application-controller -n argocdKibana 검색
# istio-proxy 로그에서 특정 trace 검색 trace_id:15434b0153e43190afcbfb316469ccfe AND k8s_container_name:istio-proxy # 앱 로그 + istio-proxy 같이 검색 trace_id:* AND (service.name:auth-api OR k8s_container_name:istio-proxy)
커밋
feat(istio): add EnvoyFilter for JSON access log with trace.id - Enable structured JSON access logging via EnvoyFilter - Include trace.id, span.id, request_id in access logs - Add Telemetry API configuration for mesh-wide access logging - Use ECS standard field names (dot notation)fix(istio): use %TRACE_ID% for all requests including ext-authz denials - Change from %REQ(X-B3-TRACEID)% to %TRACE_ID% in EnvoyFilter - %TRACE_ID% is auto-generated by Envoy for all requests - Enables trace correlation for 401 (ext-authz denied) and 404 errors - Before: ext-authz denied requests had no trace.id - After: all requests have trace.id for full error trackingfeat(argocd): enable OTEL tracing to Jaeger - Configure otlp.address in argocd-cmd-params-cm - ArgoCD operations now visible in Jaeger
OpenTelemetry 커버리지 분석
OTEL이 커버하는 범위
컴포넌트 방식 trace.id 지원 비고 Python API (auth, chat 등) OTEL SDK 자동 계측 ✅ opentelemetry-instrumentIstio Sidecar Envoy 내장 tracing ✅ %TRACE_ID%변수Istio Ingress Gateway Envoy 내장 tracing ✅ Trace 생성 원점 ArgoCD 내장 OTLP 지원 ✅ otlp.address설정OTEL이 커버하지 않는 범위
ext-authz (Go gRPC) OTEL SDK 미적용 gRPC 메타데이터에서 B3 헤더 추출 Calico 네트워크 레이어 N/A (trace 불필요) Kubernetes 컴포넌트 제한적 지원 N/A
gRPC 서비스 Trace 추적 (ext-authz)
문제 상황
ext-authz는 Go로 작성된 gRPC 서비스로, Python API처럼 OTEL 자동 계측이 불가능.
초기 상태: - ext-authz 로그에 trace.id 없음 - istio-proxy 로그에만 trace.id 존재 - 인증 실패 원인 추적 시 trace 연결 불가해결: gRPC 메타데이터에서 Trace Context 추출
Istio sidecar가 ext-authz로 gRPC 요청 시 메타데이터에 B3 헤더를 주입한다.
1. 상수 정의
const ( // B3 Trace Context headers (Istio/Envoy) HeaderB3TraceID = "x-b3-traceid" HeaderB3SpanID = "x-b3-spanid" )2. gRPC 메타데이터 추출
import "google.golang.org/grpc/metadata" // extractTraceInfo extracts B3 trace context from gRPC metadata func extractTraceInfo(ctx context.Context, req *authv3.CheckRequest) logging.TraceInfo { trace := logging.TraceInfo{} // 1. gRPC metadata (Istio sidecar가 주입) if md, ok := metadata.FromIncomingContext(ctx); ok { if vals := md.Get("x-b3-traceid"); len(vals) > 0 { trace.TraceID = vals[0] } if vals := md.Get("x-b3-spanid"); len(vals) > 0 { trace.SpanID = vals[0] } } // 2. Fallback: HTTP 헤더 (클라이언트가 직접 전송한 경우) if trace.TraceID == "" && req.Attributes != nil { headers := req.Attributes.Request.Http.Headers trace.TraceID = headers["x-b3-traceid"] } return trace }3. 로그에 trace.id 포함
func (l *Logger) WithTrace(traceID, spanID string) *Logger { if traceID == "" { return l } return &Logger{ Logger: l.With( slog.String("trace.id", traceID), slog.String("span.id", spanID), ), } }결과
{ "@timestamp": "2025-12-18T12:02:06.845Z", "service.name": "ext-authz", "trace.id": "a593d6809fe6f036728dc73cfd170b0e", "span.id": "3e491beac3443f3c", "msg": "Authorization denied", "event.outcome": "failure", "event.reason": "missing_auth_header" }전체 요청 흐름 추적 (동일 trace.id)
trace.id:a593d6809fe6f036728dc73cfd170b0e12:02:06.845 ext-authz Authorization denied 12:02:06.846 istio-proxy gRPC /Authorization/Check → 200 12:02:07.742 istio-proxy HTTP /api/v1/auth/register → 401
Trace 전파 경로 요약
┌─────────────────────────────────────────────────────────────────────────┐ │ Trace ID Propagation │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ [Client Request] │ │ │ │ │ ▼ │ │ ┌──────────────────┐ │ │ │ Istio Ingress │ ◀── trace.id 생성 (%TRACE_ID%) │ │ │ Gateway │ │ │ └────────┬─────────┘ │ │ │ gRPC + B3 메타데이터 │ │ ▼ │ │ ┌──────────────────┐ │ │ │ ext-authz │ ◀── gRPC metadata에서 trace.id 추출 │ │ │ (Go gRPC) │ → 로그에 포함 │ │ └────────┬─────────┘ │ │ │ 인증 결과 │ │ ▼ │ │ ┌──────────────────┐ │ │ │ App Sidecar │ ◀── X-B3-TraceId 헤더 전파 │ │ │ (istio-proxy) │ │ │ └────────┬─────────┘ │ │ │ HTTP + B3 헤더 │ │ ▼ │ │ ┌──────────────────┐ │ │ │ App (Python) │ ◀── OTEL SDK가 B3 헤더 읽음 │ │ │ + OTEL SDK │ → 동일 trace.id로 span 생성 │ │ └──────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
커밋
feat(ext-authz): add B3 trace context to authorization logs - Extract x-b3-traceid from gRPC metadata (Istio sidecar injects here) - Fallback to HTTP headers if client sent them - Add trace.id and span.id to all authorization log entries - Enables end-to-end trace correlation in Kibana'이코에코(Eco²) > Troubleshooting' 카테고리의 다른 글
Message Queue 트러블슈팅: Quorum Queue -> Classic Queue 마이그레이션 (0) 2025.12.24 Message Queue 트러블슈팅: RabbitMQ 구축 (0) 2025.12.22 분산 트레이싱 트러블슈팅: Log-Trace 연동 및 Kibana 검색 구조 (0) 2025.12.19 선언적 배포 트러블슈팅: eck-custom-resources operator 도입 실패 포스트모템 (0) 2025.12.18 분산 트레이싱 트러블슈팅: NetworkPolicy, Zipkin, OpenTelemetry (0) 2025.12.18