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 は柔軟で設定が簡単であり、重みベースとルールベースの両方のトラフィック分割スキームでうまく機能します。