部署网关

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

本页面介绍如何在 Google Kubernetes Engine (GKE) 上部署 Kubernetes 网关资源。此外,还介绍了如何部署专用网关和面向互联网的网关来公开应用,并演示了 Gateway API 资源模型的一些概念。

多集群负载均衡的网关部署目前为预览版。如需了解如何部署网关以实现多集群负载均衡,请参阅部署多集群网关

准备工作

在开始之前,请确保您已执行以下任务:

  • 启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 如果您要使用 Google Cloud CLI 执行此任务,请安装初始化 gcloud CLI。

GKE Gateway Controller 要求

  • 对于标准版,需要 GKE 1.24 或更高版本。
  • Google Cloud CLI 407.0.0 版或更高版本。
  • VPC 原生集群支持 Gateway API。
  • 如果您使用的是内部 GatewayClass,则必须启用代理专用子网
  • 如果您使用的是 Istio,则必须将 Istio 升级到以下版本之一:
    • 1.15.2 或更高版本
    • 1.14.5 或更高版本
    • 1.13.9 或更高版本。
  • 您不能将 Gateway v1alpha API 与 Istio 搭配使用。如需了解详情,请参阅 Kubernetes Gateway 和 Istio Gateway
  • Gateway API 不支持 networking.gke.io/app-protocols 注解。请改用 appProtocol 字段

限制和已知问题

存在以下限制:

  • GKE GatewayClass 支持不同的功能,具体取决于它们使用的负载均衡器。如需详细了解每个 GatewayClass 支持的不同功能,请参阅 GatewayClass 功能
  • 您可以在 Google Cloud 控制台中查看 GKE 为网关创建的负载均衡器资源,但这些资源不会引用它们所连接的网关或 GKE 集群。
  • 您无法在 Google Cloud 控制台中查看网关、HTTPRoute 和政策资源。您可以使用 Kubernetes API 发现和管理网关资源。
  • 您无法使用网关自动生成 Google 管理的 SSL 证书,但可以手动创建和引用 Google 管理的 SSL 证书。如需了解详情,请参阅保护网关安全
  • 您必须在同一宿主项目或服务项目中的 GKE 集群中部署网关。不支持跨项目部署。
  • 正式版中的内部 GatewayClass(内部 HTTP[S] 负载均衡器)支持流量管理功能。预览版中的 gke-l7-global-external-managedgke-l7-global-external-managed-mc GatewayClass 支持外部 GatewayClass(外部 HTTP[S] 负载均衡器)。
  • HTTPRoute 是唯一受支持的路由类型。不支持 TCPRoute、UDPRoute 和 TLSRoute。如需查看 GKE Gateway Controller 支持的字段列表,请参阅 GatewayClass 功能

GKE Gateway 不支持以下负载均衡功能:

  • Cloud CDN。
  • Identity-Aware Proxy。
  • Google Cloud Armor。
  • SSL 政策。
  • 从 HTTP 到 HTTPS 的重定向。
  • 自定义请求和响应标头
  • 将 GKE Gateway Controller 控制器与 Compute Engine 上的 Kubernetes(自行管理的 Kubernetes)搭配使用。
  • FrontendConfigBackendConfig 资源。请改用 Gateway 政策

在集群中启用 Gateway API

在 GKE 中使用网关资源之前,您的集群必须启用 Gateway API。此 API 由 --gateway-api 标志控制。您可以在启用时使用值 standard,在停用时使用 disabled

创建启用 Gateway API 的新集群

创建启用 Gateway API 的新 VPC 原生 GKE 集群:

gcloud container clusters create CLUSTER_NAME \
    --gateway-api=standard \
    --cluster-version=VERSION \
    --region=COMPUTE_REGION

请替换以下内容:

  • CLUSTER_NAME:集群的名称。
  • VERSION:GKE 版本,必须为 1.24 或更高版本。您还可以使用 --release-channel 标志来选择发布渠道。发布渠道必须具有默认版本 1.24 或更高版本。
  • COMPUTE_REGION:新集群的 Compute Engine 区域。对于区域级集群,请使用 --zone=COMPUTE_ZONE

--gateway-api=standard 标志指示 GKE 使用集群安装 v1beta1 CRD。

在现有集群上启用 Gateway API

更新现有 VPC 原生集群:

gcloud container clusters update CLUSTER_NAME \
    --gateway-api=standard \
    --region=COMPUTE_REGION

请替换以下内容:

  • CLUSTER_NAME:现有集群的名称。
  • COMPUTE_REGION:集群的 Compute Engine 区域。对于区域级集群,请使用 --zone=COMPUTE_ZONE

验证集群

创建或升级集群后,GKE Gateway Controller 会自动安装 GatewayClass。控制器可能需要几分钟时间才能识别 CRD 并安装 GatewayClass。

确认您的集群中安装了 GatewayClass:

kubectl get gatewayclass

输出类似于以下内容:

NAME                             CONTROLLER                  ACCEPTED   AGE
gke-l7-global-external-managed   networking.gke.io/gateway   True       16h
gke-l7-gxlb                      networking.gke.io/gateway   True       16h
gke-l7-rilb                      networking.gke.io/gateway   True       16h

如需了解每个 GatewayClass 的功能,请参阅 GatewayClass 功能

部署内部网关

内部网关公开只能从 VPC 或连接到 VPC 的网络访问的应用。

配置代理专用子网

您必须先配置代理专用子网,然后才能创建使用内部 HTTP(S) 负载均衡器的网关。在使用内部 HTTP(S) 负载均衡器的 VPC 中,每个区域都必须具有一个代理专用子网。此子网为负载均衡器代理提供了内部 IP 地址。

  1. 创建代理专用子网:

    gcloud compute networks subnets create SUBNET_NAME \
        --purpose=REGIONAL_MANAGED_PROXY \
        --role=ACTIVE \
        --region=REGION \
        --network=VPC_NETWORK_NAME \
        --range=CIDR_RANGE
    

    请替换以下内容:

    • SUBNET_NAME:代理专用子网的名称。
    • REGION:代理专用子网的区域。
    • VPC_NETWORK_NAME:包含子网的 VPC 网络的名称。
    • CIDR_RANGE:子网的主要 IP 地址范围。使用的子网掩码长度不得超过 /26,以确保至少有 64 个 IP 地址可用于该区域中的代理。建议的子网掩码为 /23
  2. 验证代理专用子网:

    gcloud compute networks subnets describe SUBNET_NAME \
        --region=REGION
    

    输出类似于以下内容:

    ...
    gatewayAddress: 10.1.1.1
    ipCidrRange: 10.1.1.0/24
    kind: compute#subnetwork
    name: proxy-subnet
    network: https://www.googleapis.com/compute/v1/projects/PROJECT_NAME/global/networks/default
    privateIpGoogleAccess: false
    privateIpv6GoogleAccess: DISABLE_GOOGLE_ACCESS
    purpose: REGIONAL_MANAGED_PROXY
    region: https://www.googleapis.com/compute/v1/projects/PROJECT_NAME/regions/REGION
    role: ACTIVE
    selfLink: https://www.googleapis.com/compute/v1/projects/PROJECT_NAME/regions/REGION/subnetworks/proxy-subnet
    state: READY
    

创建网关

网关资源表示在 Kubernetes 中路由流量的数据层面。网关可表示许多不同类型的负载均衡和路由,具体取决于派生网关的 GatewayClass。如需详细了解网关资源,请参阅网关资源说明或 API 规范

在这种情况下,GKE 集群的管理员需要创建一个网关,可供不同团队用于在内部公开其应用。管理员部署网关,应用团队独立部署其路由并将路由连接到此网关。

  1. 将以下网关清单保存到名为 gateway.yaml 的文件中:

    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: internal-http
    spec:
      gatewayClassName: gke-l7-rilb
      listeners:
      - name: http
        protocol: HTTP
        port: 80
    

    此清单包含以下字段:

    • gatewayClassName: gke-l7-rilb:指定派生此网关的 GatewayClass。gke-l7-rilb 对应于区域内部 HTTP(S) 负载均衡器。
    • port: 80 指定网关仅公开端口 80 来侦听 HTTP 流量。
  2. 在您的集群中部署网关:

    kubectl apply -f gateway.yaml
    
  3. 验证网关是否已正确部署。部署其所有资源可能需要几分钟时间。

    kubectl describe gateways.gateway.networking.k8s.io internal-http
    

    输出类似于以下内容:

    Name:         internal-http
    Namespace:    default
    Spec:
      Gateway Class Name:  gke-l7-rilb
      Listeners:
        Allowed Routes:
          Kinds:
            Group:  gateway.networking.k8s.io
            Kind:   HTTPRoute
          Namespaces:
            From:  Same
        Name:      http
        Port:      80
        Protocol:  HTTP
    Status:
      Addresses:
        Type:   IPAddress
        Value:  192.168.1.14
      Conditions:
        Last Transition Time:  1970-01-01T00:00:00Z
        Message:               Waiting for controller
        Reason:                NotReconciled
        Status:                False
        Type:                  Scheduled
    Events:
      Type    Reason  Age                From                       Message
      ----    ------  ----               ----                       -------
      Normal  ADD     92s                networking.gke.io/gateway  test/internal-http
      Normal  UPDATE  45s (x3 over 91s)  networking.gke.io/gateway  test/internal-http
      Normal  SYNC    45s                networking.gke.io/gateway  SYNC on test/internal-http was a success
    

    此时,您的集群中部署了一个网关,该网关已预配负载均衡器和 IP 地址。但是,该网关没有路由,因此它还不知道应该如何将流量发送到后端。如果没有路由,所有流量都会流向默认后端,这会返回 HTTP 404。接下来,您将部署应用和路由,告知网关如何到达应用后端。

部署演示应用

应用团队可以独立于网关的部署部署他们的应用和路由。在某些情况下,应用团队可能还希望拥有网关,并自行将其部署为专用于应用的资源。如需了解网关和路由的不同所有权模型,请参阅路由绑定。不过在此示例中,商店团队会部署其应用以及通过在上一部分中创建的 internal-http 网关公开其应用的附带 HTTPRoute。

HTTPRoute 资源有许多可配置的字段可用于流量匹配。如需了解 HTTPRoute 字段的说明,请参阅 API 规范

  1. 将商店应用(store-v1、store-v2 和 store-german 部署)部署到您的集群:

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/app/store.yaml
    

    这将创建三个 Deployment 和三个 Service,分别名为 store-v1、store-v2 和 store-german。

  2. 验证应用已成功部署:

    kubectl get pod
    

    应用运行后,输出类似于以下内容:

    NAME                        READY   STATUS    RESTARTS   AGE
    store-german-66dcb75977-5gr2n   1/1     Running   0          38s
    store-v1-65b47557df-jkjbm       1/1     Running   0          14m
    store-v2-6856f59f7f-sq889       1/1     Running   0          14m
    
  3. 验证 Service 已部署:

    kubectl get service
    

    输出会显示每个商店 Deployment 的 Service:

    NAME           TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
    store-german   ClusterIP   10.48.3.183   <none>        8080/TCP   4s
    store-v1       ClusterIP   10.48.2.224   <none>        8080/TCP   5s
    store-v2       ClusterIP   10.48.4.48    <none>        8080/TCP   5s
    

部署 HTTPRoute

路由资源定义了用于将流量从网关映射到 Kubernetes 后端的特定于协议的规则。HTTPRoute 资源会执行 HTTP 和 HTTPS 流量匹配和过滤,并且受所有 gke-l7 GatewayClass 支持。

在本部分中,您将部署一个 HTTPRoute,它会使用访问您的商店应用所需的路由规则对网关进行编程。

  1. 将以下 HTTPRoute 清单保存到名为 store-route.yaml 的文件中:

    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: store
    spec:
      parentRefs:
      - kind: Gateway
        name: internal-http
      hostnames:
      - "store.example.com"
      rules:
      - backendRefs:
        - name: store-v1
          port: 8080
      - matches:
        - headers:
          - name: env
            value: canary
        backendRefs:
        - name: store-v2
          port: 8080
      - matches:
        - path:
            value: /de
        backendRefs:
        - name: store-german
          port: 8080
    
  2. 在集群中部署 HTTPRoute:

    kubectl apply -f store-route.yaml
    

    store HTTPRoute 使用 parentRefs 属性绑定到 internal-http 网关:这些路由规则在底层的负载均衡器上配置,如下图所示:

    由商店 HTTPRoute 配置的路由规则

    这些路由规则通过以下方式处理 HTTP 流量:

    • 发送到 store.example.com/de 的流量路由到 Service store-german
    • 发送到 store.example.com 且具有 HTTP 标头 "env: canary" 的流量路由到 Service store-v2
    • 发送到 store.example.com 的其余流量路由到 Service store-v1
  3. 验证 HTTPRoute 已部署:

    kubectl describe httproute store
    

    输出类似于以下内容:

    Name:         store
    Namespace:    default
    Labels:       <none>
    Annotations:  <none>
    API Version:  gateway.networking.k8s.io/v1beta1
    Kind:         HTTPRoute
    <...>
    Spec:
      Hostnames:
        store.example.com
      Parent Refs:
        Group:  gateway.networking.k8s.io
        Kind:   Gateway
        Name:   internal-http
      Rules:
        Backend Refs:
          Group:
          Kind:    Service
          Name:    store-v1
          Port:    8080
          Weight:  1
        Matches:
          Path:
            Type:   PathPrefix
            Value:  /
        Backend Refs:
          Group:
          Kind:    Service
          Name:    store-v2
          Port:    8080
          Weight:  1
        Matches:
          Headers:
            Name:   env
            Type:   Exact
            Value:  canary
          Path:
            Type:   PathPrefix
            Value:  /
        Backend Refs:
          Group:
          Kind:    Service
          Name:    store-german
          Port:    8080
          Weight:  1
        Matches:
          Path:
            Type:   PathPrefix
            Value:  /de
    Status:
      Parents:
        Conditions:
          Last Transition Time:  2022-11-01T04:18:52Z
          Message:
          Reason:                Accepted
          Status:                True
          Type:                  Accepted
          Last Transition Time:  2022-11-01T04:18:52Z
          Message:
          Reason:                ReconciliationSucceeded
          Status:                True
          Type:                  Reconciled
        Controller Name:         networking.gke.io/gateway
        Parent Ref:
          Group:  gateway.networking.k8s.io
          Kind:   Gateway
          Name:   internal-http
    Events:
      Type    Reason  Age                From                   Message
      ----    ------  ----               ----                   -------
      Normal  ADD     24m                sc-gateway-controller  default/store
      Normal  SYNC    16m (x4 over 23m)  sc-gateway-controller  Bind of HTTPRoute "default/store" to ParentRef {Group:       gateway.networking.k8s.io",
      <...>
    
  4. 验证 HTTPRoute 已绑定到网关:

    kubectl describe gateway
    

    输出类似于以下内容:

    Name:         internal-http
    Namespace:    default
    Labels:       <none>
    <...>
    Status:
      Addresses:
        Type:   IPAddress
        Value:  10.128.15.203
      Conditions:
        Last Transition Time:  2022-11-01T03:47:01Z
        Message:
        Reason:                Scheduled
        Status:                True
        Type:                  Scheduled
        Last Transition Time:  2022-11-01T03:47:01Z
        Message:
        Reason:                Ready
        Status:                True
        Type:                  Ready
      Listeners:
        Attached Routes:  1
        Conditions:
          Last Transition Time:  2022-11-01T03:47:01Z
          Message:
          Reason:                Ready
          Status:                True
          Type:                  Ready
        Name:                    http
        Supported Kinds:
          Group:  gateway.networking.k8s.io
          Kind:   HTTPRoute
          <...>
    

将流量发送到应用

现在,网关、路由和应用已在集群中部署,您可以将流量传递到您的应用。

  1. 从网关检索 IP 地址,以便将流量发送到您的应用:

    kubectl get gateways.gateway.networking.k8s.io internal-http -o=jsonpath="{.status.addresses[0].value}"
    

    输出为一个 IP 地址。

  2. 在与集群连接的虚拟机 (VM) 实例上,通过 shell 将流量发送到此 IP 地址。为此,您可以创建一个虚拟机。这是必要的操作,因为网关具有内部 IP 地址,并且只能从 VPC 网络内部访问。由于 internal-http 是区域级负载均衡器,因此客户端 shell 必须与 GKE 集群位于同一区域。

    由于您没有 example.com 主机名,因此请手动设置主机标头,以便观察流量路由。首先尝试请求 store.example.com:

    curl -H "host: store.example.com" VIP
    

    VIP 替换为上一步中的 IP 地址。

    演示应用的输出显示了应用运行位置的相关信息:

    {
      "cluster_name": "gke1",
      "host_header": "store.example.com",
      "metadata": "store-v1",
      "node_name": "gke-gke1-pool-2-bd121936-5pfc.c.gateway-demo-243723.internal",
      "pod_name": "store-v1-84b47c7f58-pmgmk",
      "pod_name_emoji": "💇🏼‍♀️",
      "project_id": "gateway-demo-243723",
      "timestamp": "2022-10-25T13:31:17",
      "zone": "us-central1-a"
    }
    
  3. 转到 store.example.com/de 中的德语版商店服务,测试路径匹配:

    curl -H "host: store.example.com" VIP/de
    

    输出会确认请求已由 store-german Pod 处理:

    {
      "cluster_name": "gke1",
      "host_header": "store.example.com",
      "metadata": "Gutentag!", 
      "node_name": "gke-gke1-pool-2-bd121936-n3xn.c.gateway-demo-243723.internal",
      "pod_name": "store-german-5cb6474c55-lq5pl", 
      "pod_name_emoji": "🧞‍♀",
      "project_id": "gateway-demo-243723",
      "timestamp": "2022-10-25T13:35:37",
      "zone": "us-central1-a"
    }
    
  4. 最后,使用 env: canary HTTP 标头将流量发送到 Canary 版商店服务:

    curl -H "host: store.example.com" -H "env: canary " VIP
    

    输出会确认请求已由 store-v2 Pod 处理:

    {
      "cluster_name": "gke1",
      "host_header": "store.example.com",
      "metadata": "store-v2", 
      "node_name": "gke-gke1-pool-2-bd121936-5pfc.c.gateway-demo-243723.internal",
      "pod_name": "store-v2-5788476cbd-s9thb", 
      "pod_name_emoji": "🦰",
      "project_id": "gateway-demo-243723",
      "timestamp": "2022-10-25T13:38:26",
      "zone": "us-central1-a"
    }
    

部署外部网关

外部网关公开可从互联网或 VPC 外部网络访问的应用。外部网关部署与内部网关部署类似,但您必须保护应用的安全,因为公共互联网可以访问网关。

作为平台管理员,您可能需要使用外部网关公开应用。以下示例展示了如何将多个证书附加到网关并将证书分组到证书映射,并使用 Certificate Manager 和 HTTPRoute 公开商店应用。

创建证书映射

如果每个网关需要 15 个或更多证书或者您需要使用通配符证书,Google 建议使用 Certificate Manager 来管理证书。

您还可以使用 Kubernetes Secret 或 Google 管理的 SSL 证书来保护外部网关。如需了解详情,请参阅网关安全性

在本部分中,您将使用 Certificate Manager 创建证书,以保护集群上运行的应用。

  1. 启用 Certificate Manager API:

    gcloud services enable certificatemanager.googleapis.com
    
  2. 创建证书映射:

    gcloud beta certificate-manager maps create store-example-com-map
    
  3. 将 Google 管理的证书和密钥加载到证书中:

    gcloud beta certificate-manager certificates create store-example-com-cert \
        --certificate-file="CERTIFICATE_FILE" \
        --private-key-file="PRIVATE_KEY_FILE"
    

    请替换以下内容:

    • CERTIFICATE_FILE:您选择的新文件的名称。文件必须具有扩展名 .pem。例如 cert.pem
    • PRIVATE_KEY_FILE:您的私钥文件的名称。

    如需了解详情,请参阅创建私钥和证书

  4. 创建将证书分配给证书映射的 CertificateMapEntry

    gcloud beta certificate-manager maps entries create store-example-com-map-entry \
        --map=store-example-com-map \
        --hostname=store.example.com \
        --certificates=store-example-com-cert
    

如需了解如何使用其他证书来源(例如 Kubernetes Secret 或 SSL 证书)保护网关,请参阅保护网关安全

创建网关

网关资源表示在 Kubernetes 中路由流量的数据层面。网关可以表示许多不同类型的负载均衡和路由,具体取决于其使用的 GatewayClass。

如需详细了解网关资源,请参阅网关资源说明或 API 规范

在本部分中,您将创建一个网关。应用团队可以独立部署路由并将其安全地附加到网关,从而使用网关将应用公开给互联网。

  1. 将以下清单保存到名为 gateway.yaml 的文件中:

    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: external-http
      annotations:
        networking.gke.io/certmap: store-example-com-map
    spec:
      gatewayClassName: gke-l7-gxlb
      listeners:
      - name: https
        protocol: HTTPS
        port: 443
    

    此清单描述了具有以下字段的网关:

    • gatewayClassName: gke-l7-gxlb:指定此网关的 GatewayClass。此网关类使用全球外部 HTTP(S) 负载均衡器(经典版)。
    • protocol: HTTPSport: 443:指定网关为 HTTPS 流量公开端口 443。这些字段会启用 TLS。
    • networking.gke.io/certmap: store-example-com-map:指定 Certificate Manager 中证书映射的名称。

    没有 TLS 部分,因为 TLS 是使用注解 networking.gke.io/certmap 以及使用 Certificate Manager 配置的。

  2. 将清单应用到您的集群:

    kubectl apply -f gateway.yaml
    

    GKE 可能需要几分钟时间来部署资源。

  3. 验证网关已成功部署:

    kubectl describe gateways.gateway.networking.k8s.io external-http
    

    输出类似于以下内容:

    Spec:
      Gateway Class Name:  gke-l7-gxlb
      Listeners:
        Allowed Routes:
          Namespaces:
            From:  Same
        Name:      https
        Port:      443
        Protocol:  HTTPS
    Status:
      Addresses:
        Type:   IPAddress
        Value:  34.149.207.45
      Conditions:
        Last Transition Time:  2022-11-01T05:37:21Z
        Message:
        Reason:                Scheduled
        Status:                True
        Type:                  Scheduled
        Last Transition Time:  2022-11-01T05:37:21Z
        Message:
        Reason:                Ready
        Status:                True
        Type:                  Ready
      Listeners:
        Attached Routes:  0
        Conditions:
          Last Transition Time:  2022-11-01T05:37:21Z
          Message:
          Reason:                Ready
          Status:                True
          Type:                  Ready
        Name:                    https
        Supported Kinds:
          Group:  gateway.networking.k8s.io
          Kind:   HTTPRoute
    Events:
      Type    Reason  Age               From                   Message
      ----    ------  ----              ----                   -------
      Normal  ADD     50s               sc-gateway-controller  default/external-http
      Normal  UPDATE  5s (x3 over 50s)  sc-gateway-controller  default/external-http
      Normal  SYNC    5s                sc-gateway-controller  SYNC on default/external-http was a success
    

    此输出显示集群中部署的网关具有负载均衡器和公共 IP 地址。该网关没有路由,这意味着它无法将流量发送到后端。如果没有路由,所有流量都会流向默认后端,这会返回 HTTP 404 响应。在下一部分中,您将部署路由,以指示网关将流量发送到后端。

部署演示应用

应用团队可以独立于网关的部署部署他们的应用和路由。在某些情况下,应用团队可能还希望拥有网关,并自行将其部署为专用于其应用的资源。如需了解网关和路由的不同所有权模型,请参阅路由绑定。在此示例中,商店团队会部署应用和附带的 HTTPRoute,以通过在上一部分中创建的 external-http 网关公开应用。

如需详细了解 HTTPRoute 字段,请参阅 API 规范

  1. 将示例应用部署到您的集群:

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/app/store.yaml
    

    此示例应用创建了三个 Deployment 和三个 Service,分别名为 store-v1store-v2store-german

  2. 验证应用已成功部署:

    kubectl get pod
    

    输出类似于以下内容:

    NAME                            READY   STATUS    RESTARTS   AGE
    store-german-66dcb75977-5gr2n   1/1     Running   0          38s
    store-v1-65b47557df-jkjbm       1/1     Running   0          14m
    store-v2-6856f59f7f-sq889       1/1     Running   0          14m
    
  3. 验证 Service 已成功部署:

    kubectl get service
    

    输出类似于以下内容:

    NAME           TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
    store-german   ClusterIP   10.48.3.183   <none>        8080/TCP   4s
    store-v1       ClusterIP   10.48.2.224   <none>        8080/TCP   5s
    store-v2       ClusterIP   10.48.4.48    <none>        8080/TCP   5s
    

创建 HTTPRoute

路由资源定义了用于将流量从网关映射到 Kubernetes 后端的特定于协议的规则。HTTPRoute 资源会执行 HTTP 和 HTTPS 流量匹配和过滤,并且受所有 gke-l7-* GatewayClass 支持。

在本部分中,您将部署 HTTPRoute,它会使用访问示例应用所需的路由规则来配置网关。

  1. 将以下清单保存到名为 store-route-external.yaml 的文件中:

    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: store-external
    spec:
      parentRefs:
      - kind: Gateway
        name: external-http
      hostnames:
      - "store.example.com"
      rules:
      - backendRefs:
        - name: store-v1
          port: 8080
      - matches:
        - headers:
          - name: env
            value: canary
        backendRefs:
        - name: store-v2
          port: 8080
      - matches:
        - path:
            value: /de
        backendRefs:
        - name: store-german
          port: 8080
    

    此清单描述了引用 external-http 网关的 HTTPRoute。

  2. 将清单应用到您的集群:

    kubectl apply -f store-route-external.yaml
    

    store HTTPRoute 使用 parentRefs 属性绑定到 external-http 网关:下图展示了在底层负载均衡器上配置的路由规则:

    由商店 HTTPRoute 配置的路由规则

    路由规则对 HTTP 流量的处理方式如下:

    • 发送到 store.example.com/de 的流量路由到 Service store-german
    • 发送到 store.example.com 且具有 HTTP 标头 "env: canary" 的流量路由到 Service store-v2
    • 发送到 store.example.com 的其余流量路由到 Service store-v1
  3. 验证 HTTPRoute 已部署:

    kubectl describe httproute store-external
    

    输出类似于以下内容:

    Name:         store-external
    Namespace:    default
    Labels:       <none>
    Annotations:  <none>
    API Version:  gateway.networking.k8s.io/v1beta1
    Kind:         HTTPRoute
    <...>
    Spec:
      Hostnames:
        store.example.com
      Parent Refs:
        Group:  gateway.networking.k8s.io
        Kind:   Gateway
        Name:   external-http
      Rules:
        Backend Refs:
          Group:
          Kind:    Service
          Name:    store-v1
          Port:    8080
          Weight:  1
        Matches:
          Path:
            Type:   PathPrefix
            Value:  /
        Backend Refs:
          Group:
          Kind:    Service
          Name:    store-v2
          Port:    8080
          Weight:  1
        Matches:
          Headers:
            Name:   env
            Type:   Exact
            Value:  canary
          Path:
            Type:   PathPrefix
            Value:  /
        Backend Refs:
          Group:
          Kind:    Service
          Name:    store-german
          Port:    8080
          Weight:  1
        Matches:
          Path:
            Type:   PathPrefix
            Value:  /de
    Status:
      Parents:
        Conditions:
          Last Transition Time:  2022-11-01T05:42:31Z
          Message:
          Reason:                Accepted
          Status:                True
          Type:                  Accepted
          Last Transition Time:  2022-11-01T05:43:18Z
          Message:
          Reason:                ReconciliationSucceeded
          Status:                True
          Type:                  Reconciled
        Controller Name:         networking.gke.io/gateway
        Parent Ref:
          Group:  gateway.networking.k8s.io
          Kind:   Gateway
          Name:   external-http
    Events:
      Type     Reason  Age    From                   Message
      ----     ------  ----   ----                   -------
      Normal   ADD     2m48s  sc-gateway-controller  default/store-external
      Normal  SYNC  61s (x3 over 2m27s)  sc-gateway-controller  Bind of HTTPRoute "default/store-external" to ParentRef Group:       "gateway.networking.k8s.io",
      ...
    
  4. 验证 HTTPRoute 已绑定到网关:

    kubectl describe gateway external-http
    

    输出类似于以下内容:

    Name:         external-http
    Namespace:    default
    Labels:       <none>
    <...>
    Status:
      Addresses:
        Type:   IPAddress
        Value:  34.149.207.45
      Conditions:
        Last Transition Time:  2022-11-01T05:37:21Z
        Message:
        Reason:                Scheduled
        Status:                True
        Type:                  Scheduled
        Last Transition Time:  2022-11-01T05:43:18Z
        Message:
        Reason:                Ready
        Status:                True
        Type:                  Ready
      Listeners:
        Attached Routes:  1
        Conditions:
          Last Transition Time:  2022-11-01T05:43:18Z
          Message:
          Reason:                Ready
          Status:                True
          Type:                  Ready
        Name:                    https
        Supported Kinds:
          Group:  gateway.networking.k8s.io
          Kind:   HTTPRoute
          <...>
    

将流量发送到应用

现在,网关、路由和应用已在集群中部署,您可以将流量传递到您的应用。

  1. 获取网关的 IP 地址:

    kubectl get gateways.gateway.networking.k8s.io external-http -o=jsonpath="{.status.addresses[0].value}"
    

    输出为一个 IP 地址。

  2. 创建一个虚拟机:

    gcloud cloud-shell ssh
    
  3. 将流量从虚拟机发送到网关 IP 地址。您必须手动设置主机标头,因为您不拥有 example.com 主机名。

    curl -H "host: store.example.com" https://GATEWAY_IP_ADDRESS --resolve store.example.com:443:GATEWAY_IP_ADDRESS --cacert cacert.pem -v
    

    GATEWAY_IP_ADDRESS 替换为上一步中网关的 IP 地址。

    输出显示来自演示版应用有关应用运行位置的信息:

    {
      "cluster_name": "gke1",
      "host_header": "store.example.com",
      "metadata": "store-v1",
      "node_name": "gke-gke1-pool-2-bd121936-5pfc.c.gateway-demo-243723.internal",
      "pod_name": "store-v1-84b47c7f58-pmgmk",
      "pod_name_emoji": "💇🏼‍♀️",
      "project_id": "gateway-demo-243723",
      "timestamp": "2022-09-25T13:31:17",
      "zone": "us-central1-a"
    }
    
  4. 转到 store.example.com/de 中的德语版 store 服务,以测试路径匹配:

    curl -H "host: store.example.com" https://GATEWAY_IP_ADDRESS/de --resolve store.example.com:443:GATEWAY_IP_ADDRESS --cacert cacert.pem -v
    

    输出会确认请求已由 store-german Pod 处理:

    {
      "cluster_name": "gke1",
      "host_header": "store.example.com",
      "metadata": "Gutentag!",
      "node_name": "gke-gke1-pool-2-bd121936-n3xn.c.gateway-demo-243723.internal",
      "pod_name": "store-german-5cb6474c55-lq5pl",
      "pod_name_emoji": "🧞‍♀",
      "project_id": "gateway-demo-243723",
      "timestamp": "2022-09-25T13:35:37",
      "zone": "us-central1-a"
    }
    
  5. 使用 env: canary HTTP 标头将流量发送到 store 服务的 Canary 版本:

    curl -H "host: store.example.com" -H "env: canary " https://GATEWAY_IP_ADDRESS/de --resolve store.example.com:443:GATEWAY_IP_ADDRESS --cacert cacert.pem -v
    

    输出会确认请求已由 store-v2 Pod 处理:

    {
      "cluster_name": "gke1",
      "host_header": "store.example.com",
      "metadata": "store-v2",
      "node_name": "gke-gke1-pool-2-bd121936-5pfc.c.gateway-demo-243723.internal",
      "pod_name": "store-v2-5788476cbd-s9thb",
      "pod_name_emoji": "👩🏿",
      "project_id": "gateway-demo-243723",
      "timestamp": "2022-09-25T13:38:26",
      "zone": "us-central1-a"
    }
    

使用共享 Gateway

Gateway API 使用单独的资源(网关和路由资源)来部署负载均衡器和路由规则。这与 Ingress 不同,Ingress 会将所有内容组合到一个资源中。通过在资源之间拆分责任,网关可让负载均衡器及其路由规则单独部署,并由不同用户或团队进行部署。这样一来,网关即可成为共享网关,这些网关与多个不同路由连接,可以完全由独立团队拥有和管理,甚至可以跨不同命名空间。

针对共享网关部署路由

此示例基于部署内部 Gateway中部署的 internal-http Gateway。

在此示例中,网站团队将部署其应用、Service 和HTTPRoute,以将来自 Gateway 的流量与这些 Service 匹配。

  1. 部署示例应用:

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/app/site.yaml
    
  2. 将以下清单保存到名为 site-route-internal.yaml 的文件中:

    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: site-internal
    spec:
      parentRefs:
      - kind: Gateway
        name: internal-http
      hostnames:
      - "site.example.com"
      rules:
      - backendRefs:
        - name: site-v1
          port: 8080
    

    此清单描述了一个与 site.example.com 的所有流量匹配并将其路由到 site-v1 Service 的 HTTPRoute。

  3. 将清单应用到您的集群:

    kubectl apply -f site-route-internal.yaml
    
  4. 验证 HTTPRoute 是否已连接到 Gateway:

    kubectl describe httproute.gateway.networking.k8s.io site-internal
    

    输出类似于以下内容:

    Status:
      Parents:
        Conditions:
          Last Transition Time:  2023-01-09T15:05:43Z
          Message:
          Reason:                Accepted
          Status:                True
          Type:                  Accepted
          Last Transition Time:  2023-01-09T15:05:43Z
          Message:
          Reason:                ReconciliationSucceeded
          Status:                True
          Type:                  Reconciled
        Controller Name:         networking.gke.io/gateway
        Parent Ref:
          Group:  gateway.networking.k8s.io
          Kind:   Gateway
          Name:   internal-http
          ...
    

    如果 Gateway 的允许条件为 True,则表示 HTTPRoute 已成功绑定到 Gateway。如需详细了解状态字段,请参阅路由状态

  5. 验证流向 Gateway 的流量是否已正确路由:

    curl -H "host: site.example.com" GATEWAY_IP_ADDRESS
    curl -H "host: store.example.com" GATEWAY_IP_ADDRESS
    

    GATEWAY_IP_ADDRESS 替换为 Gateway 的 IP 地址。

    您必须在 Gateway 所在的 VPC 中使用虚拟机 (VM)。

    输出类似于以下内容:

    {
      "cluster_name": "CLUSTER_NAME",
      "host_header": "site.example.com",
      "metadata": "site-v1",
      "pod_name": "site-v1-5d64fc4d7d-fz6f6",
      "pod_name_emoji": "👩🏼‍🍳",
      "project_id": "PROJECT_ID",
      "timestamp": "2022-11-02T19:07:01",
      "zone": "us-central1-a"
    }
    ...
    {
      "cluster_name": "CLUSTER_NAME",
      "host_header": "store.example.com",
      "metadata": "store-v1",
      "pod_name": "store-v1-6d8d58d78-vz8pn",
      "pod_name_emoji": "🧝🏻‍♂️",
      "project_id": "PROJECT_ID",
      "timestamp": "2022-11-02T19:07:01",
      "zone": "us-central1-a"
    }
    

配置 Gateway 默认后端

所有 gke-l7-* GatewayClass 都会向不匹配的流量返回 HTTP 404。您可以使用显式默认 Route 配置默认后端,该路由会将不匹配的流量发送到用户提供的 Service。

以下 HTTPRoute 示例演示了如何自定义默认后端。如果您应用与以下内容类似的 HTTPRoute,则其优先于隐式默认后端:

kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
  name: custom-default-backend
spec:
  parentRefs:
  - kind: Gateway
    name: my-internal-gateway
  rules:
  - backendRefs:
    - name: my-custom-default-backend-service
      port: 8080

此 HTTPRoute 匹配来自特定网关的所有流量。每个 Gateway 只能有一个规则,否则规则会发生冲突并且优先顺序适用。

您可以使用默认后端来阻止他人创建路由所有 Gateway 流量的默认路由后端。显式 HTTPRoute 始终优先于具有冲突路由规则的新 HTTPRoute。

为 Gateway 配置静态 IP 地址

每个 Gateway 都有一个 IP 地址,用于监听流量。如果您未在 Gateway 上指定 IP 地址,则 Gateway 控制器会自动提供 IP 地址。您还可以创建静态 IP 地址,以使 IP 地址的存在与 Gateway 生命周期无关。

部署网关后,其 IP 地址会显示在状态字段中:

kind: Gateway
...
status:
  addresses:
    - value: 10.15.32.3

根据 GatewayClass,IP 地址是从以下子网分配的:

GatewayClass 默认 IP 地址池
  • gke-l7-rilb
  • gke-l7-rilb-rc
  • 主节点 IP 地址范围内的区域级专用 IP 地址
  • gke-l7-gxlb
  • gke-l7-gxlb-mc
  • gke-l7-global-external-managed
  • gke-l7-global-external-managed-mc
  • Google 的公共 IP 范围中的全球公共 IP 地址

    addresses.NamedAddress 字段可让您指定独立于网关的 IP 地址。您可以在网关部署之前创建静态 IP 地址资源,并且 NamedAddress 会引用该资源。即使网关已删除,您也可以重复使用静态 IP 地址。

    使用已命名的 IP 地址

    您可以通过指定 NamedAddress 来配置 IP 地址。您必须先预配静态 IP 地址,然后才能创建 Gateway。

    1. 为全球级或区域级 Gateway 创建静态 IP 地址资源:

      gcloud compute addresses create IP_ADDRESS_NAME \
          --purpose=SHARED_LOADBALANCER_VIP \
          --region=REGION \
          --subnet=SUBNET \
          --project=PROJECT_ID
      

      请替换以下内容:

      • IP_ADDRESS_NAME:新的静态 IP 地址的名称
      • REGION:对于区域级 Gateway,是集群在其中运行的 Compute Engine 区域。对于全球级外部 Gateway,不需要此标志。
      • SUBNET:IP 地址的子网。对于全球级外部 Gateway,不需要此标志。
      • PROJECT_ID:GKE 集群在其中运行的项目。
    2. 将以下清单保存到名为 named-ip-gateway.yaml 的文件中:

      kind: Gateway
      apiVersion: gateway.networking.k8s.io/v1beta1
      metadata:
        name: internal-http
      spec:
        gatewayClassName: gke-l7-rilb
        listeners:
        - name: http
          protocol: HTTP
          port: 80
        addresses:
        - type: NamedAddress
          value: IP_ADDRESS_NAME
      

      此清单描述了一个引用已命名的 IP 地址的 Gateway。

    3. 将清单应用到您的集群:

      kubectl apply -f named-ip-gateway.yaml
      
    4. 验证 Gateway IP 地址:

      kubectl describe gateway internal-http
      

      输出类似于以下内容:

      Name:         internal-http
      Namespace:    default
      Labels:       <none>
      ...
      Spec:
        Addresses:
          Type:              NamedAddress
          Value:             IP_ADDRESS_NAME
        Gateway Class Name:  gke-l7-rilb
        Listeners:
          Allowed Routes:
            Namespaces:
              From:  Same
          Name:      http
          Port:      80
          Protocol:  HTTP
      Status:
        Addresses:
          Type:   IPAddress
          Value:  10.15.32.103
      

    路由状态

    HTTPRoute 资源会发出条件和事件,以帮助用户了解 HTTPRoute 是否已成功与一个或多个 Gateway 绑定,或者 HTTPRoute 是否被拒绝。

    HTTPRoute 条件

    HTTPRoute 条件指示 Route 的状态及其绑定到的 Gateway。由于一个 Route 可以绑定到多个 Gateway,因此这是一个 Gateway 以及 Route 与每个 Gateway 之间的各个条件的列表。

    • Accepted=True 表示 HTTPRoute 已成功绑定到 Gateway。
    • Accepted=False 表示 HTTPRoute 已被拒绝与此 Gateway 绑定。

    如果 Gateway bindings 标题下未列出任何 Gateway,则 HTTPRoute 标签和 Gateway 标签选择器可能不匹配。如果 Route 未被任何 Gateway 选择,则可能会发生这种情况。

    HTTPRoute 事件

    HTTPRoute 事件提供有关 HTTPRoute 状态的详细信息。事件分组的原因如下:

    • ADD 事件由添加的资源触发。
    • UPDATE 事件由更新的资源触发。
    • SYNC 事件由定期协调触发。

    路由合并、优先权和验证

    路由优先权

    Gateway API 针对具有重叠路由规则的 Route 如何匹配流量定义了严格的优先权规则。两个重叠 HTTPRoute 之间的优先权如下所示:

    1. 主机名合并:最长/最具体的主机名匹配。
    2. 路径合并:最长/最具体的路径匹配。
    3. 标头合并:匹配的 HTTP 标头的最大数。
    4. 冲突:如果前三个规则未确立优先权,则优先权转给时间戳最早的 HTTPRoute 资源。

    路由合并

    对于 gke-l7 GatewayClass,给定网关的所有 HTTPRoutes 都会合并到同一个网址映射资源中。HTTPRoute 的合并在一起的方式取决于 HTTPRoute 之间的重叠类型。来自前面的示例的 HTTPRoute 可以拆分为三个单独的 HTTPRoute,用于说明路由合并和优先权:

    1. 路由合并:所有三个 HTTPRoute 都与同一 internal-http 网关连接,因此它们会合并在一起。
    2. 主机名合并:对于 store.example.com,所有三个路由都匹配,因此它们的主机名规则会合并。
    3. 路径合并:store-german-route 具有更具体的路径 /de,因此不会进一步合并。store-v1-route 和 store-v2-route 同样与同一 /* 路径匹配,因此它们会在该路径上合并。
    4. 标头合并:store-v2-route 具有一组比 store-v1-route 更具体的 HTTP 标头匹配项,因此不会进一步合并。
    5. 冲突:由于路由可以按主机名、路径和标头进行合并,因此不会发生冲突,并且所有路由规则都将应用于流量。

    前面的示例中使用的单个 HTTPRoute 相当于这三个单独的路由:

    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: store-v1-route
    spec:
      parentRefs:
      - kind: Gateway
        name: internal-http
      hostnames:
      - "store.example.com"
      rules:
      - backendRefs:
        - kind: Service
          name: store-v1
          port: 8080
    ---
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: store-v2-route
    spec:
      parentRefs:
      - kind: Gateway
        name: internal-http
      hostnames:
      - "store.example.com"
      rules:
      - matches:
        - headers:
          - type: Exact
            name: env
            value: canary
        backendRefs:
        - kind: Service
          name: store-v2
          port: 8080
    ---
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: store-german-route
    spec:
      parentRefs:
      - kind: Gateway
        name: internal-http
      hostnames:
      - "store.example.com"
      rules:
      - matches:
        - path:
            type: PathPrefix
            value: /de
        backendRefs:
        - kind: Service
          name: store-german
          port: 8080
    

    Kubernetes 网关和 Istio 网关

    请注意,Kubernetes Gateway API 和 Istio API 都有一个名为 Gateway 的资源。虽然它们执行类似的功能,但并不是相同的资源。如果您在同一 Kubernetes 集群中同时使用 Istio 和 Gateway API,则当您在命令行中使用 kubectl 时,这些名称将重叠。kubectl get gateway 可能会返回 Kubernetes 网关资源,但不会返回 Istio 网关资源,反之亦然。

    $ kubectl api-resources
    NAME       SHORTNAMES   APIGROUP                       NAMESPACED   KIND
    gateways   gw           networking.istio.io/v1beta1    true         Gateway
    gateways   gtw          networking.k8s.io/v1beta1      true         Gateway
    

    如果您使用的是 Istio 并升级到 GKE 1.20 及更高版本,则建议开始使用网关资源简称或指定 API 组。Kubernetes 网关的简称为 gtw,Istio 网关的简称为 gw。以下命令会分别返回 Kubernetes 网关资源和 Istio 网关资源。

    # Kubernetes Gateway
    $ kubectl get gtw
    NAME                        CLASS
    multi-cluster-gateway       gke-l7-gxlb-mc
    
    $ kubectl get gateway.networking.x-k8s.io
    NAME                        CLASS
    multi-cluster-gateway       gke-l7-gxlb-mc
    
    # Istio Gateway
    $ kubectl get gw
    NAME               AGE
    bookinfo-gateway   64m
    
    $ kubectl get gateway.networking.istio.io
    NAME               AGE
    bookinfo-gateway   64m
    

    问题排查

    内部网关缺少代理专用子网

    创建内部网关时可能会出现以下问题:

    generic::invalid_argument: error ensuring load balancer: Insert: Invalid value for field 'resource.target': 'regions/[REGION_NAME]/targetHttpProxies/gkegw-x5vt-default-internal-http-[ID]'. A reserved and active subnetwork is required in the same region and VPC as the forwarding rule.
    

    如需解决此问题,请配置代理专用子网

    后续步骤