- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに
- Envoy統計システム
- Istio標準メトリクス
- Telemetry API v2
- 分散トレーシング
- アクセスロギング
- Kiali:サービスメッシュ可視化
- Prometheus統合
- Grafanaダッシュボード
- Jaeger/Zipkin/Tempo統合
- デバッグのヒント
- まとめ
はじめに
オブザーバビリティ(Observability)はサービスメッシュの核心的価値の一つです。Istioはアプリケーションコードの修正なしにメトリクス、分散トレーシング、アクセスロギングを自動的に生成します。
この記事ではIstioがどのようにこれらのテレメトリデータを生成し、Envoyフィルターチェーンでどのような処理が行われ、外部システムとどのように統合されるかの内部実装を分析します。
Envoy統計システム
統計タイプ
Envoyは3種類の統計を生成します:
| タイプ | 説明 | 例 |
|---|---|---|
| Counter | 単調増加する値 | 総リクエスト数、総エラー数 |
| Gauge | 増加/減少可能な現在値 | アクティブ接続数、待機中リクエスト数 |
| Histogram | 値の分布 | リクエストレイテンシ、レスポンスサイズ |
フィルターチェーンでの統計生成
リクエストフローと統計生成位置:
Listener (connection stats)
|
v
HTTP Connection Manager (request stats)
|
+-- JWT Authn Filter -> 認証成功/失敗カウンター
+-- RBAC Filter -> 認可許可/拒否カウンター
+-- Fault Filter -> 注入された遅延/中断カウンター
+-- Stats Filter (istio.stats) -> Istio標準メトリクス生成
+-- Router Filter -> アップストリームリクエスト統計
|
v
Cluster (upstream stats)
|
v
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: ベンダー固有のkey=valueペア
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
+-- ワークロード別詳細(Pod単位メトリクス)
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でトレース照会
デバッグのヒント
メトリクス確認
# 特定Podの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、オブザーバビリティの内部動作を見てきました。これらの内部理解が実務でサービスメッシュを効果的に運用する際の助けになることを願っています。