通过云原生API网关使用Gateway API暴露服务

更新时间:
复制为 MD 格式

Gateway APIKubernetes社区推出的下一代服务网络模型API,云原生API网关(APIG)支持Kubernetes Gateway API标准,您可以通过声明式的GatewayHTTPRoute资源来配置网关路由,将集群内的服务暴露给外部用户访问。

背景信息

Gateway APIKubernetes社区推出的下一代服务网络模型API,旨在提供更加具有表达力、可扩展性和面向角色的路由与负载均衡接口。相较于传统的Ingress API,Gateway API提供了更清晰的资源模型分层(GatewayClass、Gateway、HTTPRoute等),支持更丰富的流量管理能力。

云原生API网关(APIG)支持Kubernetes Gateway API标准,您可以通过声明式的GatewayHTTPRoute资源来配置网关路由,将集群内的服务暴露给外部用户访问。

前提条件

  • 已创建云原生API网关实例,且引擎版本在2.1.16及以上。如果低于该版本,请先升级网关实例。

  • 已有Kubernetes集群(ACK集群),版本在1.24及以上,且已安装Gateway API CRD。当前网关兼容的最高版本为v1.3.0,建议安装v1.3.0版本。如果使用的是阿里云ACK,建议直接通过组件管理安装Gateway API CRD。

配置Gateway API监听

开启Gateway API监听

  1. 登录云原生API网关控制台,单击目标网关实例,进入API页面,单击创建API

  2. HTTP API下方,单击导入Gateway API

  3. 在创建面板中,填写API名称和描述,选择ACK来源集群。在自定义配置区域,根据需要配置监听的命名空间和GatewayClass,默认监听集群中所有命名空间的Gateway API资源。配置完成后,单击确定

  4. 单击进入新创建的API。如果ACK集群中已存在Gateway API资源且在配置的监听范围内,路由列表中将展示对应的路由条目,表示Gateway API监听已生效。

说明

出于对开源Higress的兼容性考虑,云原生API网关默认监听GatewayClasshigress的资源,不受上述过滤配置的影响。

修改Gateway API监听

  1. 登录云原生API网关控制台,单击目标网关实例,进入API页面,单击需要修改的Gateway API类型的API。

  2. 在右上角单击编辑,根据需要调整监听的GatewayClass和命名空间,单击确认后配置生效。如果对应配置项留空,则表示监听全部。

实操示例

在满足上述前提条件并完成Gateway API监听配置后,您可以参考以下示例,通过创建Gateway API资源实现服务的路由转发。

部署示例应用

  1. 创建httpbin.yaml文件,内容如下:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: httpbin
      namespace: default
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: httpbin
      template:
        metadata:
          labels:
            app: httpbin
            version: v1
        spec:
          containers:
            - image: registry.cn-hangzhou.aliyuncs.com/mse-ingress/go-httpbin
              args:
                - "--port=8080"
                - "--version=v1"
              imagePullPolicy: Always
              name: httpbin
              ports:
                - containerPort: 8080
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: httpbin
      namespace: default
    spec:
      type: ClusterIP
      ports:
        - port: 80
          targetPort: 8080
          protocol: TCP
      selector:
        app: httpbin
  2. 部署应用:

    kubectl apply -f httpbin.yaml
  3. 确认应用运行状态:

    kubectl get pods -l app=httpbin

    预期输出:

    NAME                       READY   STATUS    RESTARTS   AGE
    httpbin-5d4f8b6c7f-xxxxx   1/1     Running   0          30s

部署Gateway和路由

开启监听后,云原生API网关会默认创建一个名为higressGatewayClass。您也可以根据需要自行创建GatewayClass资源。

  1. (可选)如需自定义GatewayClass,创建gatewayclass.yaml文件:

    apiVersion: gateway.networking.k8s.io/v1
    kind: GatewayClass
    metadata:
      name: apig  # 可按需修改为自定义名称
    spec:
      controllerName: higress.io/gateway-controller
    kubectl apply -f gatewayclass.yaml
  2. 创建gateway.yaml文件,内容如下:

    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: apig-gateway
      namespace: default
    spec:
      gatewayClassName: apig
      listeners:
        - name: http
          protocol: HTTP
          port: 80
          hostname: "*.example.com"
          allowedRoutes:
            namespaces:
              from: Same
    ---
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: httpbin-route
      namespace: default
    spec:
      parentRefs:
        - name: apig-gateway
      hostnames:
        - "demo.example.com"
      rules:
        - matches:
            - path:
                type: PathPrefix
                value: /
          backendRefs:
            - kind: Service
              name: httpbin
              port: 80
  3. 部署Gateway和路由规则:

    kubectl apply -f gateway.yaml
  4. 确认Gateway创建状态。等待约2分钟,执行以下命令:

    kubectl get gateway apig-gateway

    预期输出:

    NAME           CLASS   ADDRESS       PROGRAMMED   AGE
    apig-gateway   apig    nlb-xxxx      True         2m

    PROGRAMMED状态为True表示Gateway已就绪。

  5. 确认路由已被正确监听:登录云原生API网关控制台,进入目标网关实例,单击API,找到Gateway API类型的API,点击进入后查看路由列表。如果列表中包含在容器侧创建的Gateway API路由,说明路由已被正确监听。

测试应用访问

  1. 获取网关接入点:登录云原生API网关控制台,进入目标网关实例,单击概览 > 基本信息,获取网关接入点地址。

  2. 访问应用(将<网关接入点>替换为上一步获取的地址):

    curl -H "Host: demo.example.com" http://<网关接入点>/version

    预期输出:

    version: v1
    hostname: httpbin-5d4f8b6c7f-xxxxx

场景演示

以下示例参考自Gateway API官方文档,更丰富的实践场景请参考Gateway API官方用户指南

场景一:使用filter修改请求头

通过HTTPRoute中的filter能力,可以在请求或响应阶段进行额外处理。以下演示如何使用filter为发送到后端的请求添加自定义请求头。

  1. 创建httproute-filter.yaml文件:

    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: demo-filter
    spec:
      parentRefs:
        - name: apig-gateway
      hostnames:
        - "filter.example.com"
      rules:
        - matches:
            - path:
                type: PathPrefix
                value: /
          filters:
            - type: RequestHeaderModifier
              requestHeaderModifier:
                add:
                  - name: my-header
                    value: foo
          backendRefs:
            - kind: Service
              name: httpbin
              port: 80

    该配置将创建一个名为demo-filterHTTPRoute资源。部署后,访问域名filter.example.com的请求将自动添加请求头my-header: foo

  2. 部署路由规则:

    kubectl apply -f httproute-filter.yaml
  3. 访问应用:

    curl -H "Host: filter.example.com" http://<网关接入点>/header

    预期输出中包含自动添加的请求头:

    headers: {
        ...
        "My-Header": [
          "foo"
        ],
        ...
    }

场景二:按照权重对流量进行切分

通过设置后端多个服务的权重,可以将请求按比例分发到不同的服务,常用于灰度发布场景。

  1. 创建nginx.yaml文件,部署两个nginx应用。其中old-nginx服务返回字符串oldnew-nginx服务返回字符串new

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: old-nginx
    spec:
      replicas: 1
      selector:
        matchLabels:
          run: old-nginx
      template:
        metadata:
          labels:
            run: old-nginx
        spec:
          containers:
          - image: registry.cn-hangzhou.aliyuncs.com/acs-sample/old-nginx
            imagePullPolicy: Always
            name: old-nginx
            ports:
            - containerPort: 80
              protocol: TCP
          restartPolicy: Always
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: old-nginx
    spec:
      type: ClusterIP
      ports:
      - port: 80
        protocol: TCP
        targetPort: 80
      selector:
        run: old-nginx
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: new-nginx
    spec:
      replicas: 1
      selector:
        matchLabels:
          run: new-nginx
      template:
        metadata:
          labels:
            run: new-nginx
        spec:
          containers:
          - image: registry.cn-hangzhou.aliyuncs.com/acs-sample/new-nginx
            imagePullPolicy: Always
            name: new-nginx
            ports:
            - containerPort: 80
              protocol: TCP
          restartPolicy: Always
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: new-nginx
    spec:
      type: ClusterIP
      ports:
      - port: 80
        protocol: TCP
        targetPort: 80
      selector:
        run: new-nginx
  2. 创建httproute-weight.yaml文件:

    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: demo-weight
    spec:
      parentRefs:
        - name: apig-gateway
      hostnames:
        - "weight.example.com"
      rules:
        - matches:
            - path:
                type: PathPrefix
                value: /
          backendRefs:
            - kind: Service
              name: old-nginx
              port: 80
              weight: 90
            - kind: Service
              name: new-nginx
              port: 80
              weight: 10
    说明

    weight字段为相对权重值,无需等于100。上述配置中90:10表示约90%的流量路由到old-nginx,约10%的流量路由到new-nginx

  3. 部署应用和路由规则:

    kubectl apply -f nginx.yaml
    kubectl apply -f httproute-weight.yaml
  4. 多次访问应用验证流量分配:

    for i in {1..10}; do curl -s -H "Host: weight.example.com" http://<网关接入点>/; echo; done

    预期输出中,大部分返回old,少量返回new,比例接近9:1。

场景三:基于Header的灰度路由

通过Header匹配规则,可以将携带特定请求头的流量路由到灰度服务,未携带则路由到稳定版本。该方式常用于在不影响线上用户的前提下,对灰度版本进行验证。

  1. 在上述实操示例的基础上,为httpbin部署灰度版本服务。创建httpbin-canary.yaml文件:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: httpbin-canary
      namespace: default
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: httpbin-canary
      template:
        metadata:
          labels:
            app: httpbin-canary
            version: v2
        spec:
          containers:
            - image: registry.cn-hangzhou.aliyuncs.com/mse-ingress/go-httpbin
              args:
                - "--port=8080"
                - "--version=v2"
              imagePullPolicy: Always
              name: httpbin
              ports:
                - containerPort: 8080
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: httpbin-canary
      namespace: default
    spec:
      type: ClusterIP
      ports:
        - port: 80
          targetPort: 8080
          protocol: TCP
      selector:
        app: httpbin-canary
    kubectl apply -f httpbin-canary.yaml
  2. 创建httproute-canary.yaml文件,配置基于Header的灰度路由规则:

    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: demo-canary
    spec:
      parentRefs:
        - name: apig-gateway
      hostnames:
        - "canary.example.com"
      rules:
        - matches:
            - headers:
                - type: Exact
                  name: env
                  value: canary
          backendRefs:
            - kind: Service
              name: httpbin-canary
              port: 80
        - backendRefs:
            - kind: Service
              name: httpbin
              port: 80

    该配置定义了两条路由规则:携带请求头env: canary的流量路由到灰度服务httpbin-canary,其余流量路由到稳定版本httpbin

  3. 部署路由规则:

    kubectl apply -f httproute-canary.yaml
  4. 验证灰度路由。不携带Header时访问稳定版本:

    curl -H "Host: canary.example.com" http://<网关接入点>/version

    预期输出:

    version: v1
    hostname: httpbin-f4c58d59c-xxxxx
  5. 携带env: canary请求头时访问灰度版本:

    curl -H "Host: canary.example.com" -H "env: canary" http://<网关接入点>/version

    预期输出:

    version: v2
    hostname: httpbin-canary-585859f9b6-xxxxx