TLS パススルー ゲートウェイをデプロイする

このドキュメントでは、TLS パススルー用に構成された Kubernetes Gateway リソースをデプロイして、Google Kubernetes Engine(GKE)で Server Name Indication(SNI)ベースのルーティングを有効にする方法について説明します。このアプローチでは、クライアントとバックエンド Pod 間のエンドツーエンドの暗号化と相互 TLS(mTLS)が維持され、HTTP 以外の TLS ベースのプロトコルがサポートされ、IP アドレスが節約されます。

このドキュメントは、コンテナ化されたワークロードの暗号化されたネットワーキングを設計、デプロイ、管理するクラウド アーキテクト、ネットワーク スペシャリスト、デベロッパーを対象としています。一般的なロールとタスクの例の詳細については、一般的な GKE ユーザーのロールとタスクをご覧ください。

概要

GKE Gateway Controller は、レイヤ 7 HTTP および HTTPS アプリケーション ロードバランサに加えて、TLS パススルーで構成されたレイヤ 4 プロキシ ネットワーク ロードバランサ(TCP プロキシ ロードバランサ)をサポートしています。

TLS パススルーを使用すると、ロードバランサは TLS セッションを終端または復号しません。代わりに、クライアントが TLS handshake を開始すると、ロードバランサは暗号化されていない ClientHello メッセージを検査して、Server Name Indication(SNI)ホスト名を読み取ります。この SNI ホスト名に基づいて、ロードバランサは暗号化された未加工の TCP ストリームを適切なバックエンド Kubernetes Service にルーティングします。ここで、アプリケーション Pod は TLS を直接終端します。

Gateway API で TLS パススルーを使用すると、次のことができます。

  • エンドツーエンドの暗号化を維持し、ゼロトラスト コンプライアンスをサポートする: ロードバランサに秘密鍵や平文データへのアクセス権を付与することなく、クライアントからバックエンド Pod へのトラフィックを完全に暗号化します。
  • 相互 TLS(mTLS)認証を使用する: バックエンド アプリケーション レイヤでクライアントを直接認証します。
  • IPv4 アドレスを節約する: 単一の Gateway IP アドレスとポート(443 など)を使用して、異なるドメインまたはサブドメインに複数の独立した TLS サービスを多重化します。
  • HTTP 以外の TLS ベースのプロトコルをサポートする: ステートフル ワークロード、データベース、カスタム gRPC サービス、または TLS で実行される独自のプロトコルの暗号化されたトラフィックをルーティングします。

サポートされている GatewayClass

次の GKE GatewayClass は、TLS パススルー リスナーと TLSRoute リソースをサポートしています。

  • gke-l7-regional-external-managed
  • gke-l7-regional-external-managed-mc
  • gke-l7-rilb
  • gke-l7-rilb-mc
  • gke-l7-cross-regional-internal-managed-mc

GatewayClass の機能の完全な比較については、GatewayClass の機能をご覧ください。

始める前に

作業を始める前に、次のタスクが完了していることを確認してください。

  • Google Kubernetes Engine API を有効にする。
  • Google Kubernetes Engine API を有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化します。gcloud CLI をインストール済みの場合は、gcloud components update コマンドを実行して最新のバージョンを取得します。以前のバージョンの gcloud CLI では、このドキュメントのコマンドを実行できない場合があります。
  • Gateway API が有効になっている GKE バージョン 1.35 以降を実行している既存の Autopilot クラスタまたは Standard クラスタがあることを確認します。クラスタを作成する場合は、Autopilot クラスタの作成をご覧ください。既存のクラスタで Gateway API を有効にするには、クラスタで Gateway API を有効にするをご覧ください。
  • OpenSSL コマンドライン ツールがインストールされていることを確認します。このツールを使用して、サンプル アプリケーションの自己署名 TLS 証明書を生成します。

GKE Gateway コントローラの要件

  • Gateway API は、VPC ネイティブ クラスタでのみサポートされます。
  • リージョンまたはクロスリージョンの GatewayClass を使用している場合は、プロキシ専用サブネットを有効にする必要があります。
  • クラスタで HttpLoadBalancing アドオンが有効になっている必要があります。
  • Istio を使用している場合は、Istio を次のいずれかのバージョンにアップグレードする必要があります。
    • 1.15.2 以降
    • 1.14.5 以降
    • 1.13.9 以降
  • 共有 VPC を使用している場合は、ホスト プロジェクトで、サービス プロジェクトの GKE サービス アカウントに Compute Network User ロールを割り当てる必要があります。

制限事項

TLS パススルー Gateway リソースと TLSRoute リソースをデプロイする場合は、次の制限事項に注意してください。

  • リスナーで protocol: TLS 設定を構成する場合は、tls.mode フィールドを Passthrough 値に明示的に設定する必要があります。GKE は、TLS リスナーのデフォルトの Terminate モードをサポートしていません。
  • TLS リスナーでは、certificateRefs フィールドを設定しないでください。ゲートウェイ レベルのクライアント証明書の検証(spec.tls.frontendValidation フィールド)は、TLS リスナーには適用されません。mTLS を使用するには、バックエンドでクライアント証明書を検証します。
  • TLS リスナーは、他の TLS リスナーとのみポートを共有できます。TLS リスナーと同じポートに HTTP リスナーまたは HTTPS リスナーを構成することはできません。
  • TLSRoute.spec.hostnames フィールドには、1 ~ 1,024 個の SNI ホスト名(正確なホスト名またはワイルドカード接頭辞(*.example.com など))のリストを指定する必要があります。各エントリは 253 文字以下にする必要があります。また、IP アドレスにすることはできません。
  • TLSRoute.spec.rules フィールドには、最大 1 つのルールを構成できます。
  • 各ルールは 1 つの backendRef エントリのみをサポートします。レイヤ 4 プロキシ バックエンドは、複数のバックエンド間での重み付けによるトラフィック分割をサポートしていません。
  • 単一クラスタ GatewayClass は Service バックエンドのみをサポートし、マルチクラスタ GatewayClass は ServiceImport バックエンドのみをサポートします。
  • TLSRoute.spec.rules.backendRefs フィールドで、port フィールドを、コンテナポートではなく、ターゲット Service または ServiceImport リソースによって宣言されたポートに設定します。
  • TLSRoute リソースによって参照されるバックエンド Kubernetes Service(ServiceImport としてエクスポートされた Service を含む)は、ポート プロトコルに TCP 値を使用する必要があります。
  • コントローラがこのタイプの構成を拒否するため、BackendTLSPolicy リソース、Identity-Aware Proxy、Google Cloud Armor セキュリティ ポリシーを TLSRoute バックエンドに接続することはできません。
  • TLSRoute リソースによって参照される Service または ServiceImport に GCPBackendPolicy リソースを接続する場合は、spec.protocol フィールドを TCP 値に設定する必要があります(spec.protocol フィールドを省略すると、デフォルトで HTTP 値が設定されます。これは HTTPRoute バックエンドにのみ適用されます)。Service または ServiceImport には、1 つの HTTP ポリシーと 1 つの TCP ポリシーを同時に接続できます。同じ spec.protocol 値を持つ複数のポリシーが同じ Service または ServiceImport をターゲットにしている場合、最も古いポリシーが優先され、新しいポリシーは接続されません。
  • spec.protocol フィールドが TCP に設定された GCPBackendPolicy リソースは、spec.default フィールドの次のフィールドのみをサポートします(ServiceImport リソースをターゲットとする場合は、代わりに spec.default.scopes フィールドでクラスタ、ゾーン、リージョンごとに最後の 3 つのフィールドを設定できます)。

    • timeoutSec
    • connectionDraining
    • logging.enabled と logging.sampleRate
    • sessionAffinity.type(有効な値は CLIENT_IP または NONE)
    • capacityScalerPercent
    • balancingMode: CONNECTION
    • maxConnectionsPerEndpoint

    他のフィールドを含む TCP ポリシーや、TCP 専用フィールド(balancingMode: CONNECTION と maxConnectionsPerEndpoint)を含む HTTP ポリシーは作成できません。Kubernetes API サーバーは、作成時にポリシーを拒否します。詳細については、ポリシーを使用してゲートウェイ リソースを構成するをご覧ください。

  • HTTPRoute リソースと TLSRoute リソースの両方が同じ Service または ServiceImport を参照している場合、HealthCheckPolicy リソースをそのターゲットに接続すると、個別のデフォルト ヘルスチェックが置き換えられ、同じヘルスチェック構成が両方のルートのバックエンドに適用されます。ヘルスチェックを個別に構成するには(HTTPRoute バックエンドの HTTP ヘルスチェックや、TLSRoute バックエンドの SSL または TCP ヘルスチェックなど)、ルートごとに個別の Service を作成し、各 Service に HealthCheckPolicy リソースを接続します。両方の Service で同じ Pod を選択できます。

  • レイヤ 7 の機能(URL の書き換え、パスベースのルーティング、HTTP ヘッダー変換など)は、レイヤ 4 TCP プロキシ ロードバランサには適用されません。

プロキシ専用サブネットを構成する

リージョン Gateway には、デプロイする各リージョンにプロキシ専用サブネット(purpose=REGIONAL_MANAGED_PROXY)が必要です。1 つのプロキシ専用サブネットが、そのリージョン内のすべてのリージョン Envoy ベースのロードバランサにサービスを提供します。リージョンにプロキシ専用サブネットがまだない場合は、プロキシ専用サブネットを構成するをご覧ください。

静的 IP アドレスを予約する

Gateway をデプロイする前に、Gateway アドレスが変更されないようにリージョン静的 IP アドレスを予約し、サンプル ホスト名を解決できるようにします。静的 IP アドレスの作成手順については、リージョン外部 HTTP(S) Gateway を作成するをご覧ください。

TLS パススルー リスナーを使用して Gateway をデプロイする

このセクションの Gateway は、ポート 443 を共有する 2 つの TLS リスナーを構成します。各リスナーは異なる SNI ホスト名と一致し、パススルー モードで接続を、接続された TLSRoute リソースに転送します。

  1. 次のマニフェストを store-gateway.yaml として保存します。

    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: store-tls
    spec:
      gatewayClassName: gke-l7-regional-external-managed
      listeners:
      # Accept TLS connections for store.example.com without decrypting them.
      - name: store
        protocol: TLS
        port: 443
        hostname: "store.example.com"
        tls:
          mode: Passthrough
        allowedRoutes:
          kinds:
          - kind: TLSRoute
      # A second listener on the same port, selected by a different SNI name.
      - name: store-german
        protocol: TLS
        port: 443
        hostname: "store-de.example.com"
        tls:
          mode: Passthrough
        allowedRoutes:
          kinds:
          - kind: TLSRoute
      addresses:
      - type: NamedAddress
        value: IP_ADDRESS_NAME
    

    IP_ADDRESS_NAME は、予約した静的 IP アドレスの名前に置き換えます。

    tls ブロックには certificateRefs フィールドがありません。Passthrough モードでは、TLS 証明書はロードバランサではなくバックエンド Pod にあります。

  2. Gateway マニフェストを適用します。

    kubectl apply -f store-gateway.yaml
    
  3. Gateway がプログラムされていることを確認します。

    kubectl get gateway store-tls
    

    ロードバランサのプロビジョニングには数分かかります。出力は次のようになります。

    NAME        CLASS                              ADDRESS         PROGRAMMED   AGE
    store-tls   gke-l7-regional-external-managed   35.118.32.224   True         53s
    

内部 Gateway を使用する

VPC ネットワーク内にトラフィックを維持するには、gatewayClassName フィールドの値を gke-l7-rilb に変更し、Gateway マニフェストから addresses フィールドを削除します。次に、GKE はリージョン内部プロキシ ネットワーク ロードバランサを作成し、クラスタ サブネットから内部 IP アドレスを割り当てます。このページの残りの手順は変更されていません。内部 IP アドレスはインターネットから到達できないため、同じ VPC ネットワーク内の Compute Engine インスタンスからテスト リクエストを送信します。

サンプル TLS アプリケーションをデプロイする

このセクションでは、ポート 8443 で TLS を終端する 2 つの NGINX Deployment(store-v1 と store-german)をデプロイします。各 Deployment は異なる TLS 証明書を提供し、受信した SNI ホスト名を報告します。これにより、SNI に基づいてルーティングが行われたことを確認できます。

  1. 各ホスト名の自己署名 TLS 証明書と秘密鍵を生成します。

    openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 365 \
        -keyout store.key -out store.crt \
        -subj '/CN=store.example.com' \
        -addext 'subjectAltName=DNS:store.example.com'
    openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 365 \
        -keyout store-de.key -out store-de.crt \
        -subj '/CN=store-de.example.com' \
        -addext 'subjectAltName=DNS:store-de.example.com'
    

    本番環境では、認証局の証明書を使用します。自己署名証明書を使用すると、この例を自己完結型にできます。

  2. 次のコマンドを実行して、各証明書と鍵を Kubernetes Secret に保存します。

    kubectl create secret tls store-cert \
        --cert=store.crt --key=store.key
    kubectl create secret tls store-de-cert \
        --cert=store-de.crt --key=store-de.key
    
  3. 次のマニフェストを store-tls-app.yaml として保存します。

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: store-nginx
    data:
      default.conf: |
        server {
          listen 8443 ssl;
          ssl_certificate     /etc/nginx/ssl/tls.crt;
          ssl_certificate_key /etc/nginx/ssl/tls.key;
          default_type text/plain;
          # Report which Pod answered and which SNI name it received.
          return 200 "backend: $hostname\nsni: $ssl_server_name\n";
        }
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: store-v1
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: store-v1
      template:
        metadata:
          labels:
            app: store-v1
        spec:
          containers:
          - name: nginx
            image: nginx:1.27
            ports:
            - containerPort: 8443
            volumeMounts:
            - name: config
              mountPath: /etc/nginx/conf.d
            - name: cert
              mountPath: /etc/nginx/ssl
              readOnly: true
          volumes:
          - name: config
            configMap:
              name: store-nginx
          - name: cert
            secret:
              secretName: store-cert
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: store-v1
    spec:
      selector:
        app: store-v1
      ports:
      - port: 443
        targetPort: 8443
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: store-german
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: store-german
      template:
        metadata:
          labels:
            app: store-german
        spec:
          containers:
          - name: nginx
            image: nginx:1.27
            ports:
            - containerPort: 8443
            volumeMounts:
            - name: config
              mountPath: /etc/nginx/conf.d
            - name: cert
              mountPath: /etc/nginx/ssl
              readOnly: true
          volumes:
          - name: config
            configMap:
              name: store-nginx
          - name: cert
            secret:
              secretName: store-de-cert
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: store-german
    spec:
      selector:
        app: store-german
      ports:
      - port: 443
        targetPort: 8443
    

    Service に appProtocol フィールドは必要ありません。GKE は TLSRoute トラフィックを TCP としてバックエンドに転送するため、このフィールドは効果がありません。

  4. マニフェストをクラスタに適用します。

    kubectl apply -f store-tls-app.yaml
    
  5. Pod が実行されていることを確認します。

    kubectl get pods -l 'app in (store-v1,store-german)'
    

    出力は次のようになります。

    NAME                            READY   STATUS    RESTARTS   AGE
    store-german-7d9f8c5b47-2xqkd   1/1     Running   0          41s
    store-german-7d9f8c5b47-h6tzn   1/1     Running   0          41s
    store-v1-6c4b9d8f65-kp8vr       1/1     Running   0          41s
    store-v1-6c4b9d8f65-wm2ls       1/1     Running   0          41s
    

SNI ベースのルーティング用に TLSRoute リソースをデプロイする

TLSRoute リソースは Gateway リスナーに接続し、ホスト名に一致する接続を単一のバックエンド サービスに転送します。

  1. 次のマニフェストを store-tls-routes.yaml として保存します。

    kind: TLSRoute
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: store
    spec:
      parentRefs:
      - kind: Gateway
        name: store-tls
        sectionName: store
      hostnames:
      - "store.example.com"
      rules:
      - backendRefs:
        - name: store-v1
          port: 443
    ---
    kind: TLSRoute
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: store-german
    spec:
      parentRefs:
      - kind: Gateway
        name: store-tls
        sectionName: store-german
      hostnames:
      - "store-de.example.com"
      rules:
      - backendRefs:
        - name: store-german
          port: 443
    

    sectionName フィールドは、Gateway のターゲット リスナーを選択します。port フィールドには、コンテナポートではなく Service ポート(443)を指定します。

  2. TLSRoute マニフェストを適用します。

    kubectl apply -f store-tls-routes.yaml
    
  3. Gateway コントローラがルートを受け入れたことを確認します。

    kubectl get tlsroute store -o yaml
    

    status フィールドは次のようになります。

    status:
      parents:
      - controllerName: networking.gke.io/gateway
        parentRef:
          name: store-tls
          sectionName: store
        conditions:
        - type: ResolvedRefs
          status: "True"
          reason: ResolvedRefs
        - type: Accepted
          status: "True"
          reason: Accepted
        - type: Reconciled
          status: "True"
          reason: ReconciliationSucceeded
    

    Reconciled 条件は、Gateway コントローラが基盤となるGoogle Cloud リソースを作成したかどうかを報告する GKE 固有のステータス条件です。

  4. Gateway リスナーがルートを受け入れたことを確認します。

    kubectl get gateway store-tls -o yaml
    

    status.listeners フィールドの各エントリは次のようになります。

    - name: store
      supportedKinds:
      - group: gateway.networking.k8s.io
        kind: TLSRoute
      attachedRoutes: 1
      conditions:
      - type: ResolvedRefs
        status: "True"
        reason: ResolvedRefs
      - type: Accepted
        status: "True"
        reason: Accepted
      - type: Programmed
        status: "True"
        reason: Programmed
    

トラフィック ルーティングを確認する

GKE は、TLS handshake を完了して TLS パススルー バックエンドのヘルスチェックを行います。Pod が最初のヘルスチェックに合格するまでに数分かかることがあります。

  1. Gateway の IP アドレスを取得します。

    kubectl get gateway store-tls -o jsonpath='{.status.addresses[0].value}'
    
  2. 各ホスト名にリクエストを送信します。GATEWAY_IP は、前の手順で取得した IP アドレスに置き換えます。

    curl --resolve store.example.com:443:GATEWAY_IP \
        --cacert store.crt https://store.example.com
    curl --resolve store-de.example.com:443:GATEWAY_IP \
        --cacert store-de.crt https://store-de.example.com
    

    各レスポンスには、応答した Pod の名前と、到達した SNI ホスト名がエコーされます。

    backend: store-v1-6c4b9d8f65-kp8vr
    sni: store.example.com
    backend: store-german-7d9f8c5b47-2xqkd
    sni: store-de.example.com
    

    各リクエストで異なる認証局ファイルが使用され、両方とも成功したため、各バックエンドは独自の証明書を提供しました。ロードバランサは、暗号化された接続を終端せずに転送しました。

  3. クライアントが HTTP リクエストを送信する前に、TLS handshake 中にルーティングが行われることを確認します。

    openssl s_client -connect GATEWAY_IP:443 \
        -servername store-de.example.com </dev/null 2>/dev/null \
        | openssl x509 -noout -subject
    

    出力には、store-german バックエンドの証明書が表示されます。

    subject=CN=store-de.example.com
    

    -servername フラグの値のみを変更すると、返される証明書が変更され、ロードバランサが SNI のみに基づいてバックエンドを選択したことが確認されます。

  4. 一致するリスナーがない SNI ホスト名を含むリクエストを送信します。

    curl --resolve other.example.com:443:GATEWAY_IP \
        https://other.example.com
    

    ポート 443 のリスナーが SNI ホスト名 other.example.com と一致しないため、接続が閉じます。

バックエンド接続容量を構成する

TLSRoute リソースによって参照される GKE Service の場合、GKE Gateway コントローラは CONNECTION バランシング モードを使用して TCP バックエンド サービスを構成し、maxConnectionsPerEndpoint フィールドのデフォルト値を 100,000,000(1e8)に設定します。

Gateway コントローラはコンテナの同時接続数の上限を自動的に決定できないため、GCPBackendPolicy リソースを使用して maxConnectionsPerEndpoint フィールドのデフォルト値を 1 から 1,000,000,000 にオーバーライドできます。バックエンド Pod の最適な接続数の上限を決定するには、次の点を評価します。

  • ソフトウェアの上限: サーバーまたはアプリケーションの構成(Apache MaxRequestWorkers ディレクティブ、NGINX worker_processes * worker_connections 設定、Tomcat maxConnections 属性、データベース接続プールの上限など)を確認します。
  • 負荷テスト: 分離された Pod に対する同時接続数を徐々に増やしながら、CPU、メモリ、レイテンシ、ソケットまたはファイル記述子の上限をモニタリングします。

カスタム GCPBackendPolicy を適用する

TLSRoute バックエンド サービスの maxConnectionsPerEndpoint フィールドのデフォルト値をオーバーライドするには、spec.protocol フィールドを TCP に設定し、balancingMode フィールドを CONNECTION に設定した GCPBackendPolicy リソースを接続します。

  1. 次のマニフェストを backend-policy.yaml として保存します。

    apiVersion: networking.gke.io/v1
    kind: GCPBackendPolicy
    metadata:
      name: store-v1-backend-policy
    spec:
      protocol: TCP
      default:
        balancingMode: CONNECTION
        maxConnectionsPerEndpoint: 1000
      targetRef:
        group: ""
        kind: Service
        name: store-v1
    
  2. ポリシーを適用します。

    kubectl apply -f backend-policy.yaml
    

バックエンド ポリシーの構成の詳細については、ポリシーを使用して Gateway リソースを構成するをご覧ください。

TLS パススルーのヘルスチェックを構成する

デフォルトでは、TLS パススルー TLSRoute リソースに関連付けられたバックエンド サービス の場合、GKE Gateway Controller は SSL タイプのヘルスチェックを自動的にプロビジョニングします。

プローバーからの SNI 拡張機能がない場合を処理する

TLS パススルー バックエンドのヘルスチェックを構成する場合は、 Google Cloud ヘルスチェック プローバーが TLS エンドポイントとどのようにやり取りするかを考慮する必要があります。

バックエンド アプリケーションで TLS handshake を完了するために有効な SNI ホスト名が必要な場合、デフォルトの SSL ヘルスチェックは失敗します。この問題を解決するには、次のいずれかの方法を使用します。

  • フォールバック証明書を構成する: SNI 拡張機能なしで TLS 接続が到着したときに、デフォルトのフォールバック TLS 証明書を提供するようにバックエンド サーバーまたは Pod を構成します。
  • HealthCheckPolicy で TCP ヘルスチェックを使用する: HealthCheckPolicy リソース(config.type フィールドを TCP に設定)を Service に接続して、未加工の TCP ポート接続を確認し、TLS handshake を回避します。

カスタム HealthCheckPolicy を適用する

カスタム ヘルスチェック パラメータを構成する場合、または TCP ヘルスチェックに切り替える場合は、HealthCheckPolicy リソースを使用します。

  1. 次のマニフェストを health-check-policy.yaml として保存します。

    apiVersion: networking.gke.io/v1
    kind: HealthCheckPolicy
    metadata:
      name: store-health-check
    spec:
      default:
        checkIntervalSec: 10
        timeoutSec: 5
        healthyThreshold: 2
        unhealthyThreshold: 3
        config:
          type: TCP
          tcpHealthCheck:
            port: 8443
      targetRef:
        group: ""
        kind: Service
        name: store-service
    
  2. ポリシーを適用します。

    kubectl apply -f health-check-policy.yaml
    

ヘルスチェックの構成の詳細については、ポリシーを使用して Gateway リソースを構成するをご覧ください。

高度な構成

このセクションでは、TLS パススルー ゲートウェイと TLSRoute リソースでサポートされている高度なルーティング パターン、マルチテナント構成、マルチクラスタ デプロイについて説明します。

ワイルドカード ホスト名の一致

TLSRoute リソースは、ホスト名のワイルドカード プレフィックス マッチングをサポートし、複数のルートが同じ受信トラフィックに一致する場合は、決定論的な優先順位ルールに従います。

次のマニフェストを保存して、ワイルドカード TLSRoute を定義します。

apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
  name: wildcard-tls-route
spec:
  parentRefs:
  - name: store-tls
    sectionName: store
  hostnames:
  - "*.example.com"
  rules:
  - backendRefs:
    - name: store-v1
      port: 443

優先順位と競合解決のルール

複数の TLSRoute リソースが同じ Gateway リスナーに接続し、重複するホスト名または同一のホスト名を要求する場合、GKE Gateway コントローラは、他の Gateway ルートと同じホスト名の特異性と作成タイムスタンプ ルールを使用して競合を解決します。

  • ホスト名の完全一致(store.example.com など)は、ワイルドカード一致(*.example.com など)よりも優先されます。また、長いワイルドカード一致は、より広範なワイルドカード一致(*.com など)よりも優先されます。
  • 2 つ以上の TLSRoute リソースがまったく同じホスト名を宣言している場合、作成タイムスタンプ(metadata.creationTimestamp)が最も古いルートが優先されます。作成タイムスタンプが一致する場合は、辞書順で小さい <namespace>/<name> 識別子が優先されます。Gateway Controller は、競合するルートを受け入れません。競合するルートは、Conflicted の理由で Accepted: False 条件を報告します。

ルートの優先順位について詳しくは、ルートのマージ、優先度、検証をご覧ください。

ReferenceGrant を使用した Namespace をまたがるルーティング

マルチテナント環境では、一元化されたプラットフォーム チームがインフラストラクチャの名前空間(gateway-infra など)で Gateway を管理し、アプリケーション チームがデベロッパーの名前空間(store-ns など)で TLSRoute リソースと Service リソースを管理できます。

  1. Gateway リスナーで、すべての Namespace(または選択した Namespace)からのルートを許可します。

    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: shared-tls-gateway
      namespace: gateway-infra
    spec:
      gatewayClassName: gke-l7-regional-external-managed
      listeners:
      - name: tls-passthrough
        protocol: TLS
        port: 443
        tls:
          mode: Passthrough
        allowedRoutes:
          namespaces:
            from: All
          kinds:
          - kind: TLSRoute
    
  2. アプリケーション Namespace(store-ns)で、gateway-infra Namespace の Gateway が TLSRoute リソースまたは Service リソースを参照できるようにする ReferenceGrant リソースを作成します。

    apiVersion: gateway.networking.k8s.io/v1beta1
    kind: ReferenceGrant
    metadata:
      name: allow-gateway-infra
      namespace: store-ns
    spec:
      from:
      - group: gateway.networking.k8s.io
        kind: Gateway
        namespace: gateway-infra
      to:
      - group: ""
        kind: Service
    
  3. アプリケーションの名前空間(store-ns)で、共有 Gateway を参照する TLSRoute リソースを作成します。

    apiVersion: gateway.networking.k8s.io/v1
    kind: TLSRoute
    metadata:
      name: store-tls-route
      namespace: store-ns
    spec:
      parentRefs:
      - name: shared-tls-gateway
        namespace: gateway-infra
        sectionName: tls-passthrough
      hostnames:
      - "store.example.com"
      rules:
      - backendRefs:
        - name: store-service
          port: 443
    

マルチクラスタ TLS パススルー Gateway

フリート内の複数の GKE クラスタに TLS パススルーを使用するマルチクラスタ Gateway をデプロイできます。

  1. サポートされているマルチクラスタ GatewayClass(gke-l7-regional-external-managed-mc、gke-l7-rilb-mc、gke-l7-cross-regional-internal-managed-mc)を使用します。
  2. ServiceExport リソースを使用して、フリート全体にバックエンド サービスをエクスポートします。
  3. TLSRoute リソースで、ServiceImport リソースを使用してマルチクラスタ バックエンドを参照します。

    apiVersion: gateway.networking.k8s.io/v1
    kind: TLSRoute
    metadata:
      name: mc-store-tls-route
    spec:
      parentRefs:
      - name: mc-tls-gateway
        sectionName: tls-passthrough
      hostnames:
      - "store.example.com"
      rules:
      - backendRefs:
        - group: net.gke.io
          kind: ServiceImport
          name: store-service
          port: 443
    

マルチクラスタ Gateway の複数の IP アドレス

クロスリージョン マルチクラスタ Gateway をデプロイする場合(gke-l7-cross-regional-internal-managed-mc GatewayClass を使用する場合など)、spec.addresses フィールドに複数の IP アドレスを定義して、異なるリージョン間で内部 VIP を公開できます。

1 つの Gateway に複数の IP アドレスを構成すると、GKE Gateway コントローラは各 IP アドレスを個別の転送ルールに変換します。同時に、コントローラは共有ターゲット プロキシを参照し、同じ基盤となるルーティング構成を再利用します。コントローラは、基盤となる TLSRoute リソースに IP アドレスの数を乗算しません。これにより、ルートの重複と割り当ての枯渇を防ぐことができます。

次の例は、2 つのリージョンにエフェメラル IP アドレスが構成されたマルチクラスタ Gateway を示しています。

kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
  name: internal-cross-region-tls-gateway
spec:
  gatewayClassName: gke-l7-cross-regional-internal-managed-mc
  addresses:
  - type: networking.gke.io/ephemeral-ipv4-address/us-west1
    value: "us-west1"
  - type: networking.gke.io/ephemeral-ipv4-address/us-east1
    value: "us-east1"
  listeners:
  - name: tls-passthrough
    protocol: TLS
    port: 443
    tls:
      mode: Passthrough
    allowedRoutes:
      kinds:
      - kind: TLSRoute

内部マルチクラスタ Gateway のデプロイの詳細については、内部マルチクラスタ Gateway をデプロイするをご覧ください。

HTTP 接続の統合

RFC 7540(セクション 9.1.1)によると、次の両方の条件が満たされている場合、ウェブブラウザと HTTP/2 クライアントは、確立された TLS 接続を複数のリクエストに再利用できます。

  • ターゲットのホスト名が同じ IP アドレスに解決される。
  • サーバーから提示された TLS 証明書は、両方のホスト名に対して信頼できる。たとえば、*.example.com などのワイルドカード証明書やマルチドメインのサブジェクト代替名(SAN)を使用すると、1 つの証明書で複数のホスト名をカバーできます。

レイヤ 4 TCP プロキシ ロードバランサは、最初の TLS handshake の後に HTTP リクエスト ヘッダーを検査しないため、ロードバランサは、既存の接続を介して、最初に一致したバックエンド Pod に統合されたリクエストを転送します。

接続の結合に関する問題を回避するには:

  • 重複しない個別の TLS 証明書を使用する: 同じ Gateway IP アドレスの背後で多重化された異なるバックエンド サービス間で、ワイルドカード証明書や共通の SAN ID を共有しないでください。
  • HTTP 421 Misdirected Request を返す: HTTP :authority(または Host)ヘッダーを確認するようにバックエンド HTTPS サーバーを構成します。リクエストされたホストがサーバーの想定されるドメインと一致しない場合は、HTTP 421 Misdirected Request ステータス コードを返して、クライアントに新しい接続を開くよう促します。
  • HTTP/2 ORIGIN フレームを有効にする: 配信元サーバーで RFC 8336 ORIGIN フレームのサポートを採用し、接続の再利用に有効なホスト名を明示的に宣言します。

クリーンアップ

このページで使用したリソースについて、 Google Cloud アカウントに課金されないようにするには、リソースを削除します。

  1. TLSRoute リソースを削除します。

    kubectl delete tlsroute store store-german
    
  2. Gateway リソースを削除します。

    kubectl delete gateway store-tls
    
  3. サンプルの Deployment、Service、Secret リソースを削除します。

    kubectl delete -f store-tls-app.yaml
    kubectl delete secret store-cert store-de-cert
    

次のステップ