1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ingress-nginxでのCanary機能と根本的課題

Last updated at Posted at 2019-10-11

ingress-nginxはどうやって動いているのか(1)ingress-nginxはどうやって動いているのか(2)と続けて、ingress-nginxがどのような仕組みで動いているのかを見てきた。

この記事では、ingress-nginxが提供するCanary機能の概要と、メンテナーの方が現時点(v0.26.1)で『Fundamental Issue(根本的な課題)』と言っているある課題について見ていく。

Canary機能

公式ドキュメントでは、

In some cases, you may want to "canary" a new set of changes by sending a small number of requests to a different service than the production service. The canary annotation enables the Ingress spec to act as an alternative service for requests to route to depending on the rules applied.

とあるように、Canary機能用の独自AnnotationをIngress用のyamlファイルに記述しておくことで、クライアントから来たリクエストの数%を別のServiceに流したりすることができる。

現時点でサポートしているのは、以下の4つのパターン。

Name Description
canary-weigh %を指定し、特定の割合でルーティング先を変えることができる。
canary-by-cookie 特定のCookieの名前を設定しておくことで、そのCookieの値がAlwaysの時にルーティング先を変えることができる。
canary-by-header 特定のHeaderの名前を設定しておくことで、そのHeaderの値がAlwaysの時にルーティング先を変えることができる。
canary-by-header-value canary-by-header で指定したHeaderの値が、ここで指定した値の場合にルーティング先を変えることができる。

Canaryの例

ingress.yaml
kind: Ingress
spec:
  rules:
  - host: foobar.com
    http:
      paths:
        - path: /
          backend:
            serviceName: foo-service
            servicePort: 1234
ingress-canary.yaml
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  rules:
  - host: foobar.com
    http:
      paths:
        - path: /
          backend:
            serviceName: bar-service
            servicePort: 1234

この2つのyamlを適用すると、foobar.comへのアクセスは90%がfoo-serviceにルーティングされて、10%がbar-serviceにルーティングされる。

実際にはIngressリソース以外に、ingress-nginxをIngress ControllerとしてデプロイするためのDeploymentやServiceのyamlが必要だが、そちらについては以下のサイトなどを参照してほしい。

内部的な処理フローとコードリーディング

処理③:Canaryを参照。

Canary機能の根本的な課題

ingress-nginxのメンテナーの1人であるElvinEfendiが、Canary機能の実装上の根本的な課題として、以下のようなコメントをしている。

I think fundamental issue is that canary is configured based on host and path combination but when it is actually applied to a request, we use $proxy_upstream_name, which is based on namespace, service and port. This is the problem.

どういうことかというのを、具体的な例で見ていく。

ingress.yaml
kind: Ingress
spec:
  rules:
  - host: foobar.com
    http:
      paths:
        - path: /
          backend:
            serviceName: foo-service
            servicePort: 1234
        - path: /foobar
          backend:
            serviceName: foo-service
            servicePort: 1234
ingress-canary.yaml
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  rules:
  - host: foobar.com
    http:
      paths:
        - path: /foobar
          backend:
            serviceName: bar-service
            servicePort: 1234

このような2つのyamlで、foobar.com/foobarに対して、Canaryを設定する。
これがこの部分のことである。

canary is configured based on host and path combination

しかし、内部的な実装としては、ingress-nginxはどうやって動いているのか(2)#オブジェクトの関連図 にある通り、Backend(namespace, service, portで一意)にAlternativeBackendsとして、Canaryとして追加されるルーティング先(Backend)が格納される。

image.png

これが、

we use $proxy_upstream_name, which is based on namespace, service and port.

の部分のことを示している。
この、設定の仕方内部実装の違いが、いくつかの問題を生じさせている。

具体的な課題①:複数Path指定のIngressでCanaryがうまく動かない

Issue#4028: Canary ingress with multiple paths behaves improperly

実は先程例であげた2つのyamlを適用した場合、想定どおりにCanary機能が動作しない。

パス 期待する挙動 実際の挙動
/ 100%: foo-service 90%: foo-service, 10%: bar-service
/foobar 90%: foo-service, 10%: bar-service 90%: foo-service, 10%: bar-service

foo-serviceBackendAlternativeBackendsに、bar-serviceBackendが設定されており、/foobarというPath情報はBackendでは考慮されないため、どのパスへのリクエストももれなくCanaryの対象となってしまっている。

これが、ElvinEfendiが述べている"根本的な課題"のことである。

処理の流れ的にはこんな感じ(なはず)

  1. / へのリクエストをnginxが受け取る
  2. nginx.confのServer>Locationの設定に応じて、メインのBackendが選択される(今回の場合はfoo-service
  3. 実際のルーティング処理を行うLuaのBalancerモジュールでは、foo-serviceBackendに設定されているAlternativeBackendsを見て、10%の確率でリクエストをbar-serviceへルーティングする

具体的な課題②:Canary指定時のログの情報が正しくない

Issue#4368: Incorrect logs and ngx variables when using canary Ingresses

先程例であげた2つのyamlを適用した場合、Canaryでbar-serviceにリクエストがルーティングされた場合にも、ログにはfoo-serviceの情報が記録されてしまうというもの。

これは、ログに記録する情報はnginx.confで設定される値を利用していることに起因している。Canary機能を利用しない場合はそれで問題ないが、Canary機能を利用する場合、LuaのBalancerモジュールでルーティング処理時に、ルーティング先のBackendが変更される可能性がある。現在の実装ではその場合も、ログに書かれる情報の更新を行っていないため、正しくない情報がログに記録されることになる。

具体的な課題③:同一Serviceで複数Hostnameの場合のCanaryの挙動が正しくない

Issue#4667: Ingresses with different host point to same upstream cause confusion

別々のHostnameが同一のサービスへルーティングされる場合に片方のHostnameでCanaryを適用すると、もう片方のHostnameへのアクセスもCanaryの対象となってしまうというもの。

ingress.yaml
kind: Ingress
spec:
  rules:
  - host: foobar1.com
    http:
      paths:
        - path: /
          backend:
            serviceName: foo-service
            servicePort: 1234
  - host: foobar2.com
    http:
      paths:
        - path: /
          backend:
            serviceName: foo-service
            servicePort: 1234
ingress-canary.yaml
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  rules:
  - host: foobar1.com
    http:
      paths:
        - path: /
          backend:
            serviceName: bar-service
            servicePort: 1234

上記のyamlを適用した場合、foobar1.comに対して設定したつもりのCanaryの設定が、foo-service(のBackend)へ適用されてしまうため、foobar2.comへのアクセスも同様のCanaryの設定で動いてしまう。

これも具体的な課題①:複数Path指定のIngressでCanaryがうまく動かないと同様に、設定の仕方と内部実装の矛盾が根本原因となっている。

解決案

次回はこの根本的課題の解決案について検討してみたい。
ingress-nginxのCanary機能の根本的課題の解決案

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?