Istio の VirtualService と DestinationRule を理解する
この記事で分かること
- 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 は、この「どのように」を定義するためのリソースです。

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 つです。
- サブセットの定義: Pod をバージョンなどのグループに分ける
- トラフィックポリシーの設定: 負荷分散方式、サーキットブレーカー、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: v1 と version: v2 のラベルを持っている場合、サブセットで v1 と v2 のグループを定義します。VirtualService はこのサブセット名を使ってルーティング先を指定します。

主な機能
負荷分散ポリシー
サブセット内の Pod にどうリクエストを分散するかを設定します。
trafficPolicy:
loadBalancer:
simple: LEAST_REQUEST # 最もリクエストが少ない Pod に振り分け
主な方式:
| 方式 | 説明 |
|---|---|
| ROUND_ROBIN | 順番に振り分け(デフォルト) |
| LEAST_REQUEST | リクエスト数が最も少ない Pod に振り分け |
| RANDOM | ランダムに振り分け |
| PASSTHROUGH | ロードバランシングなし(元の宛先にそのまま転送) |
※ LEAST_CONN は deprecated となり、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 | 宛先のポリシーとグループ定義 | 道路の制限速度やレーン分け |

Service との役割分担
ここで「Kubernetes の Service はどうなるのか」と疑問に思うかもしれません。VirtualService は Service を置き換えるものではなく、Service と連携して動きます。
実際のトラフィックの流れを見てみましょう。
- サービス A が
my-appにリクエストを送る - istiod が VirtualService と DestinationRule の設定を Envoy Proxy に配布する(事前に行われる)
- Envoy Proxy が Kubernetes Service を通じて宛先の Pod IP を取得する
- Envoy Proxy が 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
リクエストの流れ:
- サービス A が
my-appにリクエストを送る - VirtualService のルールにより、90% が v1、10% が v2 にルーティングされる
- 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 という標準的な選択肢もあり、基本的なルーティングであればそちらも利用可能
参考
- Istio / Traffic Management
- Istio / Virtual Service
- Istio / Destination Rule
- Istio / Request Routing (Task)
- Istio / Traffic Shifting (Task)
- Istio / Circuit Breaking (Task)
- Kubernetes Gateway API