【Kubernetes】Service ごとにパスを書き換える場合の Ingress 設定
こんにちは、たかしです。 Ingress でのルーティング設定時に、Service ごとに転送する URI を書き換える設定を解説していきます。
【結論】rewrite-target を使えばいい
metadata:
annotations:
nginx.ingress.kubernetes.io/use-regex: "true" # 「rewrite-target で正規表現を使うよ」っていう指定
nginx.ingress.kubernetes.io/rewrite-target: /$2 # 「正規表現の2番目にマッチした部分だけ残すよ」っていう指定
spec:
rules:
- host: example.com
http:
paths:
- path: /something(/|$)(.*) # 正規表現で path を指定
pathType: ImplementationSpecific
backend:
service:
name: my-svc
port:
number: 80
サンプルを作るのが面倒だったので ChatGPT に出してもらいました。 たぶん動くと思います。
上記設定の場合、例えばホスト/something/sample
というアドレスにアクセスすると、my-svc
には「ホスト/sample
」として転送されます。rewrite-target: /$2
となっているので、2つ目の正規表現グループである (.*)
にマッチしたパスのみが残るわけですね。
より実用的な例
例えば以下のような要件と仕様で考えてみます。
要件
1. 「web サービス->api サービス」のように、HTTP 通信でブラウザを経由して別サービスにアクセスしたい
2. web と api の両サービスについて、ステージング環境では Basic 認証によりアクセス制限をかけたい
具体的な仕様
1. パスごとのルーティング先サービス:「/xxx
-> web サービス」「/api/xxx
-> api サービス」
2. パスの書き換え:「/api/xxx
を /xxx
に rename して api サービスに転送。これは api サービス内のエンドポイントが /api/xxxx
のように定義されていない状況を想定。
3. Basic 認証:同じホストでルーティングすることで、 Basic 認証設定をブラウザに管理させる
3つ目の仕様について
3つ目の仕様、なぜ同じホストでルーティングをするのかについて、もう少し詳しく説明しておきます。別に気にならないよって人は、次の見出しまで読み飛ばしてください。
例えば別々の ingress、つまり別々のホストで2つのサービスをルーティングしたとします。「IngressA で web.example.com をルーティング, IngressB で api.example.com をルーティング」みたいな感じで。
こうすると、「web.example.com-> api.example.com」のアクセス時に認証情報を HTTP ヘッダーに入れる必要があります。つまり、認証情報をフロントエンドコード内に書く必要が出てきてしまうのです。フロントエンドコード内の認証情報は簡単に見られてしまうので、セキュリティ上望ましくありません。
これの一番簡単な解決方法は、「web.example.com->web.example.com/api」のように、同一ドメインで api にアクセスすることです。こうすると以下のような感じでブラウザが何とかしてくれます。
1. web.example.com アクセス時に手動入力した Basic 認証情報をブラウザが保持
2. api にアクセスする際、ブラウザくんが自動で HTTP ヘッダーに認証情報を付与
これを実現するために、1つの ingress(ホスト) で2つのサービスをルーティングしていく狙いです。
要件を満たした実装
上記要件を満たすため、以下のような設定にしていきます。
・web と api の2つのサービスにルーティングする ingress を定義する
・/api
のみ rewrite したいので、ingress を master
と minion
に分けて一部のパスにのみ `rewrite-target
` が適用されるようにする
・ingress に Basic 認証を設定する
具体的なコードは以下になります。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "web.appname" . }}
labels:
app.kubernetes.io/name: {{ include "web.appname" . }}
{{- include "sample-chart.labels" . | nindent 4 }}
annotations:
# master にする
nginx.ingress.kubernetes.io/mergeable-type: master
# ステージング環境では Basic 認証を設定
# master で指定されているので minion にも適用される
{{- if .Values.onStaging }}
nginx.ingress.kubernetes.io/auth-type: "basic"
nginx.ingress.kubernetes.io/auth-secret: {{ .Values.web.ingress.authSecret }}
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
{{- end }}
spec:
rules:
- {{- if .Values.web.ingress.host }}
host: {{ .Values.web.ingress.host }}
{{- end }}
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: {{ include "web.appname" . }}
port:
number: {{ .Values.web.service.port }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "web.appname" . }}-api
labels:
app.kubernetes.io/name: {{ include "web.appname" . }}-api
{{- include "sample-chart.labels" . | nindent 4 }}
annotations:
# minion にして master を拡張
nginx.ingress.kubernetes.io/mergeable-type: minion
# この minion の範囲のみ rewrite 設定
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- {{- if .Values.web.ingress.host }}
host: {{ .Values.web.ingress.host }}
{{- end }}
http:
paths:
- pathType: Prefix
path: /api(/|$)(.*) # 正規表現で path を指定
backend:
service:
name: {{ include "api.appname" . }}
port:
number: {{ .Values.api.service.port }}
rewrite-target
を活用したことで以下の仕様をシンプルに実装できました。
・パスの /api
部分を削除して api サービスに転送
・Basic 認証情報をセキュアに扱う
まとめ
最近はこういったマニフェストファイルも AI が出してくれるので楽でいいですね。
以上、たかしでした。