Help us understand the problem. What is going on with this article?

ingress-nginxのCanary機能の根本的課題の解決案

前回の記事(ingress-nginxでのCanary機能と根本的課題)では、ingress-nginxのメンテナーが『根本的な課題』と語るCanary機能の問題点について説明した。この記事では、どのような解決方針がありそうか検討したい。

根本原因は何か?

現在のingress-nginxのリクエストの処理フローとしては、

  1. nginx.confの定義に従って(Hostname+Pathに応じて)、Backendを決定。
  2. LuaモジュールのBalancer処理で、Backendを変更(Canary機能)

と、2段階でBackendの決定を行っているおり、ここに諸々の問題の原因が関連している。

根本原因①:ingress.yamlでの定義情報と内部的な紐付け情報の不整合

IngressのyamlではHostname(+Path)に対して設定されるCanaryの定義情報が、ingress-nginxの内部ではBackend(≒Service+Port)に対して紐付いてしまっている。そのため、先程の2のBalancerでのBackendの切り替え時にHostnamePathの情報を一切考慮しないため、特定のHostnameや特定のPathに対してCanaryを指定した場合に、意図しない動きをしてしまうことになる。

根本原因②:Backend切替時の考慮漏れ

Canary機能によりBackendが切り替わった際に、合わせて変更しなければいけない情報(ログに出力する値など)が考慮されていない。別の捉え方をすると、先程の2の処理でBackendが切り替わった際にこれらを考慮しなければいけない作りになっている。

根本原因①の解決案

定義情報内部実装がずれてしまっているのが問題なので、それを合わせてみる。
Canaryに関するデータとしては以下の2つがあるので、これらのデータを現在関連が張られているBackendから、Locationに移してみる。

  • AlternativeBackends
    • ルーティング先のBackend
  • TrafficShapingPolicy
    • Canaryのルール

ingress-nginx_uml_new.png

Luaモジュールへのこれらの情報の連携はどうなるだろうか。
現状の実装では、

  • Configuration.Backends[] → LuaのConfigurationモジュールを経由して、Luaモジュールに連携
  • Location.***nginx.confsetで値をセットして、ngx.var.***としてLuaモジュールから参照されている

ので、

Location.CanaryBackend(string)Location.TrafficShapingPolicy、は、ngx.var.***として連携すれば問題なさそうだ。

これで、特定のHostnameや特定のPathに対していCanaryを設定した場合にも問題なくCanary機能を提供できそうだ。

根本原因②の解決案

Canary機能によるBackendの切り替えの実装をLuaモジュールではなく、nginx.confのみで実現できれば、根本解決になりそうだが、実際問題かなり難しそうだ。。

そもそも、LuaモジュールでのBalancer処理の際に接続先Backendを決定しているのは、ヘッダーやクッキーの値によって接続先を切り替えるといった高機能なCanary機能を提供するためである。

Issueの議論の中では、nginx自体の機能であるsplit_clientsを利用して、Luaモジュールでの処理ではなく、nginxそのものの処理の中(先程の1の処理の中)でCanary機能を実装できないかという提案も出されていたが、前述した高機能なCanary機能をsplit_clientsのみで提供するのは難しそうなのだ。。

なので、ここは愚直に、CanaryでBackendが切り替わった際に変更すべき値を愚直にLuaモジュール内で切り替えれるようにしていく方針がよさげ。

ということですでにIssueで上がっているログ情報の不整合について、修正案を考えてみたい。

CanaryでのBackend切り替えに伴うログ情報の修正

考慮すべきは、

monitor.lua
    ingress = ngx.var.ingress_name or "-",
    service = ngx.var.service_name or "-",

の部分。

CanaryのBackendが切り替えられた場合は、

    ingress = ngx.var.canary_ingress_name,
    service = backend.service.metadata.name,

みたいな感じにする。
最終的にはこんな感じ。

ingress-nginx_uml_new3.png

nginx.conf
       location {{ $path }} {
            {{ $ing := (getIngressInformation $location.Ingress $server.Hostname $location.Path) }}
            set $ingress_name          {{ $ing.Rule | quote }};
            set $canary_ingress_name   {{ $location.CanaryIngressName | quote }}; // <- Added

最後に

半ばメモみたいな感じで解決案を考えてみたが、今度はこの内容でKEP(Kubernetes Enhancement Proposals) を書いてみようと思う。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした