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とは?サービスメッシュの仕組みとサイドカー/アンビエント方式・導入判断を解説で扱っています。ここではルーティングを担う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のリファレンスでは、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のリファレンスの注記どおり、Serviceではなくルールを置いたnamespaceを基準に解釈されます。別のnamespaceにVirtualServiceを置いた途端に効かなくなる事故を避けるため、公式はFQDNでの記述を勧めています。
DestinationRuleのsubsetとweightによる段階的なカナリアリリース
新バージョンを一部のリクエストだけで試し、問題が無ければ割合を上げていく方式です。方式そのものの比較はカナリアリリースとは?段階リリースの仕組み・他方式との違い・実装と採用判断を実装視点で解説を参照してください。
weightを90対10で始めて50対50へ寄せていくYAMLと適用手順
公式の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対応で扱っています。
ヘッダーとURIの一致条件で振り分けるルーティング規則と評価の順序
割合ではなく条件で振り分けると、社内の検証者だけに新バージョンを見せる運用ができます。
end-userヘッダーが一致した利用者だけをv2へ送るmatchの書き方
公式の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のトラフィック管理の解説によると、規則は上から順に評価され、最初に一致したものが使われます。条件の無いルートを先頭に置くと、後ろの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タスクは接続数の上限と組み合わせた例です。
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と到達しない規則の検出からたどる確認の順番
次の順で見ると、原因の多くに当たります。
istioctl analyze -n defaultで設定を検査する。後ろの規則に到達しない場合のVirtualServiceUnreachableRuleや、効かない一致条件を示すVirtualServiceIneffectiveMatchが出ていないか見るkubectl get pods -l app=reviews --show-labelsで、subsetのlabelsと同じラベルがPodに付いているか確かめるistioctl proxy-config routes deploy/productpageで、呼び出し元のEnvoyに規則が届いているかを見る- 入口から来る通信なら、VirtualServiceの
gateways欄を確かめる
4つ目は見落としやすい点です。リファレンスでは、gatewaysを省くとメッシュ内のサイドカーすべて(mesh)に適用され、Gateway名だけを並べるとそのGatewayにしか効きません。入口とメッシュ内の両方に効かせるなら、meshも並べて書きます。
サイドカー方式ならVirtualServiceでアンビエント方式ならHTTPRoute
判断の軸はデータプレーンの方式です。サイドカー方式で運用していて、フォールト注入やsubset単位の接続設定まで使うなら、VirtualServiceとDestinationRuleを選んでください。タスクや解説の蓄積も、こちらの方が厚い状況です。
アンビエント方式を採るなら、HTTPRouteで書きます。アンビエント方式のL7機能のページは、アンビエントでのVirtualServiceをAlpha扱いとし、Gateway APIの設定と混ぜた場合の挙動は未定義と明記しています。同じサービスに両方を書く構成は、どちらの方式でも避けてください。
サービスが2〜3本で、割合での切り替えを月に数回する程度なら、Istioを入れずにArgo RolloutsやクラウドのALBの重み付けで足りる場面もあります。リリースの段階制御とメトリクス監視をパイプラインに組み込むところまで進めるなら、DevOps・CI/CD導入支援のように、デプロイの自動化と合わせて設計を任せる方法もあります。
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タスクは、ratingsに2秒の遅延を入れ、reviewsのtimeout: 0.5sで打ち切られることを確かめる流れです。リトライやタイムアウトの値を決める前に、検証環境で同じ手順を踏むと設定の効き方を目で確認できます。
weightを変えたのに配分が変わらないのはなぜですか?
多いのは、呼び出し元にサイドカーが入っていないケースです。VirtualServiceの規則は呼び出し元側のEnvoyで適用されるため、メッシュ外のPodから直接Serviceを呼ぶと割合は効きません。次に、配分はリクエストごとの確率で決まるため、数十件程度では比率がぶれて見えます。数百件以上のリクエストを流してから、メトリクスで配分を確かめてください。
Istioのバージョンを上げるとVirtualServiceの書き換えが要りますか?
v1で書いていれば、1.31系への更新で書き換えは要りません。Istioのサポート表では2026年10月時点で1.31系が現行(最新パッチは1.31.2)です。ただしv1alpha3のまま残っているマニフェストは、更新の機会にv1へ直しておくと後の移行が楽になります。版ごとの差分はIstioの最新バージョンは1.31|1.28〜1.31の新機能・サポート期限とアップグレード手順にまとめています。
関連記事
- Istioとは?サービスメッシュの仕組みとサイドカー/アンビエント方式・導入判断を解説:Istio全体の仕組みと導入判断
- Istio Gatewayの設定方法:Ingress GatewayのYAML・TLS終端・Gateway API対応:外部からの入口をVirtualServiceへつなぐ設定
- カナリアリリースとは?段階リリースの仕組み・他方式との違い・実装と採用判断を実装視点で解説:段階リリースの方式比較と採用判断
- サービスメッシュとは?サイドカー方式の仕組みとAPIゲートウェイとの違い・導入判断を解説:サービスメッシュの位置づけ
- Linkerdとは?軽量なRust製サービスメッシュの仕組みとIstioとの選び分けを解説:より軽量なメッシュとの比較
- Istio AuthorizationPolicyの書き方:ALLOW・DENYの評価順とmTLS前提のYAML例【1.31系】:サービス間の通信を許可・拒否する認可の設定