Skip to content
Published on

Istioトラフィック管理エンジン深層分析

Authors

はじめに

Istioのトラフィック管理は試験範囲の40%を占めるほど中核的な領域です。この記事では、Istio CRDがどのようにEnvoy構成に変換され、Envoyが実際にトラフィックをどう処理するかの内部メカニズムを分析します。

VirtualServiceからEnvoy RouteConfigurationへ

変換パイプライン

VirtualService (Istio CRD)
Pilot変換エンジン
Envoy Route Configuration
  ├── VirtualHost(ホストベースマッチング)
  │   ├── Route(パス/ヘッダーマッチング)
  │   │   ├── RouteAction(ルーティング先)
  │   │   ├── WeightedCluster(重み付き分割)
  │   │   └── RetryPolicy(リトライポリシー)
  │   └── Route(デフォルトパス)
  └── VirtualHost(他のホスト)

マッチ優先順位

VirtualServiceのHTTPマッチルールは定義順に評価されます。EnvoyのRouteConfigurationでも同じ順序が維持されます:

  1. 最も具体的なマッチが先(exact > prefix > regex)
  2. 複数のmatchブロックがある場合、最初のマッチが適用
  3. マッチ条件のないrouteはcatch-allとして動作

トラフィックシフティング

重み付きトラフィック分割の内部実装:

# Istio VirtualService
spec:
  http:
    - route:
        - destination:
            host: reviews
            subset: v1
          weight: 75
        - destination:
            host: reviews
            subset: v2
          weight: 25

これがEnvoyではWeightedClusterに変換されます:

{
  "route": {
    "weighted_clusters": {
      "clusters": [
        {
          "name": "outbound|9080|v1|reviews.default.svc.cluster.local",
          "weight": 75
        },
        {
          "name": "outbound|9080|v2|reviews.default.svc.cluster.local",
          "weight": 25
        }
      ],
      "total_weight": 100
    }
  }
}

Envoyは各リクエストごとに乱数を生成し、重み範囲に応じてクラスターを選択します。これは確率的分割なので、正確に75:25ではなく近似値で動作します。

DestinationRuleからEnvoy Cluster Configurationへ

Cluster構成変換

DestinationRuleはEnvoyのCluster構成に変換されます:

DestinationRule
  ├── host → Cluster名プレフィックス
  ├── subsets → 個別Cluster生成
  │   ├── subset v1 → outbound|9080|v1|reviews.default.svc.cluster.local
  │   └── subset v2 → outbound|9080|v2|reviews.default.svc.cluster.local
  └── trafficPolicy → Clusterレベル設定
      ├── connectionPool → circuit_breakers
      ├── outlierDetection → outlier_detection
      └── loadBalancer → lb_policy

Cluster命名規則

Envoy Clusterの名前は以下の形式に従います:

方向|ポート|サブセット|FQDN

例:
outbound|9080|v1|reviews.default.svc.cluster.local
inbound|8080||productpage.default.svc.cluster.local

ロードバランシングアルゴリズム

Round Robin(デフォルト)

リクエスト1Endpoint A
リクエスト2Endpoint B
リクエスト3Endpoint C
リクエスト4Endpoint A(循環)

Envoy実装:各エンドポイントに順番にリクエストを分配します。重みがある場合はWeighted Round Robinとして動作します。

Least Connections

Endpoint A: アクティブ接続3Endpoint B: アクティブ接続1個  ← 次のリクエストはここへ
Endpoint C: アクティブ接続5

Envoy実装:O(1)演算で最も少ないアクティブリクエストを持つエンドポイントを選択します。

Random

各リクエストごとにランダムにエンドポイントを選択
大規模では統計的に均等分配

Consistent Hash

セッションアフィニティが必要な場合に使用します:

spec:
  trafficPolicy:
    loadBalancer:
      consistentHashLB:
        httpHeaderName: x-user-id

Envoy実装:Ketamaハッシュアルゴリズムベース。ハッシュリングにエンドポイントを配置し、リクエストキーをハッシュして最も近いエンドポイントを選択します。

ハッシュリング:
   0 ─── EP_A ─── EP_B ─── EP_C ─── MAX
         │              │
    x-user-id:alice  x-user-id:bob
    (常にEP_A)       (常にEP_B)

利点:

  • エンドポイント追加/削除時の最小限の再マッピング
  • 同じキーは常に同じエンドポイントへ(スティッキーセッション)

サーキットブレーカー実装

DestinationRule outlierDetectionからEnvoyへの変換

# Istio DestinationRule
spec:
  trafficPolicy:
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 10s
      baseEjectionTime: 30s
      maxEjectionPercent: 50

Envoyでの動作:

{
  "outlier_detection": {
    "consecutive_5xx": 5,
    "interval": "10s",
    "base_ejection_time": "30s",
    "max_ejection_percent": 50,
    "enforcing_consecutive_5xx": 100
  }
}

サーキットブレーカー状態マシン

[Closed] ──── 連続5xx >= 5 ────→ [Open/Ejected]
   ▲                                    │
   │                              baseEjectionTime経過
   │                                    │
   └──────── 成功リクエスト ──────── [Half-Open]
                                   (リトライ)

詳細動作:

  1. Closed:正常状態。すべてのリクエストがエンドポイントに転送
  2. Open(Ejected):連続エラー閾値超過。エンドポイントがロードバランシングプールから除外
  3. Half-Open:baseEjectionTime後にエンドポイントがプールに復帰。再失敗時はejection時間が増加

接続プールベースのサーキットブレーカー

spec:
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        h2UpgradePolicy: DEFAULT
        http1MaxPendingRequests: 1024
        http2MaxRequests: 1024
        maxRequestsPerConnection: 10
        maxRetries: 3

Envoy内部動作:

リクエスト到着
maxConnections (100) 超過? ──→ 503 (overflow)
No
http1MaxPendingRequests (1024) 超過? ──→ 503 (overflow)
No
http2MaxRequests (1024) 超過? ──→ 503 (overflow)
No
リクエスト処理開始
maxRequestsPerConnection (10) 到達? ──→ 接続終了後新規接続

リトライ実装

VirtualService retriesからEnvoyへの変換

spec:
  http:
    - route:
        - destination:
            host: reviews
      retries:
        attempts: 3
        perTryTimeout: 2s
        retryOn: gateway-error,connect-failure,retriable-4xx

Envoyリトライポリシー:

{
  "retry_policy": {
    "retry_on": "gateway-error,connect-failure,retriable-4xx",
    "num_retries": 3,
    "per_try_timeout": "2s",
    "retry_host_predicate": [
      {
        "name": "envoy.retry_host_predicates.previous_hosts"
      }
    ]
  }
}

リトライ動作詳細

元のリクエスト → Endpoint A(失敗、503リトライ1Endpoint B(失敗、503)  ← previous_hostsでA回避
リトライ2Endpoint C(失敗、503)  ← AB回避
リトライ3Endpoint A(成功、200)  ← 候補枯渇時は再利用
最終レスポンス:200

ポイント:

  • previous_hosts:以前に失敗したホストを回避して別のエンドポイントにリトライ
  • perTryTimeout:各試行ごとのタイムアウト(全体タイムアウトとは別)
  • リトライバジェット:Envoy内部で同時リトライ数を制限

retryOn条件詳細

条件Envoy動作
5xxアップストリームが5xxレスポンスを返した場合
gateway-error502、503、504レスポンス時
connect-failureTCP接続失敗時
retriable-4xx409(Conflict)など特定の4xx
refused-streamアップストリームがREFUSED_STREAMエラーコードでストリームリセット時
resetレスポンスなしで接続がリセットされた場合

フォールトインジェクション実装

遅延注入(Delay Injection)

spec:
  http:
    - fault:
        delay:
          percentage:
            value: 10
          fixedDelay: 5s
      route:
        - destination:
            host: reviews

Envoy実装:envoy.filters.http.faultフィルターがHTTPフィルターチェーンに挿入されます。

リクエスト到着
Fault Filter:乱数生成(0-100    ├── 乱数 <= 105秒遅延後次のフィルターへ転送
    └── 乱数 > 10 → 即座に次のフィルターへ転送
Router Filter → アップストリームへ転送

中断注入(Abort Injection)

spec:
  http:
    - fault:
        abort:
          percentage:
            value: 20
          httpStatus: 503
      route:
        - destination:
            host: reviews

Envoy実装:

リクエスト到着
Fault Filter:乱数生成(0-100    ├── 乱数 <= 20 → 即座に503レスポンスを返却(アップストリームへリクエストしない)
    └── 乱数 > 20 → 次のフィルターへ転送

複合フォールトインジェクション

遅延と中断を同時に適用できます:

spec:
  http:
    - fault:
        delay:
          percentage:
            value: 50
          fixedDelay: 3s
        abort:
          percentage:
            value: 10
          httpStatus: 500

評価順序:遅延が先に適用され、その後中断が評価されます。

トラフィックミラーリング実装

VirtualService mirrorからEnvoyへの変換

spec:
  http:
    - route:
        - destination:
            host: reviews
            subset: v1
      mirror:
        host: reviews
        subset: v2
      mirrorPercentage:
        value: 100

Envoy実装:

リクエスト到着
    ├── 元のリクエスト → reviews v1(レスポンスをクライアントに返却)
    └── ミラーリクエスト → reviews v2(レスポンス無視、fire-and-forget)
         └── Hostヘッダーに"-shadow"サフィックス追加
             例:reviews → reviews-shadow

主な特徴:

  • ミラーリクエストのレスポンスは完全に無視
  • ミラーリクエストの失敗が元のリクエストに影響しない
  • Hostヘッダーに"-shadow"が追加されてミラートラフィックを識別可能

ServiceEntry:メッシュ拡張

外部サービス登録

apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: external-api
spec:
  hosts:
    - api.external-service.com
  location: MESH_EXTERNAL
  ports:
    - number: 443
      name: https
      protocol: TLS
  resolution: DNS

Envoyで生成されるリソース:

ServiceEntry
    ├── Listener: 0.0.0.0:443(アウトバウンド)にフィルターチェーン追加
    ├── Cluster: outbound|443||api.external-service.com
    │   ├── type: STRICT_DNS
    │   └── dns_lookup_family: V4_ONLY
    └── Route: api.external-service.com → 該当Clusterにルーティング

Resolutionモード別動作

ResolutionEnvoy Cluster Type動作
NONEORIGINAL_DST元のリクエストアドレスに転送
STATICSTATICendpointsフィールドのIPを直接使用
DNSSTRICT_DNSDNSで周期的に解決
DNS_ROUND_ROBINLOGICAL_DNSDNS結果の中から1つをラウンドロビン

高度なルーティングパターン

ヘッダーベースのカナリアデプロイ

spec:
  http:
    - match:
        - headers:
            x-canary:
              exact: 'true'
      route:
        - destination:
            host: reviews
            subset: v2
    - route:
        - destination:
            host: reviews
            subset: v1

URIベースのルーティング

spec:
  http:
    - match:
        - uri:
            prefix: '/api/v2'
      route:
        - destination:
            host: api-v2
    - match:
        - uri:
            prefix: '/api'
      route:
        - destination:
            host: api-v1

ソースベースのルーティング

spec:
  http:
    - match:
        - sourceLabels:
            app: frontend
            version: v2
      route:
        - destination:
            host: reviews
            subset: v2
    - route:
        - destination:
            host: reviews
            subset: v1

デバッグツール

istioctlコマンド集

# VirtualService適用状態の確認
istioctl analyze -n NAMESPACE

# 特定サービスのルーティング構成確認
istioctl proxy-config routes PODNAME -o json | python3 -m json.tool

# Envoyのクラスター統計確認
istioctl proxy-config clusters PODNAME --fqdn reviews.default.svc.cluster.local

# サーキットブレーカー状態確認(Envoy管理ポート)
kubectl exec PODNAME -c istio-proxy -- curl -s localhost:15000/clusters | grep outlier

# リトライ統計
kubectl exec PODNAME -c istio-proxy -- curl -s localhost:15000/stats | grep retry

一般的な問題パターン

  1. 503 UC(Upstream Connection):アップストリーム接続失敗 - connectionPool設定を確認
  2. 503 UO(Upstream Overflow):サーキットブレーカートリガー - maxConnections/maxPendingRequestsを確認
  3. 503 NR(No Route):ルートマッチング失敗 - VirtualServiceのhost/matchを確認
  4. 503 UH(Upstream Unhealthy):すべてのエンドポイントがejected - outlierDetection設定を確認

まとめ

Istioのトラフィック管理エンジンは、結局Envoyの強力なプロキシ機能の上に構築されています。Istio CRDはユーザーフレンドリーな抽象化であり、実際の動作はEnvoyのリスナー、ルート、クラスター、エンドポイント構成によって決定されます。

トラフィック管理の問題をデバッグする際は、常に最終的にEnvoyに配信された構成を確認することが鍵です。istioctl proxy-configコマンドを習得すれば、ほとんどのトラフィック問題を迅速に診断できます。

次の記事では、Istioセキュリティモデルの内部実装を見ていきます。