Kubernetes

Istio の VirtualService と DestinationRule を理解する

Flaty

この記事で分かること

  • Kubernetes 標準のトラフィック制御の限界
  • VirtualService の役割と設定内容
  • DestinationRule の役割と設定内容
  • 2 つのリソースがどう連携するか
  • よくあるユースケースと設定例

Istio を導入しようとすると、VirtualService や DestinationRule といったリソースが登場します。公式ドキュメントには YAML の例が並んでいますが、そもそも何のために必要なのか、Kubernetes の Service とは何が違うのかがわかりにくいと感じる人も多いのではないでしょうか。

前回の記事では、Istio がサービス間通信の課題を解決するサービスメッシュであることを解説しました。この記事では、Istio のトラフィック管理の中核を担う VirtualService と DestinationRule について掘り下げます。

Kubernetes 標準のトラフィック制御の限界

まず、Istio なしの Kubernetes で何ができて、何ができないかを確認します。

Service でできること

Kubernetes の Service は、Pod へのトラフィックをロードバランシングします。ラベルセレクタで対象の Pod を選び、リクエストを均等に振り分けます。

apiVersion: v1
kind: Service
metadata:
  name: my-app
spec:
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080

これだけで、my-app という名前で Pod にアクセスでき、複数の Pod に均等にリクエストが分散されます。

Service ではできないこと

しかし、Service だけでは以下のようなことはできません。

  • 重み付きルーティング: v1 に 90%、v2 に 10% のトラフィックを流す
  • ヘッダーベースのルーティング: 特定のヘッダーがあるリクエストだけ別バージョンに振り分ける
  • リトライ・タイムアウト: 失敗時に自動で再試行する、一定時間で打ち切る
  • フォールトインジェクション: テスト目的で意図的に遅延やエラーを発生させる
  • サーキットブレーカー: 障害が連鎖しないよう、問題のあるサービスへのリクエストを遮断する

Kubernetes の Service は「どの Pod にトラフィックを送るか」は定義できますが、「どのようにトラフィックを制御するか」は定義できません。

Istio の VirtualService と DestinationRule は、この「どのように」を定義するためのリソースです。

Kubernetes Service と Istio の役割の違い

VirtualService とは

VirtualService は、トラフィックのルーティングルールを定義するリソースです。「リクエストをどこに、どう流すか」を制御します。(API リファレンス

Kubernetes の Service との違い

混同しやすいので整理します。

項目 Kubernetes Service Istio VirtualService
役割 Pod の発見とロードバランシング トラフィックのルーティングルール
ルーティング ラベルで Pod を選択、均等に分散 重み付け、ヘッダー、パスなど柔軟に制御
リトライ なし 設定可能
タイムアウト なし 設定可能
障害注入 なし 設定可能

VirtualService は Kubernetes の Service を置き換えるものではありません。Service は引き続き Pod の発見に使われ、VirtualService はその上にルーティングルールを追加します。

基本構造

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: my-app
spec:
  hosts:
    - my-app              # トラフィックの宛先(Service 名)
  http:
    - route:
        - destination:
            host: my-app  # 実際のルーティング先
            subset: v1    # DestinationRule で定義するサブセット

主なフィールドの役割:

  • hosts: この VirtualService が適用される宛先。クライアントがリクエストを送る先の Service 名を指定する
  • http: HTTP トラフィックのルーティングルール
  • route: ルーティング先の定義
  • destination: 実際のルーティング先。host(Service 名)と subset(バージョンなど)を指定する

主な機能

VirtualService で設定できる代表的な機能です。

重み付きルーティング

トラフィックの割合を指定して、複数のバージョンに振り分けます。

http:
  - route:
      - destination:
          host: my-app
          subset: v1
        weight: 90
      - destination:
          host: my-app
          subset: v2
        weight: 10

ヘッダーベースルーティング

リクエストヘッダーの値に基づいて、ルーティング先を変えます。

http:
  - match:
      - headers:
          x-test-user:
            exact: "true"
    route:
      - destination:
          host: my-app
          subset: v2
  - route:
      - destination:
          host: my-app
          subset: v1

x-test-user: true ヘッダーがあるリクエストは v2 に、それ以外は v1 にルーティングされます。

タイムアウト

リクエストの最大待ち時間を設定します。

http:
  - route:
      - destination:
          host: my-app
    timeout: 3s

リトライ

失敗時の再試行ルールを設定します。

http:
  - route:
      - destination:
          host: my-app
    retries:
      attempts: 3
      perTryTimeout: 2s
      retryOn: 5xx,reset,connect-failure

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

テスト目的で、意図的に遅延やエラーを発生させます。

http:
  - fault:
      delay:
        percentage:
          value: 10
        fixedDelay: 3s
    route:
      - destination:
          host: my-app

10% のリクエストに 3 秒の遅延を注入します。本番で障害が起きたときにシステムが正しく動くかをテストするのに使います。

DestinationRule とは

DestinationRule は、トラフィックが宛先に到達した後の振る舞いを定義するリソースです。(API リファレンス

VirtualService が「どこに流すか」を決めるのに対し、DestinationRule は「流した先でどう扱うか」を決めます。

主な役割

DestinationRule の役割は大きく 2 つです。

  1. サブセットの定義: Pod をバージョンなどのグループに分ける
  2. トラフィックポリシーの設定: 負荷分散方式、サーキットブレーカー、TLS 設定など

基本構造

apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: my-app
spec:
  host: my-app                 # 対象の Service
  subsets:
    - name: v1                 # サブセット名
      labels:
        version: v1            # Pod のラベルで振り分け
    - name: v2
      labels:
        version: v2

主なフィールドの役割:

  • host: ポリシーを適用する対象の Service
  • subsets: Pod をグループ分けする定義。ラベルで Pod を選択する
  • trafficPolicy: トラフィックに適用するポリシー(後述)。サブセット共通、またはサブセット個別に設定可能

サブセット

サブセットは、同じ Service に属する Pod をラベルでグループ分けする仕組みです。

例えば、my-app Service の Pod が version: v1version: v2 のラベルを持っている場合、サブセットで v1 と v2 のグループを定義します。VirtualService はこのサブセット名を使ってルーティング先を指定します。

サブセットの概念

主な機能

負荷分散ポリシー

サブセット内の Pod にどうリクエストを分散するかを設定します。

trafficPolicy:
  loadBalancer:
    simple: LEAST_REQUEST   # 最もリクエストが少ない Pod に振り分け

主な方式:

方式 説明
ROUND_ROBIN 順番に振り分け(デフォルト)
LEAST_REQUEST リクエスト数が最も少ない Pod に振り分け
RANDOM ランダムに振り分け
PASSTHROUGH ロードバランシングなし(元の宛先にそのまま転送)

LEAST_CONNdeprecated となり、LEAST_REQUEST に置き換えられています。

サーキットブレーカー

問題のあるサービスへのリクエストを遮断して、障害の連鎖を防ぎます。

trafficPolicy:
  connectionPool:
    tcp:
      maxConnections: 100
    http:
      h2UpgradePolicy: DEFAULT
      http1MaxPendingRequests: 10
      http2MaxRequests: 100
  outlierDetection:
    consecutive5xxErrors: 5      # 5回連続 5xx エラーで
    interval: 30s                # 30秒間隔でチェック
    baseEjectionTime: 30s        # 30秒間プールから除外
    maxEjectionPercent: 50       # 最大 50% の Pod を除外

outlierDetection は異常な Pod を自動的に検出してトラフィックの振り分け先から除外します。一定時間後に復帰させ、問題が解消しているか確認します。詳細は Circuit Breaking タスクを参照してください。

TLS 設定

サービスへの接続で使用する TLS モードを設定します。

trafficPolicy:
  tls:
    mode: ISTIO_MUTUAL   # Istio の mTLS を使用

VirtualService と DestinationRule の関係

2 つのリソースは組み合わせて使います。それぞれの役割を整理します。

リソース 役割 例え
VirtualService ルーティングルール 交通標識(どの車線に進むか)
DestinationRule 宛先のポリシーとグループ定義 道路の制限速度やレーン分け
VirtualService と DestinationRule の関係

Service との役割分担

ここで「Kubernetes の Service はどうなるのか」と疑問に思うかもしれません。VirtualService は Service を置き換えるものではなく、Service と連携して動きます。

実際のトラフィックの流れを見てみましょう。

  1. サービス A が my-app にリクエストを送る
  2. istiod が VirtualService と DestinationRule の設定を Envoy Proxy に配布する(事前に行われる)
  3. Envoy Proxy が Kubernetes Service を通じて宛先の Pod IP を取得する
  4. Envoy Proxy が VirtualService のルールに従ってトラフィックを振り分け、DestinationRule のポリシー(負荷分散方式、サーキットブレーカーなど)を適用する
Service と VirtualService/DestinationRule のトラフィックの流れ

ポイントは、Kubernetes Service は引き続きサービスディスカバリ(Pod IP の解決)を担当しているということです。VirtualService と DestinationRule は、その上にルーティングルールとポリシーを追加する形で動きます。

リソース 担当
Kubernetes Service サービスディスカバリ(DNS 名 → Pod IP の解決)
VirtualService トラフィックのルーティング(どの subset に何 % 流すか)
DestinationRule subset の定義と宛先ポリシー(負荷分散、サーキットブレーカーなど)

カナリアリリースの例

新バージョン(v2)に少しずつトラフィックを流すカナリアリリースを例に、2 つのリソースがどう連携するかを見ます。

まず、DestinationRule でサブセットを定義します。

apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: my-app
spec:
  host: my-app
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

次に、VirtualService でルーティングルールを定義します。

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: my-app
spec:
  hosts:
    - my-app
  http:
    - route:
        - destination:
            host: my-app
            subset: v1
          weight: 90
        - destination:
            host: my-app
            subset: v2
          weight: 10

リクエストの流れ:

  1. サービス A が my-app にリクエストを送る
  2. VirtualService のルールにより、90% が v1、10% が v2 にルーティングされる
  3. DestinationRule のサブセット定義により、v1 は version: v1 ラベルの Pod、v2 は version: v2 ラベルの Pod に振り分けられる

v2 が安定していることを確認したら、weight を 50:50、そして 0:100 と段階的に変更します。VirtualService の YAML を更新するだけで、アプリケーションのコード変更は不要です。具体的な手順は Traffic Shifting タスクを参照してください。

カナリアリリースの流れ

ユースケース別の使い分け

ここまでに紹介した機能を、どのような場面で使うかを整理します。

ユースケース 使う場面 主に使うリソース
カナリアリリース 新バージョンのデプロイ時にリスクを抑えたい VirtualService(weight)+ DestinationRule(subset
A/B テスト 特定のユーザーだけに新機能を試したい VirtualService(match)+ DestinationRule(subset
タイムアウト・リトライ サービス間通信の信頼性を上げたい VirtualService(timeout / retries
フォールトインジェクション 障害時の挙動をテストしたい VirtualService(fault
サーキットブレーカー 障害の連鎖を防ぎたい DestinationRule(outlierDetection
負荷分散の最適化 デフォルトの ROUND_ROBIN 以外の方式を使いたい DestinationRule(loadBalancer

Gateway API との関係

Istio のトラフィック管理には、VirtualService/DestinationRule 以外に Kubernetes Gateway API を使う方法もあります。

Gateway API は Kubernetes コミュニティが策定した標準 API で、Istio 固有のリソースではありません。Istio 1.22 以降で Stable となっており、新規導入では Gateway API の利用も選択肢になります。

項目 VirtualService / DestinationRule Gateway API
策定元 Istio プロジェクト Kubernetes コミュニティ
移植性 Istio 固有 他の実装(Envoy Gateway など)でも使える
機能の網羅性 Istio の全機能を利用可能 基本的なルーティングはカバー。一部 Istio 固有機能は未対応

現時点では、Istio 固有の高度な機能(フォールトインジェクション、詳細なリトライ設定など)を使う場合は VirtualService/DestinationRule が必要です。基本的なルーティングであれば Gateway API でも実現できます。

まとめ

  • VirtualService はトラフィックの「ルーティングルール」を定義する。重み付け、ヘッダーベースルーティング、リトライ、タイムアウトなどを設定できる
  • DestinationRule はトラフィックの「宛先ポリシー」を定義する。サブセット(バージョン分け)、負荷分散方式、サーキットブレーカーなどを設定できる
  • 2 つのリソースは組み合わせて使う。VirtualService が「どこに流すか」、DestinationRule が「流した先でどう扱うか」を担当する
  • カナリアリリース、A/B テスト、サーキットブレーカーなどのユースケースを YAML の変更だけで実現でき、アプリケーションのコード変更は不要
  • Kubernetes Gateway API という標準的な選択肢もあり、基本的なルーティングであればそちらも利用可能

参考

ABOUT ME
ぴょい
ぴょい
しがないソフトウェアエンジニアです。 学んだことをメモがわりに書いています。たまに技術以外の話も。 このブログの内容は個人の見解であり、所属組織とは関係ありません。記載内容には誤りがある可能性がありますので、公式ドキュメント等もあわせてご確認ください。
記事URLをコピーしました