鉄火ブログ
投稿日:更新日:

【Kubernetes】Service ごとにパスを書き換える場合の Ingress 設定

サムネイル画像

こんにちは、たかしです。 Ingress でのルーティング設定時に、Service ごとに転送する URI を書き換える設定を解説していきます。

【結論】rewrite-target を使えばいい

ingress.yaml のサンプル(ChatGPT 作)
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 を masterminion に分けて一部のパスにのみ `rewrite-target` が適用されるようにする

・ingress に Basic 認証を設定する


具体的なコードは以下になります。

ingress.yaml
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 が出してくれるので楽でいいですね。


以上、たかしでした。