Kubernetes の nginx Ingress でパスを書き換えて転送したい
Kubernetes の Ingress が受け取ったパスの一部を加工して、後ろにいる Service (Pod) へ転送したくなった。どういうことかというと、
http://example.com/my-app-1/index.html
といった URL で Ingress がリクエストを受け取った時、後ろに用意した my-app-1
Service に対しては
http://my-app-1:3000/index.html
- (
my-app-1
部分はリクエストが到達した Pod にとってはlocalhost
となる)
- (
といったように、間の /my-app-1/
部分を除去してやりたいのだ。
しかし、通常 Ingress の path
指定をそのまま書いてしまうと、Pod に対しても
http://my-app-1:3000/my-app-1/index.html
というように、/my-app-1/
部分が付いたままリクエストが転送されてしまい、正しいパスにならずに 404 エラーになってしまったりする。
そこで今回は、rewrite-target
機能と use-regex
機能を使って、このパス置換 (パス・リライト) をやってやろうと思う。
いきなりコード
いきなりだが、こんな感じでコードを書けば良い。
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-ingress
labels:
name: my-ingress
annotations:
# 正規表現を使う
nginx.ingress.kubernetes.io/use-regex: "true"
# 「/」直後から、正規表現のキャプチャしたグループでパスを指定する
nginx.ingress.kubernetes.io/rewrite-target: /$1$2
spec:
tls:
- hosts:
- example.com
rules:
- host: example.com
http:
paths:
# 前述のような `/my-app-1/index.html` などのリクエストを処理する
- path: /my-app-1/(.*)
backend:
serviceName: my-app-1-service
servicePort: 3000
# 別の Service 向けの転送処理 : コチラはパスを書き換えず `http://my-app-2:8000/my-app-2/index.html` とそのまま転送する
- path: /(my-app-2)(\/.*)
backend:
serviceName: my-app-2-service
servicePort: 8000
# 最後にフォールバック的にルートパスへのリダイレクト
- path: /
backend:
serviceName: root-app
servicePort: 8080
- path: /(.*)
backend:
serviceName: root-app
servicePort: 8080
重要なのは annotations
に書いた2つの機能と、paths.path
の中の正規表現キャプチャだ。
アノテーションで use-regex
と rewrite-target
を指定していて、rewrite-target
部分で、/
以降の文字列をどのように変換するかというルールを書いている。正規表現のキャプチャした文字列を $1
・$2
と指定して、それを渡すようにしている。
my-app-1
の path
指定を見ると、/my-app-1/(.*)
と書いている。この場合、キャプチャしているのは $1
のみで、$2
に相当する文字列はないので空文字になる。つまり my-app-1-service
に転送されるパスは /$1
の文字列になり、間に書かれていた /my-app-1/
が除去できるワケだ。
一方、my-app-2
の path
指定は、こうしたパスの除去を行わない例。path: /(my-app-2)(\/.*)
と書いていて、$1
が my-app-2
、$2
が /.*
となっているので、それをそのままくっつけて my-app-2-service
に転送されるというワケ。
今回は $1
・$2
を指定したが、ココはお好みで。以下のように $2
・$3
で処理するような例もある。
- 参考 : Specifying rewrite-target for each path in config · Issue #3122 · kubernetes/ingress-nginx · GitHub
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$2$3
spec:
rules:
- path: /()(path1)(.*)
backend:
serviceName: webapp1
- path: /(path2)(.*)
backend:
serviceName: webapp2
正規表現を扱わないといけなくなってトリッキーだが、とりあえずこんな感じで対応できそう。