部署 TLS 直通网关

本文档介绍了如何部署配置为 TLS 传递的 Kubernetes Gateway 资源,以便在 Google Kubernetes Engine (GKE) 中启用基于服务器名称指示 (SNI) 的路由。此方法可保留客户端与后端 Pod 之间的端到端加密和双向 TLS (mTLS),支持基于 TLS 的非 HTTP 协议,并节省 IP 地址。

本文档适用于为容器化工作负载设计、部署和管理加密网络的云架构师、网络专家和开发者。如需详细了解常见角色和示例任务,请参阅常见的 GKE 用户角色和任务。

概览

除了第 7 层 HTTP 和 HTTPS 应用负载均衡器之外,GKE Gateway 控制器还支持配置了 TLS 传递的第 4 层代理网络负载均衡器(TCP 代理负载均衡器)。

使用 TLS 直通时,负载均衡器不会终止或解密 TLS 会话。相反,当客户端发起 TLS 握手时,负载均衡器会检查未加密的 ClientHello 消息,以读取服务器名称指示 (SNI) 主机名。根据此 SNI 主机名,负载均衡器会将原始加密 TCP 流路由到适当的后端 Kubernetes Service,您的应用 Pod 会在此处直接终止 TLS。

将 TLS 传递与 Gateway API 搭配使用时,您可以执行以下操作:

  • 保持端到端加密并支持零信任合规性:使流量从客户端到后端 Pod 保持完全加密,而不向负载均衡器授予对私钥或明文数据的访问权限。
  • 使用双向 TLS (mTLS) 身份验证:直接在后端应用层对客户端进行身份验证。
  • 节省 IPv4 地址:通过使用单个网关 IP 地址和端口(例如 443),在不同的网域或子网域中多路复用多个独立的 TLS 服务。
  • 支持基于 TLS 的非 HTTP 协议:为有状态工作负载、数据库、自定义 gRPC 服务或通过 TLS 运行的专有协议路由加密流量。

支持的 GatewayClasses

以下 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 可能不支持运行本文档中的命令。
  • 验证您是否已有运行 GKE 1.35 版或更高版本且启用了 Gateway API 的 Autopilot 或 Standard 集群。如需创建集群,请参阅创建 Autopilot 集群。 如需在现有集群上启用 Gateway API,请参阅在集群中启用 Gateway API。
  • 验证您是否已安装 OpenSSL 命令行工具。您可以使用此工具为示例应用生成自签名 TLS 证书。

GKE Gateway Controller 要求

  • 仅 VPC 原生集群支持 Gateway API。
  • 如果您使用的是区域级或跨区域 GatewayClass,则必须启用代理专用子网。
  • 集群必须启用 HttpLoadBalancing 插件。
  • 如果您使用的是 Istio,则必须将 Istio 升级到以下版本之一:
    • 1.15.2 或更高版本
    • 1.14.5 或更高版本
    • 1.13.9 或更高版本。
  • 如果您使用的是共享 VPC,则需要在宿主项目中将 Compute Network User 角色分配给服务项目的 GKE 服务账号。

限制和局限

部署 TLS 直通网关和 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 字段中,您最多可以配置一条规则。
  • 每条规则仅支持一个 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 后端,因为控制器会拒绝此类配置。
  • 将 GCPBackendPolicy 资源附加到 TLSRoute 资源引用的 Service 或 ServiceImport 时,必须将 spec.protocol 字段设置为 TCP 值(省略 spec.protocol 字段会默认设置为 HTTP 值,该值仅适用于 HTTPRoute 后端)。一个 Service 或 ServiceImport 可以同时附加一个 HTTP 政策和一个 TCP 政策;如果具有相同 spec.protocol 值的多个政策以同一 Service 或 ServiceImport 为目标,则最早的政策优先,较新的政策不会附加。
  • spec.protocol 字段设置为 TCP 的 GCPBackendPolicy 资源仅支持 spec.default 字段中的以下字段(以 ServiceImport 资源为目标时,您可以改为在 spec.default.scopes 字段中为每个集群、可用区或区域设置最后三个字段):

    • 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,并将 HealthCheckPolicy 资源附加到每个 Service。两个 Service 可以选择相同的 Pod。

  • 第 7 层功能(例如网址重写、基于路径的路由和 HTTP 标头转换)不适用于第 4 层 TCP 代理负载均衡器。

配置代理专用子网

区域级 Gateway 需要在您部署它的每个区域中都有一个代理专用子网 (purpose=REGIONAL_MANAGED_PROXY)。一个代理专用子网可为该区域中所有基于 Envoy 的区域级负载均衡器提供服务。如果您的区域还没有代理专用子网,请参阅配置代理专用子网。

保留静态 IP 地址

在部署 Gateway 之前,请预留区域级静态 IP 地址,以便 Gateway 地址不会更改,并且您可以将示例主机名解析到该地址。如需了解如何创建静态 IP 地址,请参阅创建区域级外部 HTTP(S) 网关。

部署具有 TLS 直通监听器的网关

本部分中的 Gateway 配置了两个共享端口 443 的 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. 验证网关是否已编程:

    kubectl get gateway store-tls
    

    预配负载均衡器需要几分钟时间。输出类似于以下内容:

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

使用内部网关

如需将流量保留在 VPC 网络内,请将 gatewayClassName 字段的值更改为 gke-l7-rilb,并从 Gateway 清单中移除 addresses 字段。然后,GKE 会创建一个区域级内部代理网络负载均衡器,并从集群子网中分配一个内部 IP 地址。此页面上的其余步骤保持不变。从同一 VPC 网络中的 Compute Engine 实例发送测试请求,因为无法从互联网访问内部 IP 地址。

部署示例 TLS 应用

在本部分中,您将部署两个 NGINX Deployment(store-v1 和 store-german),它们在端口 8443 上终止 TLS。每个 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
    

    服务不需要 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
    

部署 TLSRoute 资源以实现基于 SNI 的路由

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 字段用于选择网关上的目标监听器。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 条件是 GKE 专属状态条件,用于报告 Gateway 控制器是否创建了底层Google Cloud 资源。

  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 传递后端完成 TLS 握手来对其进行健康检查。您的 Pod 可能需要几分钟才能通过初始健康检查。

  1. 检索网关 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,并回显到达该 Pod 的 SNI 主机名:

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

    由于每个请求都使用了不同的证书授权机构文件,并且都成功了,因此每个后端都提供了自己的证书。负载均衡器转发了加密连接,而未终止该连接。

  3. 确认路由发生在 TLS 握手期间,即客户端发送任何 HTTP 请求之前:

    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 字段的默认值,请附加一个 GCPBackendPolicy 资源,并将 spec.protocol 字段设置为 TCP,将 balancingMode 字段设置为 CONNECTION:

  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
    

如需详细了解如何配置后端政策,请参阅使用政策配置网关资源。

为 TLS 传递配置健康检查

默认情况下,对于与 TLS 直通 TLSRoute 资源关联的后端服务,GKE Gateway Controller 会自动预配 SSL 类型的健康检查。

处理探测器中缺少 SNI 扩展程序的情况

为 TLS 传递后端配置健康检查时,您必须考虑 Google Cloud 健康检查探测器如何与 TLS 端点交互。

如果您的后端应用需要有效的 SNI 主机名才能完成 TLS 握手,则默认 SSL 健康检查将失败。如需解决此问题,请使用以下方法之一:

  • 配置后备证书:将后端服务器或 Pod 配置为在 TLS 连接到达时提供默认后备 TLS 证书,而无需 SNI 扩展。
  • 将 TCP 健康检查与 HealthCheckPolicy 搭配使用:将 HealthCheckPolicy 资源(config.type 字段设置为 TCP)附加到您的 Service,以检查原始 TCP 端口连接并避免 TLS 握手。

应用自定义 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
    

如需详细了解如何配置健康检查,请参阅使用政策配置网关资源。

高级配置

本部分介绍 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 资源附加到同一网关监听器并声明重叠或相同的主机名时,GKE Gateway 控制器会使用与其他网关路由相同的主机名特异性和创建时间戳规则来解决冲突:

  • 完全匹配的主机名(例如 store.example.com)优先于通配符匹配(例如 *.example.com),较长的通配符匹配优先于较宽泛的通配符匹配(例如 *.com)。
  • 如果两个或更多 TLSRoute 资源声明完全相同的主机名,则创建时间戳 (metadata.creationTimestamp) 最早的路由优先。如果创建时间戳一致,则按字典顺序较小的 <namespace>/<name> 标识符优先。Gateway 控制器不接受冲突的路由,这些路由会报告 Accepted: False 条件和 Conflicted 原因。

如需详细了解路由优先级,请参阅路由合并、优先权和验证。

使用 ReferenceGrant 进行跨命名空间路由

在多租户环境中,集中式平台团队可以在基础设施命名空间(例如 gateway-infra)中管理网关,而应用团队则在开发者命名空间(例如 store-ns)中管理 TLSRoute 和 Service 资源。

  1. 在网关监听器中,允许来自所有命名空间(或所选命名空间)的路由:

    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. 在应用命名空间 (store-ns) 中,创建一个 ReferenceGrant 资源,以允许 gateway-infra 命名空间中的网关引用 TLSRoute 或 Service 资源:

    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) 中,创建引用共享网关的 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 直通网关

您可以在舰队中的多个 GKE 集群中部署具有 TLS 传递的多集群 Gateway:

  1. 使用受支持的多集群 GatewayClass(gke-l7-regional-external-managed-mc、gke-l7-rilb-mc 或 gke-l7-cross-regional-internal-managed-mc)。
  2. 使用 ServiceExport 资源在整个舰队中导出后端 Service。
  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
    

多集群网关中的多个 IP 地址

部署跨区域多集群网关时(例如使用 gke-l7-cross-regional-internal-managed-mc GatewayClass 时),您可以在 spec.addresses 字段中定义多个 IP 地址,以在不同区域中公开内部 VIP。

在单个 Gateway 中配置多个 IP 地址时,GKE Gateway 控制器会将每个 IP 地址转换为单独的转发规则。同时,控制器会引用共享目标代理,并重复使用相同的基础路由配置。控制器不会将底层 TLSRoute 资源乘以 IP 地址数量,这有助于防止路由重复和配额耗尽。

以下示例展示了在两个区域中配置了临时 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) 时,一个证书可以涵盖多个主机名。

由于第 4 层 TCP 代理负载均衡器在初始 TLS 握手后不会检查 HTTP 请求标头,因此负载均衡器会通过现有连接将合并的请求转发到最初匹配的后端 Pod。

为防止出现连接合并问题,请执行以下操作:

  • 使用不同的、不重叠的 TLS 证书:请勿在同一网关 IP 地址后多路复用的不同后端服务之间共享通配符证书或通用 SAN 身份。
  • 返回 HTTP 421 误导请求:将后端 HTTPS 服务器配置为检查 HTTP :authority(或 Host)标头。如果请求的主机与服务器的预期网域不匹配,则返回 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
    

后续步骤