Apache APISIX Ingress Controller 中的流量分割
Fei Han
March 27, 2021
Traffic Split(トラフィック分割)は、トラフィックを複数のバックエンドサービスに分割して配信する機能です。API Gateway(例: Apache APISIX や Traefik)や Service Mesh(例: Istio や Linkerd)などのソリューションは、トラフィック分割を実現し、Canary Release や Blue-Green Deployment のような機能を提供します。
Traffic Split は Ingress Controller においても重要な機能です。Kubernetes クラスターの ingress レイヤーとして、Ingress Controller にトラフィック分割ルールを設定することで、アプリケーションの新バージョンリリースに伴うリスクを低減することが望まれます。これにより、新しくリリースされたインスタンスにルーティングされるトラフィック量を制御できます。この記事では、Ingress Nginx と Kong Ingress Controller におけるトラフィック分割(Canary Release)を紹介し、最終的に Apache APISIX Ingress Controller のトラフィック分割について説明します。
(注: 説明を簡潔にするため、Canary ルールにヒットした後にルーティングされるバックエンドサービスを「Canary アプリ」、Canary ルールにヒットしなかった場合にルーティングされるバックエンドサービスを「Stable アプリ」と呼びます。例えば、以下の図では Canary アプリと Stable アプリはそれぞれ「foo-canary」と「foo」です。)
Ingress Nginx
Ingress Nginx は Canary Release をサポートしており、nginx.ingress.kubernetes.io/canary
というアノテーションで制御されます。この機能をカスタマイズするためのいくつかのアノテーションが提供されています。
nginx.ingress.kubernetes.io/canary-by-header
ヘッダーの値(nginx.ingress.kubernetes.io/canary-by-header
で指定)に基づいてルーティング先が決定されます。ヘッダーの値が「always」の場合、Canary アプリにルーティングされ、「never」の場合、Stable アプリにルーティングされます。
nginx.ingress.kubernetes.io/canary-by-header-value
このアノテーションは nginx.ingress.kubernetes.io/canary-by-header
を拡張し、ヘッダーの値が「always」または「never」である必要がなくなります。
nginx.ingress.kubernetes.io/canary-by-header-pattern
nginx.ingress.kubernetes.io/canary-by-header
と同様ですが、値は PCRE 互換の正規表現です。
nginx.ingress.kubernetes.io/canary-by-cookie
Cookie ヘッダーのデータフィールドを使用してバックエンドサービスを決定します。
nginx.ingress.kubernetes.io/canary-weight
0 から 100 の間の重み値を割り当て、この重みに基づいてトラフィックが配信されます。重みが 0 の場合、すべてのトラフィックが Canary アプリにルーティングされ、100 の場合、すべてのトラフィックが Stable アプリにルーティングされます。
以下の YAML スニペットは、URI パスが「/get」で始まり、User-Agent が「.Mozilla.」パターンに一致するリクエストを Canary アプリ「foo-canary」にプロキシします。
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "User-Agent"
nginx.ingress.kubernetes.io/canary-by-header-pattern:
".*Mozilla.*"
name: ingress-v1beta1
Kong
Kong Gateway には Canary Release プラグインがあり、KongPlugin リソースを通じてその Ingress Controller に公開されます。管理者/ユーザーは KongPlugin オブジェクトを作成し、Canary Release ルールを設定し、ターゲットの Kubernetes Service に konghq.com/plugins
アノテーションを注入する必要があります。または、KongClusterPlugin オブジェクトを作成して、この Canary ルールをクラスター全体で有効にすることもできます。
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: foo-canary
config:
percentage: 30
upstream_host: foo.com
upstream_fallback: false
upstream_port: 80
plugin: canary
---
apiVersion: v1
kind: Service
metadata:
name: foo-canary
labels:
app: foo
annotations:
konghq.com/plugins: foo-canary
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: foo
canary: true
上記の例では、サービス「foo-canary」を「canary」としてマークし、このサービスに 30% のトラフィックをプロキシする Canary Release ルールを作成しています。
Apache APISIX
Apache APISIX は、traffic-split プラグインを使用してカスタムルールに基づいてトラフィックを分割します。Apache APISIX Ingress Controller は、このプラグインと ApisixRoute の柔軟なルートマッチ機能を使用して、ApisixRoute にトラフィック分割機能を実装しています(アノテーションに依存せず、第一級サポートとして)。
重みベース
複数の Kubernetes サービスを設定することで、重みベースの Canary ルールを適用できます。
apiVersion: apisix.apache.org/v2alpha1
kind: ApisixRoute
metadata:
name: foo-route
spec:
http:
- name: rule1
match:
hosts:
- foo.org
paths:
- /get*
backends:
- serviceName: foo-canary
servicePort: 80
weight: 10
- serviceName: foo
servicePort: 80
weight: 5
上記の例では、ホストが「foo.org」で URI パスが「/get」で始まるリクエストの 2/3 を「foo-canary」サービスに、残りを「foo」サービスにルーティングします。
Canary サービスの重みは、小規模な検証のために小さく設定し、ApisixRoute を変更して重みを増やしていき、最終的にすべてのトラフィックが Canary サービスにルーティングされるようにして、リリースを完了させることができます。
ルールベース
ApisixRoute の Exprs フィールドを使用すると、カスタムルートマッチルールを設定できます。また、複数のルートルールを単一の ApisixRoute オブジェクトにグループ化できるため、ルールベースのトラフィック分割をシームレスに実装できます。
apiVersion: apisix.apache.org/v2alpha1
kind: ApisixRoute
metadata:
name: foo-route
spec:
http:
- name: rule1
priority: 1
match:
hosts:
- foo.org
paths:
- /get*
backends:
- serviceName: foo
servicePort: 80
- name: rule2
priority: 2
match:
hosts:
- foo.org
paths:
- /get*
exprs:
- subject:
scope: Query
name: id
op: In
set:
- "3"
- "13"
- "23"
- "33"
backends:
- serviceName: foo-canary
servicePort: 80
ホストが「foo.org」で URI パスが「/get」で始まるリクエストは、以下の 2 つの部分に分けられます:
- id パラメータの値が 3、13、23、または 33 の場合、rule2 にヒットし、「foo-canary」に転送されます。
- それ以外の場合、rule1 にヒットし、「foo」にルーティングされます。
まとめ
Ingress Nginx の Traffic Split(Canary Release)は、重みベースのスキームとヘッダールールベースのスキームをサポートしていますが、アノテーションに依存しており、セマンティクスが弱いです。Kong の方法は重みによる Canary Release の設定のみをサポートしており、シナリオがやや限定的で、設定が複雑です(複数のリソースを設定する必要があります)。一方、Apache APISIX Ingress Controller の Traffic Split は柔軟で設定が簡単であり、重みベースとルールベースの両方のトラフィック分割スキームでうまく機能します。