Split View: Istio 관찰성 내부 구현: 메트릭, 트레이싱, 로깅
Istio 관찰성 내부 구현: 메트릭, 트레이싱, 로깅
- 들어가며
- Envoy 통계 시스템
- Istio 표준 메트릭
- Telemetry API v2
- 분산 트레이싱
- 액세스 로깅
- Kiali: 서비스 메시 시각화
- Prometheus 통합
- Grafana 대시보드
- Jaeger/Zipkin/Tempo 통합
- 디버깅 팁
- 마무리
들어가며
관찰성(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 기반):
App → Envoy → Mixer → Prometheus/Zipkin
(별도 서비스, 높은 레이턴시)
Istio 1.12+ (Telemetry API v2):
App → Envoy (내장 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 (트레이스와 연결)
스팬 생성 상세
Frontend → Reviews → Ratings 호출 시:
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 API → VirtualService, 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 프록시의 풍부한 텔레메트리 기능 위에 구축됩니다. 핵심 포인트를 정리하면:
- 메트릭: Envoy의 Stats 필터가 istio_requests_total 등 표준 메트릭을 생성하고, Prometheus가 수집
- 트레이싱: Envoy가 자동으로 스팬을 생성하지만, 애플리케이션이 트레이스 헤더를 전파해야 end-to-end 트레이싱이 가능
- 로깅: Envoy 액세스 로그가 모든 요청/응답을 기록하며, Telemetry API로 조건부 로깅 가능
- 시각화: Kiali가 Prometheus 메트릭을 기반으로 서비스 그래프를 생성
Istio Internals 시리즈를 통해 컨트롤 플레인, 트래픽 관리, 보안, Ambient Mesh, 관찰성의 내부 동작을 살펴보았습니다. 이러한 내부 이해가 실무에서 서비스 메시를 효과적으로 운영하는 데 도움이 되길 바랍니다.
Istio Observability Internals: Metrics, Tracing, Logging
- Introduction
- Envoy Statistics System
- Istio Standard Metrics
- Telemetry API v2
- Distributed Tracing
- Access Logging
- Kiali: Service Mesh Visualization
- Prometheus Integration
- Grafana Dashboards
- Jaeger/Zipkin/Tempo Integration
- Debugging Tips
- Conclusion
Introduction
Observability is one of the core values of a service mesh. Istio automatically generates metrics, distributed tracing, and access logging without any application code changes.
This post analyzes how Istio generates telemetry data, what processing occurs in the Envoy filter chain, and how it integrates with external systems.
Envoy Statistics System
Statistics Types
Envoy generates three types of statistics:
| Type | Description | Example |
|---|---|---|
| Counter | Monotonically increasing value | Total requests, total errors |
| Gauge | Value that can increase/decrease | Active connections, pending requests |
| Histogram | Distribution of values | Request latency, response size |
Statistics Generation in the Filter Chain
Request flow and statistics generation points:
Listener (connection stats)
│
▼
HTTP Connection Manager (request stats)
│
├── JWT Authn Filter → auth success/failure counters
├── RBAC Filter → authz allow/deny counters
├── Fault Filter → injected delay/abort counters
├── Stats Filter (istio.stats) → Istio standard metrics generation
└── Router Filter → upstream request statistics
│
▼
Cluster (upstream stats)
│
▼
Endpoint (connection/request stats)
Istio Standard Metrics
Core HTTP Metrics
istio_requests_total (Counter)
Core metric tracking request counts:
istio_requests_total{
reporter="source", # or "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)
Request processing time distribution:
istio_request_duration_milliseconds_bucket{
..., # same labels as above
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)
Tracks request/response size distribution.
TCP Metrics
istio_tcp_sent_bytes_total # Total bytes sent
istio_tcp_received_bytes_total # Total bytes received
istio_tcp_connections_opened_total # Connections opened
istio_tcp_connections_closed_total # Connections closed
Metric Generation Location: Source vs Destination
Frontend Pod Reviews Pod
[App] → [Envoy] ──────→ [Envoy] → [App]
│ │
reporter="source" reporter="destination"
(outbound side) (inbound side)
Both Envoys generate metrics, distinguished by the reporter label. Generally, using "source" reporter gives client-side perspective, while "destination" gives server-side perspective.
Telemetry API v2
Architecture Evolution
Istio 1.x (Mixer-based):
App → Envoy → Mixer → Prometheus/Zipkin
(separate service, high latency)
Istio 1.12+ (Telemetry API v2):
App → Envoy (built-in Stats/Trace filters) → Prometheus/Zipkin
(in-proxy processing, low latency)
After Mixer removal, metric generation is performed directly inside the Envoy proxy.
Telemetry Resource
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: mesh-default
namespace: istio-system # mesh-wide application
spec:
# Metrics configuration
metrics:
- providers:
- name: prometheus
overrides:
- match:
metric: REQUEST_COUNT
mode: CLIENT_AND_SERVER
tagOverrides:
request_host:
operation: UPSERT
value: 'request.host'
# Tracing configuration
tracing:
- providers:
- name: zipkin
randomSamplingPercentage: 1.0
customTags:
environment:
literal:
value: 'production'
# Access logging configuration
accessLogging:
- providers:
- name: envoy
filter:
expression: 'response.code >= 400'
Metric Customization
# Disable specific metric
spec:
metrics:
- providers:
- name: prometheus
overrides:
- match:
metric: REQUEST_BYTES
disabled: true
# Add custom tags
overrides:
- match:
metric: REQUEST_COUNT
tagOverrides:
custom_tag:
operation: UPSERT
value: "request.headers['x-custom-tag']"
Distributed Tracing
Trace Propagation Mechanism
What Envoy does automatically:
├── Extract trace headers from inbound requests
├── Create spans and record timing
├── Send spans to trace collector
└── Add trace headers to outbound requests
What the application must do:
└── Copy trace headers from inbound to outbound requests
(without this, traces are broken)
Supported Trace Headers
B3 Headers (Zipkin):
x-b3-traceid: 128-bit trace ID
x-b3-spanid: 64-bit span ID
x-b3-parentspanid: 64-bit parent span ID
x-b3-sampled: sampling decision (0 or 1)
x-b3-flags: debug flag
W3C TraceContext:
traceparent: 00-TRACE_ID-SPAN_ID-FLAGS
tracestate: vendor-specific key=value pairs
Envoy Internal Header:
x-request-id: UUID generated by Envoy (linked to trace)
Span Generation Detail
When Frontend → Reviews → Ratings:
Frontend Envoy (outbound):
Span: "reviews.prod.svc.cluster.local:9080/*"
├── Start: request send start
├── End: response receive complete
├── Tags: upstream_cluster, http.method, http.status_code
└── Parent: inbound span
Reviews Envoy (inbound):
Span: "reviews.prod.svc.cluster.local:9080/*"
├── Start: request received
├── End: response sent
└── Tags: downstream_cluster, peer.address
Reviews Envoy (outbound):
Span: "ratings.prod.svc.cluster.local:9080/*"
├── Start: request send start
├── End: response receive complete
└── Parent: inbound span
Trace Sampling
# Set in MeshConfig
meshConfig:
defaultConfig:
tracing:
sampling: 1.0 # 1% (default)
# sampling: 100.0 # 100% (for debugging)
# Or set via Telemetry API
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: tracing
spec:
tracing:
- randomSamplingPercentage: 1.0
The sampling decision is made at the first Envoy and propagated via the x-b3-sampled header. Subsequent Envoys follow this decision.
Trace Collector Integration
# Zipkin integration
meshConfig:
defaultConfig:
tracing:
zipkin:
address: zipkin.istio-system:9411
# Jaeger integration (using Zipkin-compatible endpoint)
meshConfig:
defaultConfig:
tracing:
zipkin:
address: jaeger-collector.observability:9411
# OpenTelemetry Collector integration
meshConfig:
extensionProviders:
- name: otel
opentelemetry:
service: otel-collector.observability.svc.cluster.local
port: 4317
Access Logging
Envoy Access Log Format
Default log format:
[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
Response Flags
| Flag | Meaning |
|---|---|
| - | Normal response |
| UH | Upstream unhealthy (all ejected) |
| UF | Upstream connection failure |
| UO | Upstream overflow (circuit breaker) |
| NR | No route |
| URX | Retry limit exceeded |
| DC | Downstream connection termination |
| LH | Local health check failure |
| UT | Upstream timeout |
| RL | Rate limited |
| UAEX | External authorization denied |
| RLSE | Rate limit service error |
Conditional Logging
Conditional access logging using the 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) expressions allow fine-grained control over logging conditions.
Kiali: Service Mesh Visualization
Kiali Architecture
Kiali data sources:
├── Prometheus → Metric-based service graph
├── Kubernetes API → Workload, service information
├── Istio Config API → VirtualService, DestinationRule, etc.
└── Jaeger/Tempo → Distributed traces (optional)
Information Provided by Kiali
1. Service Graph (Topology)
├── Traffic flow between services
├── Request success/failure rates
├── Requests per second
└── Response times
2. Workload Health
├── Health score based on error rate
├── Inbound/outbound metrics
└── Pod status
3. Istio Configuration Validation
├── VirtualService validity
├── DestinationRule conflict detection
├── Reference integrity (non-existent hosts, etc.)
└── Best practice violations
4. Traffic Analysis
├── Traffic trends over time
├── Error pattern identification
└── Latency distribution
Prometheus Integration
Metric Collection Configuration
Istio leverages Prometheus service discovery:
# Prometheus scrape configuration
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'
Each Envoy proxy exposes Prometheus metrics on port 15090.
Useful PromQL Queries
# Per-service request success rate (last 5 minutes)
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 latency
histogram_quantile(0.99,
sum(rate(istio_request_duration_milliseconds_bucket{
reporter="source"
}[5m])) by (le, destination_service_name)
)
# Requests per second by service
sum(rate(istio_requests_total{
reporter="destination"
}[5m])) by (destination_service_name)
Grafana Dashboards
Istio Standard Dashboards
Istio provides the following Grafana dashboards:
1. Mesh Dashboard
└── Mesh-wide summary (service count, error rate, traffic)
2. Service Dashboard
└── Per-service detail (inbound/outbound, error rate, latency)
3. Workload Dashboard
└── Per-workload detail (pod-level metrics)
4. Control Plane Dashboard
└── istiod performance (xDS pushes, response time, errors)
5. Performance Dashboard
└── Envoy resource usage (memory, CPU, connections)
Jaeger/Zipkin/Tempo Integration
Distributed Tracing Backends
Supported tracing backends:
Zipkin
├── Lightweight, simple installation
├── In-memory or Cassandra/Elasticsearch storage
└── Default Istio support
Jaeger
├── Zipkin-compatible API
├── Various storage backend support
├── Spark-based analytics
└── Recommended for production
Tempo (Grafana)
├── Object storage based (S3, GCS)
├── High scalability
├── Native Grafana integration
└── Cost-effective
Trace Data Flow
[1] Request enters the mesh
│
[2] First Envoy generates trace ID
(if no existing headers)
│
[3] Each Envoy creates spans and sends to collector
├── Zipkin: HTTP POST /api/v2/spans
├── Jaeger: UDP/gRPC
└── OTLP: gRPC (OpenTelemetry)
│
[4] Collector assembles spans into traces
│
[5] Query traces in UI
Debugging Tips
Checking Metrics
# Check Envoy metrics directly for a specific pod
kubectl exec PODNAME -c istio-proxy -- \
curl -s localhost:15090/stats/prometheus | grep istio_requests
# Check statistics via Envoy admin API
kubectl exec PODNAME -c istio-proxy -- \
curl -s localhost:15000/stats | grep -E "^cluster\."
# Envoy server info
kubectl exec PODNAME -c istio-proxy -- \
curl -s localhost:15000/server_info
Checking Tracing
# Verify trace header propagation
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))
"
Checking Access Logs
# Real-time access log viewing
kubectl logs PODNAME -c istio-proxy -f | grep -v healthz
# Filter error responses only
kubectl logs PODNAME -c istio-proxy | grep -E '"[45][0-9]{2}"'
Conclusion
Istio observability is built on Envoy proxy's rich telemetry capabilities. Key takeaways:
- Metrics: Envoy Stats filter generates standard metrics like istio_requests_total, collected by Prometheus
- Tracing: Envoy automatically creates spans, but applications must propagate trace headers for end-to-end tracing
- Logging: Envoy access logs record all requests/responses, with conditional logging via Telemetry API
- Visualization: Kiali generates service graphs based on Prometheus metrics
Through the Istio Internals series, we have explored the internals of control plane, traffic management, security, Ambient Mesh, and observability. We hope this understanding helps you effectively operate service meshes in practice.