---
title: "Istio VirtualServiceの書き方：DestinationRuleとカナリア・リトライのYAML例【1.31系】"
url: "https://www.issoh.co.jp/tech/details/18573/"
published: 2026-10-19
updated: 2026-10-19
categories: ["Kubernetes・コンテナ"]
publisher: "株式会社一創"
---

# Istio VirtualServiceの書き方：DestinationRuleとカナリア・リトライのYAML例【1.31系】

Istio VirtualServiceは、サービスメッシュ内のリクエストをどのバージョンのPodへ送るかを決めるルーティング規則です。この記事では、DestinationRuleでのsubset定義、重み付けによるカナリアリリース、ヘッダー一致の振り分け、timeoutとretriesの設定、異常なPodの自動切り離しまでを、Istio 1.31系の公式ドキュメントに沿ったYAMLで示します。設定が効かないときの確認手順と、Gateway APIのHTTPRouteとどちらを選ぶかの判断も扱います。

## まとめ：VirtualServiceは振り分けの規則でDestinationRuleは宛先の定義

VirtualServiceには「どの条件のリクエストを、どの宛先へ、どの割合で送るか」を書きます。その宛先にあたるv1やv2といったバージョンの区切り（subset）と、負荷分散や接続数の上限は、DestinationRuleの役目です。2つは対で使い、VirtualServiceが参照するsubsetがDestinationRuleに無ければ通信は届きません。

カナリアリリースは、2つのsubsetへ`weight`で割合を振り、数値を書き換えて段階的に寄せていく形が基本です。リトライはIstioが既定で2回行うため、アプリ側にも再試行があるなら回数が掛け算にならないよう`retries`を明示してください。

新規にメッシュを組むならGateway APIのHTTPRouteも候補に入ります。VirtualServiceを選ぶのは、サイドカー方式で動かし、フォールト注入やsubset単位の接続設定まで1か所で管理したい場合です。

## VirtualServiceとDestinationRuleの役割とv1 APIの基本形

Istio全体の仕組みは[Istioとは？サービスメッシュの仕組みとサイドカー/アンビエント方式・導入判断を解説](https://www.issoh.co.jp/tech/details/15568/)で扱っています。ここではルーティングを担う2リソースに絞り、リクエストの行き先を決める規則と、行き先となるsubsetの定義を分けて説明します。

### VirtualServiceの経路指定とDestinationRuleのsubset定義

Kubernetesの`Service`だけでは、同じServiceの裏にあるv1とv2のPodへ割合を決めて送り分けられません。Istioでは、DestinationRuleがPodのラベルでsubsetを定義し、VirtualServiceがそのsubsetを名指しして経路を決めます。

```
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews.default.svc.cluster.local
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
```

Deployment側のPodテンプレートに`version: v1`などのラベルが付いていることが前提です。[DestinationRuleのリファレンス](https://istio.io/latest/docs/reference/config/networking/destination-rule/)では、subsetごとに負荷分散などのtrafficPolicyを上書きできると定義されています。

### networking.istio.io/v1のapiVersionとFQDNのhosts

日本語の解説記事には`v1alpha3`や`v1beta1`のYAMLが今も多く残っています。Istio 1.31系の公式タスクはすべて`networking.istio.io/v1`で書かれているため、新しく書くならv1にそろえます。

```
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews.default.svc.cluster.local
  http:
  - route:
    - destination:
        host: reviews.default.svc.cluster.local
        subset: v1
```

`hosts`に`reviews`のような短い名前を書くと、[VirtualServiceのリファレンス](https://istio.io/latest/docs/reference/config/networking/virtual-service/)の注記どおり、Serviceではなくルールを置いたnamespaceを基準に解釈されます。別のnamespaceにVirtualServiceを置いた途端に効かなくなる事故を避けるため、公式はFQDNでの記述を勧めています。

## DestinationRuleのsubsetとweightによる段階的なカナリアリリース

新バージョンを一部のリクエストだけで試し、問題が無ければ割合を上げていく方式です。方式そのものの比較は[カナリアリリースとは？段階リリースの仕組み・他方式との違い・実装と採用判断を実装視点で解説](https://www.issoh.co.jp/tech/details/13592/)を参照してください。

### weightを90対10で始めて50対50へ寄せていくYAMLと適用手順

[公式のTraffic Shiftingタスク](https://istio.io/latest/docs/tasks/traffic-management/traffic-shifting/)と同じ形で、v1に90、v2に10を振ります。

```
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews.default.svc.cluster.local
  http:
  - route:
    - destination:
        host: reviews.default.svc.cluster.local
        subset: v1
      weight: 90
    - destination:
        host: reviews.default.svc.cluster.local
        subset: v2
      weight: 10
```

```
kubectl apply -f reviews-dr.yaml
kubectl apply -f reviews-vs-canary.yaml

# 反映を確かめる
kubectl get virtualservice reviews -o yaml
```

割合を上げるときは`weight`を50と50、0と100のように書き換えて再度applyします。GitOpsで運用しているならこのYAMLの差分がそのままリリース記録になり、戻すときも1コミットで済みます。

### weightの合計が100でなくても按分される現行仕様と0の意味

古い解説には「weightは合計100にする」と書かれたものが多く見られます。現行のリファレンスは、各宛先が受け取る量を「自分のweight÷全weightの合計」と定義しています。1対1でも50対50と同じ配分になる仕様です。

宛先が1つだけならweightに関係なく全量がそこへ流れ、複数の宛先のうちweightが0のものには1件も流れません。切り戻しはv2のweightを0にするだけで済み、v2の定義を消す必要はありません。読み手が迷わないよう、運用では合計100で書くのが無難です。

### Gateway APIのHTTPRouteで書く場合はバージョン別のServiceが必要

Traffic Shiftingタスクには、Gateway APIで同じ配分を書く例も並んでいます。HTTPRouteにはsubsetの概念が無く、`reviews-v1`と`reviews-v2`のようにバージョンごとのServiceを作って`backendRefs`に並べる方式です。

```
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: reviews
spec:
  parentRefs:
  - group: ""
    kind: Service
    name: reviews
    port: 9080
  rules:
  - backendRefs:
    - name: reviews-v1
      port: 9080
      weight: 90
    - name: reviews-v2
      port: 9080
      weight: 10
```

`parentRefs`に`Service`を指定すると、メッシュ内の通信に対するルートになります。外部からの入口に付ける書き方は[Istio Gatewayの設定方法：Ingress GatewayのYAML・TLS終端・Gateway API対応](https://www.issoh.co.jp/tech/details/18571/)で扱っています。

## ヘッダーとURIの一致条件で振り分けるルーティング規則と評価の順序

割合ではなく条件で振り分けると、社内の検証者だけに新バージョンを見せる運用ができます。

### end-userヘッダーが一致した利用者だけをv2へ送るmatchの書き方

[公式のRequest Routingタスク](https://istio.io/latest/docs/tasks/traffic-management/request-routing/)は、`end-user`ヘッダーが`jason`のリクエストだけをv2へ送り、残りをv1へ送る例を示しています。

```
  http:
  - match:
    - headers:
        end-user:
          exact: jason
    route:
    - destination:
        host: reviews.default.svc.cluster.local
        subset: v2
  - route:
    - destination:
        host: reviews.default.svc.cluster.local
        subset: v1
```

ヘッダーのほか、`uri`の`prefix`や`exact`、`queryParams`でも一致条件を書けます。ヘッダーは呼び出し元のサービスが次の呼び出しへ引き継がないと途中で消えるため、多段の呼び出しで使うならアプリ側の伝播処理を先に確認してください。

### 上から順に評価される規則と条件なしの既定ルートを最後に置く理由

[Istioのトラフィック管理の解説](https://istio.io/latest/docs/concepts/traffic-management/)によると、規則は上から順に評価され、最初に一致したものが使われます。条件の無いルートを先頭に置くと、後ろのmatchは二度と評価されません。

公式は、どの条件にも一致しないリクエストの行き先として、条件なしの既定ルートを最後に置くよう勧めています。既定ルートが無いと、一致しなかったリクエストは送り先を失います。

## timeout・retries・outlierDetectionの併用による障害対策

ルーティングの次に設定するのが、宛先が遅い・落ちたときの振る舞いです。値は公式リファレンスの既定値を基準に、サービスごとに決めます。

### 既定で2回行われるリトライとretriesで回数と条件を明示する例

VirtualServiceのリファレンスによると、`retries`を書かない場合でもメッシュ全体の既定として`attempts: 2`、`retryOn: connect-failure,refused-stream,unavailable,cancelled`のリトライがかかります。一方で`timeout`の既定は無効です。

```
  http:
  - route:
    - destination:
        host: reviews.default.svc.cluster.local
        subset: v1
    timeout: 3s
    retries:
      attempts: 2
      perTryTimeout: 1s
      retryOn: connect-failure,refused-stream,unavailable,cancelled,503
```

この例では、1回あたり1秒で打ち切り、最大で初回＋2回の計3回まで試します。全体の`timeout: 3s`を超えた時点で残りのリトライは行われません。リトライの間隔は25ミリ秒を基準にした指数バックオフです。アプリ側のHTTPクライアントにも3回の再試行があれば、最悪で3×3＝9回の呼び出しが宛先に届く計算になります。

### retryOnに書いた503が接続リセットには一致しない落とし穴

`retryOn`には`503`のようにHTTPステータスを足せますが、一致するのは宛先が実際に返した応答だけです。リファレンスは、接続がリセットされた場合にIstioが呼び出し元へ503を返しても、それは宛先の503ではないため`"503"`には一致しないと注記しています。

接続リセットでも再試行したいなら`reset`を併記します。また、注文登録のような冪等でないPOSTに`5xx`系の条件を付けると、処理済みの注文が二重に登録されかねません。書き込み系の経路は既定の接続失敗系だけに絞り、5xxでの再試行は読み取り系の経路に限ってください。

### outlierDetectionで5xxを返すPodを一時的に外す設定値

リトライしても同じ不調なPodに当たり続けては意味がありません。DestinationRuleの`outlierDetection`は、連続でエラーを返したPodを負荷分散の対象から一時的に外します。[公式のCircuit Breakingタスク](https://istio.io/latest/docs/tasks/traffic-management/circuit-breaking/)は接続数の上限と組み合わせた例です。

```
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews.default.svc.cluster.local
  trafficPolicy:
    connectionPool:
      http:
        http1MaxPendingRequests: 100
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 10s
      baseEjectionTime: 30s
      maxEjectionPercent: 50
  subsets:
  - name: v1
    labels:
      version: v1
```

リファレンス上の既定値は、`consecutive5xxErrors`が5、`interval`が10秒、`baseEjectionTime`が30秒、`maxEjectionPercent`が10%です。既定の10%では、Podが3台程度のサービスで同時に外せるのは1台に限られるため、上の例では50に上げています。外れている時間は「基準時間×外された回数」で延びていきます。

## 設定が効かないときのistioctlでの確認とGateway APIとの選び分け

VirtualServiceはapplyしてもエラーにならずに無視されることがあります。確認の順番と、APIの選び方を条件付きで示します。

### istioctl analyzeと到達しない規則の検出からたどる確認の順番

次の順で見ると、原因の多くに当たります。

1. `istioctl analyze -n default`で設定を検査する。後ろの規則に到達しない場合の`VirtualServiceUnreachableRule`や、効かない一致条件を示す`VirtualServiceIneffectiveMatch`が出ていないか見る
2. `kubectl get pods -l app=reviews --show-labels`で、subsetの`labels`と同じラベルがPodに付いているか確かめる
3. `istioctl proxy-config routes deploy/productpage`で、呼び出し元のEnvoyに規則が届いているかを見る
4. 入口から来る通信なら、VirtualServiceの`gateways`欄を確かめる

4つ目は見落としやすい点です。リファレンスでは、`gateways`を省くとメッシュ内のサイドカーすべて（`mesh`）に適用され、Gateway名だけを並べるとそのGatewayにしか効きません。入口とメッシュ内の両方に効かせるなら、`mesh`も並べて書きます。

### サイドカー方式ならVirtualServiceでアンビエント方式ならHTTPRoute

判断の軸はデータプレーンの方式です。サイドカー方式で運用していて、フォールト注入やsubset単位の接続設定まで使うなら、VirtualServiceとDestinationRuleを選んでください。タスクや解説の蓄積も、こちらの方が厚い状況です。

アンビエント方式を採るなら、HTTPRouteで書きます。[アンビエント方式のL7機能のページ](https://istio.io/latest/docs/ambient/usage/l7-features/)は、アンビエントでのVirtualServiceをAlpha扱いとし、Gateway APIの設定と混ぜた場合の挙動は未定義と明記しています。同じサービスに両方を書く構成は、どちらの方式でも避けてください。

サービスが2〜3本で、割合での切り替えを月に数回する程度なら、Istioを入れずにArgo RolloutsやクラウドのALBの重み付けで足りる場面もあります。リリースの段階制御とメトリクス監視をパイプラインに組み込むところまで進めるなら、[DevOps・CI/CD導入支援](https://www.issoh.co.jp/service/system/devops/)のように、デプロイの自動化と合わせて設計を任せる方法もあります。

## Istio VirtualServiceの設定と運用で挙がるよくある質問への回答集

VirtualServiceを書き始めたときに出やすい疑問に、公式ドキュメントの記述をもとに答えます。

### DestinationRuleなしでVirtualServiceだけを使えますか？

使えます。subsetを使わず、Service単位で`timeout`や`retries`、ヘッダー一致の振り分けをするだけならVirtualServiceだけで動きます。DestinationRuleが要るのは、`subset`でバージョンを分けるときと、負荷分散方式や接続数の上限、outlierDetectionを設定するときです。subsetを参照するVirtualServiceを先にapplyすると、DestinationRuleが届くまで通信が失敗するため、適用はDestinationRuleを先にします。

### 同じホストに複数のVirtualServiceを書いてもよいですか？

書けますが、条件付きです。リファレンスは、1つのホストのトラフィック設定を複数のVirtualServiceで定義できるとしつつ、注意点を運用ガイドに分けて記しています。規則の評価順が分かりにくくなるため、チームで分担したい場合は親のVirtualServiceから`delegate`で子へ委ねる方式も用意されています。まずは1ホスト1リソースで始めるのが安全です。

### VirtualServiceで障害を意図的に起こして試せますか？

できます。`fault`欄に`delay`や`abort`を書くと、指定した割合のリクエストに遅延やエラー応答を注入できます。[公式のRequest Timeoutsタスク](https://istio.io/latest/docs/tasks/traffic-management/request-timeouts/)は、ratingsに2秒の遅延を入れ、reviewsの`timeout: 0.5s`で打ち切られることを確かめる流れです。リトライやタイムアウトの値を決める前に、検証環境で同じ手順を踏むと設定の効き方を目で確認できます。

### weightを変えたのに配分が変わらないのはなぜですか？

多いのは、呼び出し元にサイドカーが入っていないケースです。VirtualServiceの規則は呼び出し元側のEnvoyで適用されるため、メッシュ外のPodから直接Serviceを呼ぶと割合は効きません。次に、配分はリクエストごとの確率で決まるため、数十件程度では比率がぶれて見えます。数百件以上のリクエストを流してから、メトリクスで配分を確かめてください。

### Istioのバージョンを上げるとVirtualServiceの書き換えが要りますか？

v1で書いていれば、1.31系への更新で書き換えは要りません。[Istioのサポート表](https://istio.io/latest/docs/releases/supported-releases/)では2026年10月時点で1.31系が現行（最新パッチは1.31.2）です。ただし`v1alpha3`のまま残っているマニフェストは、更新の機会にv1へ直しておくと後の移行が楽になります。版ごとの差分は[Istioの最新バージョンは1.31｜1.28〜1.31の新機能・サポート期限とアップグレード手順](https://www.issoh.co.jp/tech/details/9698/)にまとめています。

## 関連記事

- [Istioとは？サービスメッシュの仕組みとサイドカー/アンビエント方式・導入判断を解説](https://www.issoh.co.jp/tech/details/15568/)：Istio全体の仕組みと導入判断
- [Istio Gatewayの設定方法：Ingress GatewayのYAML・TLS終端・Gateway API対応](https://www.issoh.co.jp/tech/details/18571/)：外部からの入口をVirtualServiceへつなぐ設定
- [カナリアリリースとは？段階リリースの仕組み・他方式との違い・実装と採用判断を実装視点で解説](https://www.issoh.co.jp/tech/details/13592/)：段階リリースの方式比較と採用判断
- [サービスメッシュとは？サイドカー方式の仕組みとAPIゲートウェイとの違い・導入判断を解説](https://www.issoh.co.jp/tech/details/13224/)：サービスメッシュの位置づけ
- [Linkerdとは？軽量なRust製サービスメッシュの仕組みとIstioとの選び分けを解説](https://www.issoh.co.jp/tech/details/15570/)：より軽量なメッシュとの比較
- [Istio AuthorizationPolicyの書き方：ALLOW・DENYの評価順とmTLS前提のYAML例【1.31系】](https://www.issoh.co.jp/tech/details/18575/)：サービス間の通信を許可・拒否する認可の設定

---

出典: [Istio VirtualServiceの書き方：DestinationRuleとカナリア・リトライのYAML例【1.31系】](<https://www.issoh.co.jp/tech/details/18573/>)（株式会社一創）
