Open1

nginx in KubernetesにおいてK8sドメイン指定しているにも関わらずIPがキャッシュされる問題

harrythecodeharrythecode

1. 問題点

Kubernetes(K8s)環境で Nginx をリバースプロキシとして使用する際、例えば以下のような proxy_pass 設定を使うと、K8s 内のサービスへのリクエストを転送することができます:

proxy_pass http://<service>.<namespace>.svc.cluster.local;

しかし、Kubernetes の内部ドメイン名を直接 proxy_pass に指定すると、Pod の再起動やスケール操作などによって IP アドレスが変更されたときに、その情報が Nginx にすぐに反映されず、古い IP アドレスにリクエストを送り続けてしまうことがあります。

2. 解決策

これを回避するために、以下の対策を取りましょう:

  • set ディレクティブを使って変数化する: set を使用し、サービス名を変数として定義します。これにより、Nginx は毎回変数を評価する際に DNS 解決を行い、最新の IP アドレスに転送するようになります。
  • resolver の設定: resolver を使用して、定期的にサービスの名前解決を行い、IP アドレスの更新を反映させるようにします。

これにより、サービスの再起動やスケールイン・スケールアウトによって Pod の IP が変更されても、Nginx はその変化をキャッチし、最新の状態を保持できます。

3. 具体例

以下は、その設定例です:

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
  namespace: service
data:
  nginx.conf: |
    events {
    }
    http {
      resolver kube-dns.kube-system.svc.cluster.local valid=5s;
      server {
          listen 443;
          location / {
            set $service_name_var <SERVICE_NAME>.namespace.svc.cluster.local;
            rewrite ^/(.*) /$1 break;
            proxy_pass http://$service_name_var;
          }
      }
    }

4. rewriteproxy_pass の組み合わせの解説

4.1. rewrite の基本

Nginx の rewrite ディレクティブは、指定された正規表現に基づいてリクエスト URI を再構築(書き換え)するために使用されます。

  • 例:
    rewrite ^/(.*) /$1 break;
    
    • ^/(.*):
      • ^:リクエスト URI の先頭を示す。
      • /(.*):任意の文字列をキャプチャしてグループ化(.* は任意の文字が 0 回以上繰り返されることを意味し、括弧で囲むことでその部分をキャプチャとして扱う)。
    • /$1:
      • キャプチャした文字列を $1 として参照し、最終的なリクエスト URI を /example/path のように構成する。

4.2. break の役割

rewrite の最後に指定されている break は、rewrite のあとでさらに別の location ブロックやリダイレクトを試みることなく、現在の location ブロック内で処理を続行することを指示します。

  • break がない場合:

    • rewrite のあと、Nginx が別の location ディレクティブにマッチする可能性があるため、意図しない挙動になることがあります。
  • break を使う場合:

    • 書き換えた URI を使用して、そのまま次のディレクティブ(ここでは proxy_pass)に渡すように処理を固定できます。

4.3. proxy_pass の構成とオプション

proxy_pass は、Nginx をリバースプロキシとして設定し、別のサーバ(サービス)にリクエストを転送するディレクティブです。

  • 基本構成:

    • proxy_pass http://backend;
    • ここで、backend は転送先のホスト(または変数)を指定しています。
  • rewriteproxy_pass の相性:

    • rewrite によって書き換えた URI をそのまま proxy_pass に渡す場合、rewrite のパターンが正しくマッチしないと意図しない URI が転送されることがあります。
    • この問題を防ぐため、proxy_pass http://backend/$1; のように $1 を直接指定する方法もありますが、rewrite + break の組み合わせの方が、複雑な URI の変形やパスの管理が容易です。

4.4. proxy_pass のパス指定方法の違い

proxy_pass のパス指定には以下の二通りの方法があります:

  1. proxy_pass http://backend;

    • 転送先の URI は、書き換え後の URI をそのまま使用します。
    • 例: 元のリクエストが /foo/bar なら、http://backend/foo/bar に転送されます。
  2. proxy_pass http://backend/some_path/;

    • この形式では、proxy_pass に指定したパスがベースパスとして使用され、転送先のパスを構築します。
    • 例: 元のリクエストが /foo/bar なら、転送先は http://backend/some_path/foo/bar になります。

5. 最適な設定の選択

Kubernetes 環境で安定した Nginx のリバースプロキシ設定をするには、以下の設定を推奨します:

  • set を使って変数を定義:

    • 毎回 DNS を解決するようになるため、サービス名や内部ドメインが変更された場合でも柔軟に対応できます。
  • rewritebreak を組み合わせて URI を固定:

    • 書き換えた URI が内部で予期しない変更を起こさないようにするためです。
  • proxy_pass はシンプルに記述:

    • できるだけベースパスを指定せず、変数を使ったリクエスト転送により、URI の扱いを明示的にする。

6. まとめ

上記の設定を用いることで、Kubernetes 環境における Nginx リバースプロキシの DNS 解決とリクエスト URI の管理を安定化できます。特に rewrite の扱い方や proxy_pass のオプションについて理解し、最適な設定を選択することが重要です。