Skip to content
Published on

Istio 관찰성 내부 구현: 메트릭, 트레이싱, 로깅

Authors

들어가며

관찰성(Observability)은 서비스 메시의 핵심 가치 중 하나입니다. Istio는 애플리케이션 코드 수정 없이 메트릭, 분산 트레이싱, 액세스 로깅을 자동으로 생성합니다.

이 글에서는 Istio가 어떻게 이러한 텔레메트리 데이터를 생성하고, Envoy 필터 체인에서 어떤 처리가 이루어지며, 외부 시스템과 어떻게 통합되는지 내부 구현을 분석합니다.

Envoy 통계 시스템

통계 타입

Envoy는 세 가지 타입의 통계를 생성합니다:

타입설명예시
Counter단조 증가하는 값총 요청 수, 총 에러 수
Gauge증가/감소 가능한 현재 값활성 연결 수, 대기 중 요청 수
Histogram값의 분포요청 지연 시간, 응답 크기

필터 체인에서의 통계 생성

요청 흐름과 통계 생성 위치:

Listener (connection stats)
HTTP Connection Manager (request stats)
    ├── JWT Authn Filter → 인증 성공/실패 카운터
    ├── RBAC Filter → 인가 허용/거부 카운터
    ├── Fault Filter → 주입된 지연/중단 카운터
    ├── Stats Filter (istio.stats)Istio 표준 메트릭 생성
    └── Router Filter → 업스트림 요청 통계
Cluster (upstream stats)
Endpoint (connection/request stats)

Istio 표준 메트릭

핵심 HTTP 메트릭

istio_requests_total (Counter)

요청 수를 추적하는 핵심 메트릭:

istio_requests_total{
  reporter="source",                    # 또는 "destination"
  source_workload="frontend",
  source_workload_namespace="prod",
  source_principal="spiffe://cluster.local/ns/prod/sa/frontend",
  destination_workload="reviews",
  destination_workload_namespace="prod",
  destination_principal="spiffe://cluster.local/ns/prod/sa/reviews",
  destination_service="reviews.prod.svc.cluster.local",
  destination_service_name="reviews",
  destination_service_namespace="prod",
  request_protocol="http",
  response_code="200",
  response_flags="-",
  connection_security_policy="mutual_tls"
}

istio_request_duration_milliseconds (Histogram)

요청 처리 시간 분포:

istio_request_duration_milliseconds_bucket{
  ...,  # 위와 동일한 라벨
  le="1"
} 100
istio_request_duration_milliseconds_bucket{le="5"} 250
istio_request_duration_milliseconds_bucket{le="10"} 380
istio_request_duration_milliseconds_bucket{le="25"} 450
istio_request_duration_milliseconds_bucket{le="50"} 490
istio_request_duration_milliseconds_bucket{le="100"} 498
istio_request_duration_milliseconds_bucket{le="+Inf"} 500

istio_request_bytes / istio_response_bytes (Histogram)

요청/응답 크기 분포를 추적합니다.

TCP 메트릭

istio_tcp_sent_bytes_total         # 전송된 바이트 총량
istio_tcp_received_bytes_total     # 수신된 바이트 총량
istio_tcp_connections_opened_total # 열린 연결 수
istio_tcp_connections_closed_total # 닫힌 연결 수

메트릭 생성 위치: Source vs Destination

Frontend Pod                    Reviews Pod
[App][Envoy]  ──────→  [Envoy][App]
         │                    │
    reporter="source"    reporter="destination"
    (아웃바운드 측 기록)   (인바운드 측 기록)

양쪽 Envoy 모두 메트릭을 생성하지만, reporter 라벨로 구분됩니다. 일반적으로 "source" 리포터를 사용하면 클라이언트 관점, "destination"을 사용하면 서버 관점의 메트릭을 얻습니다.

Telemetry API v2

아키텍처 진화

Istio 1.x (Mixer 기반):
AppEnvoyMixerPrometheus/Zipkin
              (별도 서비스, 높은 레이턴시)

Istio 1.12+ (Telemetry API v2):
AppEnvoy (내장 Stats/Trace 필터)Prometheus/Zipkin
      (프록시 내부 처리, 낮은 레이턴시)

Mixer가 제거된 후, 메트릭 생성은 Envoy 프록시 내부에서 직접 수행됩니다.

Telemetry 리소스

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: mesh-default
  namespace: istio-system # 메시 전체 적용
spec:
  # 메트릭 설정
  metrics:
    - providers:
        - name: prometheus
      overrides:
        - match:
            metric: REQUEST_COUNT
            mode: CLIENT_AND_SERVER
          tagOverrides:
            request_host:
              operation: UPSERT
              value: 'request.host'

  # 트레이싱 설정
  tracing:
    - providers:
        - name: zipkin
      randomSamplingPercentage: 1.0
      customTags:
        environment:
          literal:
            value: 'production'

  # 액세스 로깅 설정
  accessLogging:
    - providers:
        - name: envoy
      filter:
        expression: 'response.code >= 400'

메트릭 커스터마이징

# 특정 메트릭 비활성화
spec:
  metrics:
  - providers:
    - name: prometheus
    overrides:
    - match:
        metric: REQUEST_BYTES
      disabled: true

# 커스텀 태그 추가
    overrides:
    - match:
        metric: REQUEST_COUNT
      tagOverrides:
        custom_tag:
          operation: UPSERT
          value: "request.headers['x-custom-tag']"

분산 트레이싱

트레이스 전파 메커니즘

Envoy가 자동으로 수행하는 것:
├── 인바운드 요청에서 트레이스 헤더 추출
├── 스팬(span) 생성 및 타이밍 기록
├── 스팬을 트레이스 수집기에 전송
└── 아웃바운드 요청에 트레이스 헤더 추가

애플리케이션이 해야 하는 것:
└── 인바운드 요청의 트레이스 헤더를 아웃바운드 요청에 복사
    (이것을 하지 않으면 트레이스가 끊어짐)

지원하는 트레이스 헤더

B3 헤더 (Zipkin):

x-b3-traceid:      128비트 트레이스 ID
x-b3-spanid:       64비트 스팬 ID
x-b3-parentspanid: 64비트 부모 스팬 ID
x-b3-sampled:      샘플링 여부 (0 또는 1)
x-b3-flags:        디버그 플래그

W3C TraceContext:

traceparent: 00-TRACE_ID-SPAN_ID-FLAGS
tracestate:  vendor-specific key=value pairs

Envoy 내부 헤더:

x-request-id: Envoy가 생성하는 UUID (트레이스와 연결)

스팬 생성 상세

FrontendReviewsRatings 호출 시:

Frontend Envoy (아웃바운드):
  Span: "reviews.prod.svc.cluster.local:9080/*"
  ├── Start: 요청 전송 시작
  ├── End: 응답 수신 완료
  ├── Tags: upstream_cluster, http.method, http.status_code
  └── Parent: 인바운드 스팬

Reviews Envoy (인바운드):
  Span: "reviews.prod.svc.cluster.local:9080/*"
  ├── Start: 요청 수신
  ├── End: 응답 전송
  └── Tags: downstream_cluster, peer.address

Reviews Envoy (아웃바운드):
  Span: "ratings.prod.svc.cluster.local:9080/*"
  ├── Start: 요청 전송 시작
  ├── End: 응답 수신 완료
  └── Parent: 인바운드 스팬

트레이스 샘플링

# MeshConfig에서 설정
meshConfig:
  defaultConfig:
    tracing:
      sampling: 1.0 # 1% (기본값)
      # sampling: 100.0  # 100% (디버깅용)

# 또는 Telemetry API로 설정
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: tracing
spec:
  tracing:
    - randomSamplingPercentage: 1.0

샘플링 결정은 첫 번째 Envoy에서 이루어지고, x-b3-sampled 헤더를 통해 전파됩니다. 이후 Envoy들은 이 결정을 따릅니다.

트레이스 수집기 통합

# Zipkin 통합
meshConfig:
  defaultConfig:
    tracing:
      zipkin:
        address: zipkin.istio-system:9411

# Jaeger 통합 (Zipkin 호환 엔드포인트 사용)
meshConfig:
  defaultConfig:
    tracing:
      zipkin:
        address: jaeger-collector.observability:9411

# OpenTelemetry Collector 통합
meshConfig:
  extensionProviders:
  - name: otel
    opentelemetry:
      service: otel-collector.observability.svc.cluster.local
      port: 4317

액세스 로깅

Envoy 액세스 로그 형식

기본 로그 형식:

[2026-03-20T10:30:00.000Z] "GET /api/reviews HTTP/1.1" 200 - via_upstream
  - "-" 0 1234 5 3
  "-" "curl/7.68.0" "abc-123-def"
  "reviews.prod.svc.cluster.local:9080"
  inbound|9080||reviews.prod.svc.cluster.local
  10.244.1.5:9080 10.244.0.3:48292
  outbound_.9080_.v1_.reviews.prod.svc.cluster.local default

로그 필드 설명

[타임스탬프] "메서드 경로 프로토콜" 상태코드 응답플래그
  - "-" 요청바이트 응답바이트 처리시간(ms) 업스트림시간(ms)
  "-" "User-Agent" "Request-ID"
  "업스트림 호스트"
  라우트 이름
  다운스트림 주소 업스트림 주소
  클러스터 이름 네임스페이스

응답 플래그 (Response Flags)

플래그의미
-정상 응답
UH업스트림 건강하지 않음 (모두 ejected)
UF업스트림 연결 실패
UO업스트림 오버플로우 (서킷 브레이커)
NR라우트 없음
URX리트라이 한도 초과
DC다운스트림 연결 종료
LH로컬 헬스 체크 실패
UT업스트림 타임아웃
RL레이트 리밋
UAEX외부 인가 거부
RLSE레이트 리밋 서비스 에러

조건부 로깅

Telemetry API를 사용한 조건부 액세스 로깅:

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: access-log-errors
  namespace: production
spec:
  accessLogging:
    - providers:
        - name: envoy
      filter:
        expression: 'response.code >= 400 || connection.mtls == false'

CEL(Common Expression Language) 표현식을 사용하여 로깅 조건을 세밀하게 제어할 수 있습니다.

Kiali: 서비스 메시 시각화

Kiali 아키텍처

Kiali 데이터 소스:
├── Prometheus → 메트릭 기반 서비스 그래프
├── Kubernetes API → 워크로드, 서비스 정보
├── Istio Config APIVirtualService, DestinationRule└── Jaeger/Tempo → 분산 트레이스 (선택사항)

Kiali가 제공하는 정보

1. 서비스 그래프 (Topology)
   ├── 서비스 간 트래픽 흐름
   ├── 요청 성공/실패 비율
   ├── 초당 요청 수
   └── 응답 시간

2. 워크로드 건강 상태
   ├── 에러율 기반 건강 점수
   ├── 인바운드/아웃바운드 메트릭
   └── Pod 상태

3. Istio 구성 검증
   ├── VirtualService 유효성
   ├── DestinationRule 충돌 감지
   ├── 참조 무결성 (존재하지 않는 host 등)
   └── 베스트 프랙티스 위반

4. 트래픽 분석
   ├── 시간별 트래픽 추이
   ├── 에러 패턴 식별
   └── 레이턴시 분포

Prometheus 통합

메트릭 수집 구성

Istio는 Prometheus의 서비스 디스커버리를 활용합니다:

# Prometheus scrape 구성
scrape_configs:
  - job_name: 'envoy-stats'
    metrics_path: /stats/prometheus
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_container_name]
        action: keep
        regex: istio-proxy
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        target_label: __address__
        regex: (.+)
        replacement: 'target:15090'

각 Envoy 프록시는 포트 15090에서 Prometheus 메트릭을 노출합니다.

유용한 PromQL 쿼리

# 서비스별 요청 성공률 (최근 5분)
sum(rate(istio_requests_total{
  response_code!~"5.*",
  reporter="destination"
}[5m])) by (destination_service_name)
/
sum(rate(istio_requests_total{
  reporter="destination"
}[5m])) by (destination_service_name)

# P99 레이턴시
histogram_quantile(0.99,
  sum(rate(istio_request_duration_milliseconds_bucket{
    reporter="source"
  }[5m])) by (le, destination_service_name)
)

# 서비스별 초당 요청 수
sum(rate(istio_requests_total{
  reporter="destination"
}[5m])) by (destination_service_name)

Grafana 대시보드

Istio 표준 대시보드

Istio는 다음 Grafana 대시보드를 제공합니다:

1. Mesh Dashboard
   └── 메시 전체 요약 (서비스 수, 에러율, 트래픽)

2. Service Dashboard
   └── 서비스별 상세 (인바운드/아웃바운드, 에러율, 레이턴시)

3. Workload Dashboard
   └── 워크로드별 상세 (파드 단위 메트릭)

4. Control Plane Dashboard
   └── istiod 성능 (xDS 푸시 수, 응답 시간, 에러)

5. Performance Dashboard
   └── Envoy 리소스 사용량 (메모리, CPU, 연결 수)

Jaeger/Zipkin/Tempo 통합

분산 트레이싱 백엔드

지원하는 트레이싱 백엔드:

Zipkin
├── 경량, 간단한 설치
├── 인메모리 또는 Cassandra/Elasticsearch 저장
└── Istio 기본 지원

Jaeger
├── Zipkin 호환 API
├── 다양한 스토리지 백엔드 지원
├── Spark 기반 분석
└── 프로덕션 권장

Tempo (Grafana)
├── 오브젝트 스토리지 기반 (S3, GCS)
├── 높은 확장성
├── Grafana와 네이티브 통합
└── 비용 효율적

트레이스 데이터 흐름

[1] 요청이 메시 진입
[2] 첫 번째 Envoy가 트레이스 ID 생성
    (기존 헤더가 없는 경우)
[3]Envoy가 스팬 생성 및 수집기에 전송
    ├── Zipkin: HTTP POST /api/v2/spans
    ├── Jaeger: UDP/gRPC
    └── OTLP: gRPC (OpenTelemetry)
[4] 수집기가 스팬을 트레이스로 조합
[5] UI에서 트레이스 조회

디버깅 팁

메트릭 확인

# 특정 파드의 Envoy 메트릭 직접 확인
kubectl exec PODNAME -c istio-proxy -- \
  curl -s localhost:15090/stats/prometheus | grep istio_requests

# Envoy 관리 API로 통계 확인
kubectl exec PODNAME -c istio-proxy -- \
  curl -s localhost:15000/stats | grep -E "^cluster\."

# Envoy 서버 정보
kubectl exec PODNAME -c istio-proxy -- \
  curl -s localhost:15000/server_info

트레이싱 확인

# 트레이스 헤더 전파 확인
kubectl exec PODNAME -c istio-proxy -- \
  curl -s localhost:15000/config_dump | python3 -c "
import json, sys
config = json.load(sys.stdin)
for c in config.get('configs', []):
    if 'tracing' in str(c):
        print(json.dumps(c, indent=2))
"

액세스 로그 확인

# 실시간 액세스 로그 확인
kubectl logs PODNAME -c istio-proxy -f | grep -v healthz

# 에러 응답만 필터링
kubectl logs PODNAME -c istio-proxy | grep -E '"[45][0-9]{2}"'

마무리

Istio의 관찰성은 Envoy 프록시의 풍부한 텔레메트리 기능 위에 구축됩니다. 핵심 포인트를 정리하면:

  1. 메트릭: Envoy의 Stats 필터가 istio_requests_total 등 표준 메트릭을 생성하고, Prometheus가 수집
  2. 트레이싱: Envoy가 자동으로 스팬을 생성하지만, 애플리케이션이 트레이스 헤더를 전파해야 end-to-end 트레이싱이 가능
  3. 로깅: Envoy 액세스 로그가 모든 요청/응답을 기록하며, Telemetry API로 조건부 로깅 가능
  4. 시각화: Kiali가 Prometheus 메트릭을 기반으로 서비스 그래프를 생성

Istio Internals 시리즈를 통해 컨트롤 플레인, 트래픽 관리, 보안, Ambient Mesh, 관찰성의 내부 동작을 살펴보았습니다. 이러한 내부 이해가 실무에서 서비스 메시를 효과적으로 운영하는 데 도움이 되길 바랍니다.