トラフィックレーン(例:ロースモードおよびストリクトモードのトラフィックレーン)を用いてカナリアリリースを実行する場合、呼び出しチェーン上のすべてのアプリケーションは同一のリリーススケジュールを共有します。しかし実際には、異なるアプリケーションがそれぞれ独立したカナリア比率を必要とするケースが多く見られます。たとえば、2 つのアプリケーションが新機能をリリースする際に 10 % のユーザーを対象とする一方で、3 つ目のアプリケーションがバグ修正をリリースするにあたり 50 % のユーザーに到達する必要がある — このような状況が、同一の呼び出しチェーン上で同時に発生することがあります。
Alibaba Cloud Service Mesh (ASM) では、この課題をハッシュタグ付けプラグインによって解決します。このプラグインは、ゲートウェイにおいて各リクエストに複数のタグを割り当てます。さらに、タグ伝搬のための ASMHeaderPropagation CRD およびタグに基づくマッチングを行う Istio ルーティングルールと組み合わせることで、呼び出しチェーン上の各アプリケーションが、それぞれ独自のカナリア比率に基づいて独立してトラフィックをルーティングできます。
Kubernetes ネイティブのカナリアリリースがレプリカ比率に依存するのとは異なり、ASM ではトラフィックルーティングと Pod のスケーリングが分離されています。カナリア比率はルーティングルールによって制御され、各バージョンが実行するレプリカ数とは無関係です。このため、10 % のユーザーをカナリアバージョンにルーティングする場合でも、正確に「10 分の 1」の数だけ Pod をプロビジョニングする必要はありません。
仕組み
標準的なトラフィックレーン構成では、単一のタグによって呼び出しチェーン上のすべてのアプリケーションがどのバージョンを提供するかが決定されます。独立したカナリア比率を実現するため、ハッシュタグ付けプラグインは各アプリケーションごとに個別のタグヘッダーを追加します(例: appver-a、appver-b、appver-c)。各タグには、カナリアバージョンへルーティングされるユーザーの割合を制御するための独自のハッシュ範囲が設定されます。
エンドツーエンドの処理フローは以下のとおりです:
クライアントがユーザー識別子(例:
x-user-idヘッダー)を含むリクエストを送信します。ASM ゲートウェイ上のハッシュタグ付けプラグインが、その識別子をハッシュ化し、モジュロ(例: 100)で除算した余剰値を計算し、各タグの設定済み範囲内に該当するかを判定します。
範囲に一致した各タグについて、プラグインはリクエストにタグヘッダーを追加します(例:
appver-a: v2)。ASMHeaderPropagation CRD が、呼び出しチェーン全体を通じて
appverで始まるすべてのヘッダーを伝搬します。各アプリケーションの VirtualService が対応するタグヘッダーをマッチさせ、リクエストを適切なバージョンへルーティングします。
ハッシュは決定論的であるため、同一のユーザーは常に同一のバージョンセットに到達し、リクエスト間で一貫した体験を提供します。

シナリオ概要
本チュートリアルでは、呼び出しチェーン app-a → app-b → app-c を構成する 3 つのアプリケーションからなる分散システムを使用します。
| アプリケーション | 安定版 | カナリア版 | カナリア比率 | 理由 |
|---|---|---|---|---|
| app-a | v1 | v2 | 10 % | 新機能の展開 |
| app-b | v1 | v2 | 10 % | 新機能の展開 |
| app-c | v2 | v3 | 50 % | バグ修正(リスクが低く、迅速なリリースが必要) |
本チュートリアルでは、まず安定版のベースラインをデプロイし、次に app-a v2 および app-b v2 を 10 % のユーザーに展開、さらに app-c v3 を 50 % のユーザーに追加展開した後、最終的に app-c v3 を 100 % に昇格させる手順を説明します。
以下の図は、複数バージョンを備えた分散システムのトラフィックレーンアーキテクチャを示しています。

以下の図は、各アプリケーションに対して独立したカナリア比率を適用した目標状態を示しています。

前提条件
開始する前に、以下の条件を満たしていることを確認してください。
バージョン 1.18 以降の ASM インスタンスが存在し、クラスターが追加済みであること。詳細については、「ASM インスタンスへのクラスターの追加」をご参照ください。
Container Service for Kubernetes (ACK) マネージドクラスターまたは ACS クラスターが存在すること。詳細については、「ACK マネージドクラスターの作成」または「ACS クラスターの作成」をご参照ください。
イングレスゲートウェイがデプロイ済みであること。詳細については、「イングレスゲートウェイの作成」をご参照ください。
ステップ 1:ベースラインアプリケーションのデプロイ
app-a v1、app-b v1、app-c v2 およびそれらの Istio ルーティングリソースをデプロイします。このステップ完了後、すべてのトラフィックは呼び出しチェーン app-a(v1) → app-b(v1) → app-c(v2) に従います。

アプリケーションワークロードのデプロイ
以下の内容で
app-init.yamlファイルを作成します。
データプレーンクラスターの kubeconfig を使用してファイルを適用します。本例では
default名前空間を使用します。自動サイドカープロキシ注入が有効化された任意の名前空間でも動作します。
kubectl apply -f app-init.yaml -n defaultIstio ルーティングリソースの構成
以下の内容で
app-init-mesh.yamlファイルを作成します。このファイルでは、VirtualService、DestinationRule、および Gateway を定義し、すべてのトラフィックを安定版へルーティングします。
ASM インスタンス(コントロールプレーン)の kubeconfig を使用してファイルを適用します。
kubectl apply -f app-init-mesh.yamlベースラインの検証
テストリクエストを送信します。
<ingress-gateway-ip>をご利用のイングレスゲートウェイの IP アドレスに置き換えてください。このアドレスを取得する方法については、「イングレスゲートウェイの IP アドレスの取得」をご参照ください。
curl -H 'x-user-id: 0001' <ingress-gateway-ip>期待される出力:
-> app-a(バージョン: v1, IP: 10.0.250.27)-> app-b(バージョン: v1, IP: 10.0.250.6)-> app-c(バージョン: v2, IP: 10.0.250.11)
-> app-a(バージョン: v1, IP: 10.0.250.27)-> app-b(バージョン: v1, IP: 10.0.250.6)-> app-c(バージョン: v2, IP: 10.0.250.11)
-> app-a(バージョン: v1, IP: 10.0.250.27)-> app-b(バージョン: v1, IP: 10.0.250.6)-> app-c(バージョン: v2, IP: 10.0.250.11)
-> app-a(バージョン: v1, IP: 10.0.250.27)-> app-b(バージョン: v1, IP: 10.0.250.6)-> app-c(バージョン: v2, IP: 10.0.250.11)
-> app-a(バージョン: v2, IP: 10.0.250.14)-> app-b(バージョン: v2, IP: 10.0.250.8)-> app-c(バージョン: v2, IP: 10.0.250.11)すべてのリクエストは呼び出しチェーン app-a(v1) → app-b(v1) → app-c(v2) に従います。
ステップ 2:app-a および app-b のカナリアリリースの開始
このステップでは、app-a v2 および app-b v2 をデプロイし、ハッシュタグ付けプラグインを構成して、10 % のユーザーを新バージョンへルーティングします。以下の 4 つの変更を実施します。
app-a v2 および app-b v2 のワークロードをデプロイします。
DestinationRule および VirtualService を更新し、v2 サブセットおよびタグベースのルーティングルールを追加します。
ASMHeaderPropagation CRD を構成し、呼び出しチェーン全体で
appverで始まるヘッダーを伝搬します。app-a および app-b のタグ付けルールを備えたハッシュタグ付けプラグイン(WasmPlugin)をデプロイします。
注: ここに記載した順序は理解しやすさを重視したものであり、実際の運用ではアプリケーション間の依存関係に応じて順序を調整してください。

カナリアワークロードのデプロイ
以下の内容で
app-ab-v2.yamlファイルを作成します。
データプレーンクラスターの kubeconfig を使用してファイルを適用します。
kubectl apply -f app-ab-v2.yamlルーティングルールの更新
以下の内容で
app-ab-v2-mesh.yamlファイルを作成します。このファイルでは、DestinationRule に v2 サブセットを追加し、app-a および app-b の VirtualService にタグマッチングルートを追加します。リクエストにappver-a: v2ヘッダーが含まれる場合、app-a v2 へルーティングされます。同様に、appver-b: v2ヘッダーが含まれる場合は app-b v2 へルーティングされます。これらのヘッダーが含まれないリクエストは v1(デフォルト)へルーティングされます。
ASM インスタンス(コントロールプレーン)の kubeconfig を使用してファイルを適用します。
kubectl apply -f app-ab-v2-mesh.yamlヘッダー伝搬の有効化
以下の内容で
header-propagation.yamlファイルを作成します。ASMHeaderPropagation CRD は、サイドカープロキシに対し、appverで始まるすべてのヘッダーを呼び出しチェーン全体で転送するよう指示します。この設定がない場合、ゲートウェイで追加されたタグは下流のアプリケーションに到達しません。
apiVersion: istio.alibabacloud.com/v1beta1
kind: ASMHeaderPropagation
metadata:
name: tag-propagation
spec:
headerPrefixes:
- appverデータプレーンクラスターの kubeconfig を使用してファイルを適用します。
kubectl apply -f header-propagation.yaml -n defaultハッシュタグ付けプラグインの構成
以下の内容で
hash-tagging-plugin.yamlファイルを作成します。
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: hash-tagging
namespace: istio-system
spec:
imagePullPolicy: IfNotPresent
selector:
matchLabels:
istio: ingressgateway
url: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-wasm-hash-tagging:v1.22.6.2-g72656ba-aliyun
phase: AUTHN
pluginConfig:
rules:
- header: x-user-id
modulo: 100
tagHeader: appver-a
policies:
- range: 10
tagValue: v2
- header: x-user-id
modulo: 100
tagHeader: appver-b
policies:
- range: 10
tagValue: v2
- header: x-user-id
modulo: 100
tagHeader: appver-c
policies:
- range: 50 # app-c のカナリア比率は 50%
tagValue: v3以下の表は、この構成におけるタグ付けルールを説明しています。
| ルール | タグヘッダー | 範囲 | 効果 |
|---|---|---|---|
| app-a | appver-a | 10 | x-user-id 値をモジュロ 100 でハッシュ化し、余剰値が 10 以下の場合に appver-a: v2 ヘッダーを追加します。これにより、約 10 % のユーザーが app-a v2 へルーティングされます。 |
| app-b | appver-b | 100 | 同様のハッシュ計算ですが、範囲を 100 に設定しているため、すべてのユーザーに appver-b: v2 ヘッダーが付与され、app-b v2 へルーティングされます。ターゲットとなるカナリア比率に応じて range 値を調整してください。 |
WasmPlugin 構成を適用します。
kubectl apply -f hash-tagging-plugin.yamlカナリアリリースの検証
異なるユーザー ID を使用してテストリクエストを送信します。
curl -H 'x-user-id: 0001' <ingress-gateway-ip>
curl -H 'x-user-id: 0002' <ingress-gateway-ip>
curl -H 'x-user-id: 0003' <ingress-gateway-ip>
curl -H 'x-user-id: 0004' <ingress-gateway-ip>
curl -H 'x-user-id: 0005' <ingress-gateway-ip>期待される出力:
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v2, ip: 10.0.250.11)
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v2, ip: 10.0.250.11)
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v2, ip: 10.0.250.11)
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v2, ip: 10.0.250.11)
-> app-a(version: v2, ip: 10.0.250.14)-> app-b(version: v2, ip: 10.0.250.8)-> app-c(version: v2, ip: 10.0.250.11)ユーザー 0005 のハッシュ余剰値が設定済み範囲内に該当したため、ハッシュタグ付けプラグインがリクエストにタグを付与し、app-a v2 および app-b v2 へルーティングされました。ユーザー 0001 ~ 0004 は引き続き v1 へルーティングされます。
ステップ 3:app-c のカナリアリリースの開始
app-a v2 および app-b v2 のカナリアリリースが継続中の状態で、チームが app-c v2 にバグを発見し、app-c v3 を 50 % のユーザーに展開することを決定しました。
app-c v3 のデプロイ
以下の内容で
app-c-v3.yamlファイルを作成します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-c-v3
labels:
app: app-c
version: v3
spec:
replicas: 1
selector:
matchLabels:
app: app-c
version: v3
ASM_TRAFFIC_TAG: v3
template:
metadata:
labels:
app: app-c
version: v3
ASM_TRAFFIC_TAG: v3
annotations:
instrumentation.opentelemetry.io/inject-java: "true"
instrumentation.opentelemetry.io/container-names: "default"
spec:
containers:
- name: default
image: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-mock:v0.1-java
imagePullPolicy: IfNotPresent
env:
- name: version
value: v3
- name: app
value: app-c
ports:
- containerPort: 8000データプレーンクラスターの kubeconfig を使用してファイルを適用します。
kubectl apply -f app-c-v3.yamlapp-c v3 のルーティング構成
以下の内容で
app-c-v3-mesh.yamlファイルを作成します。このファイルでは、DestinationRule に v3 サブセットを追加し、app-c の VirtualService にタグマッチングルートを追加します。リクエストにappver-c: v3ヘッダーが含まれる場合、app-c v3 へルーティングされます。それ以外のリクエストは app-c v2(デフォルト)へルーティングされます。
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: app-c
namespace: default
spec:
hosts:
- app-c.default.svc.cluster.local
http:
- name: default
route:
- destination:
host: app-c.default.svc.cluster.local
port:
number: 8000
subset: v3ASM インスタンス(コントロールプレーン)の kubeconfig を使用してファイルを適用します。
kubectl apply -f app-c-v3-mesh.yamlハッシュタグ付けプラグインの更新
以下の内容で
wasm-plugin-ab-v2-c-v3.yamlファイルを作成します。このファイルでは、app-c 用の第 3 のタグ付けルールを追加し、範囲を 50 に設定することで、50 % のユーザーを app-c v3 へルーティングします。
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: hash-tagging
namespace: istio-system
spec:
imagePullPolicy: IfNotPresent
selector:
matchLabels:
istio: ingressgateway
url: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-wasm-hash-tagging:v1.22.6.2-g72656ba-aliyun
phase: AUTHN
pluginConfig:
rules:
- header: x-user-id
modulo: 100
tagHeader: appver-a
policies:
- range: 10
tagValue: v2
- header: x-user-id
modulo: 100
tagHeader: appver-b
policies:
- range: 10
tagValue: v2
- header: x-user-id
modulo: 100
tagHeader: appver-c
policies:
- range: 50 # app-c のカナリア比率:50 %
tagValue: v3更新されたプラグイン構成を適用します。
kubectl apply -f wasm-plugin-ab-v2-c-v3.yaml結果の検証
異なるユーザー ID を使用してテストリクエストを送信します。
curl -H 'x-user-id: 0001' <ingress-gateway-ip>
curl -H 'x-user-id: 0002' <ingress-gateway-ip>
curl -H 'x-user-id: 0003' <ingress-gateway-ip>
curl -H 'x-user-id: 0004' <ingress-gateway-ip>
curl -H 'x-user-id: 0005' <ingress-gateway-ip>期待される出力:
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v2, ip: 10.0.250.11)
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v2, ip: 10.0.250.11)
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v3, ip: 10.0.250.23)
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v3, ip: 10.0.250.23)
-> app-a(version: v2, ip: 10.0.250.14)-> app-b(version: v2, ip: 10.0.250.8)-> app-c(version: v3, ip: 10.0.250.23)結果より、以下の 3 つの異なるルーティングパスが独立して決定されていることがわかります。
| ユーザー ID | app-a | app-b | app-c | 説明 |
|---|---|---|---|---|
| 0001、0002 | v1 | v1 | v2 | タグが一致しなかったため、すべて安定版が使用されます |
| 0003、0004 | v1 | v1 | v3 | appver-c のみが一致(50 % 範囲) |
| 0005 | v2 | v2 | v3 | すべてのタグが一致:appver-a および appver-b(10 % 範囲)に加え、appver-c(50 % 範囲) |
ステップ 4:app-c v3 の全トラフィックへの昇格
app-c v3 がカナリア環境で正常に動作することを確認した後、すべてのトラフィックを v3 へルーティングすることで昇格を行います。この操作には、以下の 2 つの変更が必要です:VirtualService を更新してタグマッチングルートを削除し、デフォルトルートを v3 へ向けること、およびプラグインから app-c のタグ付けルールを削除して不要なヘッダーの伝搬を停止することです。
VirtualService の更新
以下の YAML を ASM インスタンス(コントロールプレーン)の kubeconfig を使用して適用します。これにより、デフォルトルートを更新して app-c のすべてのトラフィックを v3 へルーティングします。
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: app-c
namespace: default
spec:
hosts:
- app-c.default.svc.cluster.local
http:
- name: default
route:
- destination:
host: app-c.default.svc.cluster.local
port:
number: 8000
subset: v3app-c のタグ付けルールの削除
以下の YAML を適用してハッシュタグ付けプラグインを更新します。
appver-cルールを削除し、app-a および app-b のルールのみを残します。
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
name: hash-tagging
namespace: istio-system
spec:
imagePullPolicy: IfNotPresent
selector:
matchLabels:
istio: ingressgateway
url: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-wasm-hash-tagging:v1.22.6.2-g72656ba-aliyun
phase: AUTHN
pluginConfig:
rules:
- header: x-user-id
modulo: 100
tagHeader: appver-a
policies:
- range: 10
tagValue: v2
- header: x-user-id
modulo: 100
tagHeader: appver-b
policies:
- range: 10
tagValue: v2プロモーションを確認する
異なるユーザー ID を使用してテストリクエストを送信します。
curl -H 'x-user-id: 0001' <ingress-gateway-ip>
curl -H 'x-user-id: 0002' <ingress-gateway-ip>
curl -H 'x-user-id: 0003' <ingress-gateway-ip>
curl -H 'x-user-id: 0004' <ingress-gateway-ip>
curl -H 'x-user-id: 0005' <ingress-gateway-ip>期待される出力:
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v3, ip: 10.0.250.23)
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v3, ip: 10.0.250.23)
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v3, ip: 10.0.250.23)
-> app-a(version: v1, ip: 10.0.250.27)-> app-b(version: v1, ip: 10.0.250.6)-> app-c(version: v3, ip: 10.0.250.23)
-> app-a(version: v2, ip: 10.0.250.14)-> app-b(version: v2, ip: 10.0.250.8)-> app-c(version: v3, ip: 10.0.250.23)すべてのユーザーが app-c v3 に到達しており、昇格が完了したことが確認できます。
注: すべてのトラフィックが app-c v3 に切り替わった後は、運用要件に応じて app-c v2 のデプロイメントを 0 レプリカにスケールダウンするか、削除してください。