使用Gateway with Inference Extension访问服务

更新时间:
复制 MD 格式

Gateway API 是 Kubernetes 官方推出的下一代路由和负载均衡 API。本文介绍如何在 ACK Edge 集群中通过 Gateway with Inference Extension 组件配置 HTTP 路由、请求 Header 修改和按比例分发请求。

工作原理

Gateway with Inference Extension组件基于 Envoy Gateway项目构建,支持完整的Gateway API基础能力及Envoy Gateway扩展资源。

Envoy Gateway 架构包括:

  • 控制面:由 Envoy Gateway 组件构成,负责监听集群中的流量规则,动态创建和管理 Envoy 代理实例,并实时更新转发规则。控制面不直接参与业务流量转发。

  • 数据面:由 Envoy Proxy 实例组成,负责处理和转发业务流量。

ACK Edge集群中,Envoy Gateway的部署方式与标准ACK集群有所不同:

差异项

标准 ACK

ACK Edge

服务暴露方式

LoadBalancer

NodePort

控制面部署

集群级别

每个节点池各部署一套

数据面部署

集群级别

按节点池维度部署

多节点池

共享一个 Gateway

每个节点池需单独创建 Gateway

服务拓扑

无需配置

需配置 openyurt.io/topologyKeys 注解

当前部署配置了节点池打散策略,可通过组件管理调节副本数,确保每个节点池至少运行一个控制面副本。

ACK Edge 中 Envoy Gateway 部署架构

适用范围

准备工作

步骤一:创建测试应用

将下方示例保存到backend.yaml,执行 kubectl apply -f backend.yaml,创建测试应用backendbackend-2。

ACK Edge场景需要配置服务拓扑。在Service中需添加 openyurt.io/topologyKeys: openyurt.io/nodepool 注解,确保流量只会转发到同一节点池内的Pod。详情参见节点池服务拓扑管理
apiVersion: v1
kind: ServiceAccount
metadata:
  name: backend
---
apiVersion: v1
kind: Service
metadata:
  name: backend
  annotations:
    openyurt.io/topologyKeys: openyurt.io/nodepool # 配置节点池拓扑
  labels:
    app: backend
    service: backend
spec:
  ports:
    - name: http
      port: 3000
      targetPort: 3000
  selector:
    app: backend
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
      version: v1
  template:
    metadata:
      labels:
        app: backend
        version: v1
    spec:
      serviceAccountName: backend
      containers:
        - image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/envoygateway-echo-basic:v20231214-v1.0.0-140-gf544a46e
          imagePullPolicy: IfNotPresent
          name: backend
          ports:
            - containerPort: 3000
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: backend-2
---
apiVersion: v1
kind: Service
metadata:
  name: backend-2
  annotations:
    openyurt.io/topologyKeys: openyurt.io/nodepool # 配置节点池拓扑
  labels:
    app: backend-2
    service: backend-2
spec:
  ports:
    - name: http
      port: 3000
      targetPort: 3000
  selector:
    app: backend-2
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend-2
      version: v1
  template:
    metadata:
      labels:
        app: backend-2
        version: v1
    spec:
      serviceAccountName: backend-2
      containers:
        - image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/envoygateway-echo-basic:v20231214-v1.0.0-140-gf544a46e
          imagePullPolicy: IfNotPresent
          name: backend-2
          ports:
            - containerPort: 3000
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace

步骤二:确认GatewayClass

安装Gateway with Inference Extension组件后会默认创建GatewayClass。执行以下命令确认:

kubectl get gatewayclass

预期输出:

NAME          CONTROLLER                                      ACCEPTED   AGE
ack-gateway   gateway.envoyproxy.io/gatewayclass-controller   True       2m31s

如果未发现 GatewayClass资源,需手动创建。将下方示例保存到gatewayclass.yaml,执行 kubectl apply -f gatewayclass.yaml创建。

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: ack-gateway
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller

步骤三:创建自定义EnvoyProxy配置

ACK Edge集群中,需要为每个节点池创建自定义 EnvoyProxy 配置,指定数据面 Pod 的调度节点池和服务暴露方式。

将下方示例中的 NPXXX 替换为实际的节点池ID,然后保存到gateway-config.yaml,执行 kubectl apply -f gateway-config.yaml

可在ACK控制台的节点管理 > 节点池页面查看节点池 ID。
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: custom-proxy-config
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        pod:
          nodeSelector:
            alibabacloud.com/nodepool-id: NPXXX  # 替换为实际的节点池 ID
      envoyService:
        annotations:
          openyurt.io/topologyKeys: openyurt.io/nodepool  # 配置服务拓扑
        type: NodePort  # ACK Edge 使用 NodePort 暴露服务

该配置包含三个ACK Edge场景的关键设置:

配置项

说明

nodeSelector

将 Gateway 数据面 Pod 调度到指定节点池

openyurt.io/topologyKeys 注解

配置服务拓扑,确保流量在节点池内闭环

type: NodePort

使用 NodePort 暴露服务(边缘场景不使用 LoadBalancer)

步骤四:创建Gateway资源

将下方示例保存到gateway.yaml,执行 kubectl apply -f gateway.yaml

组件控制面会根据Gateway资源自动创建EnvoyProxy DeploymentNodePort类型的Service,并在节点上指定端口开启监听。
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: ack-gateway
spec:
  gatewayClassName: ack-gateway
  listeners:
    - name: http
      protocol: HTTP
      port: 80
  infrastructure:
    parametersRef:
      group: gateway.envoyproxy.io
      kind: EnvoyProxy
      name: custom-proxy-config  # 关联步骤三创建的自定义配置

步骤五:获取网关地址

网关服务采用NodePort方式暴露,对应的服务地址为节点池内任一节点IP和服务的端口。

  1. 查询Service端口。

    kubectl get service -n kube-system -l gateway.envoyproxy.io/owning-gateway-name=ack-gateway

    预期输出:

    NAME                                 TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
    envoy-default-ack-gateway-7452df7d   NodePort   192.168.86.174   <none>        80:30364/TCP   19m
  2. 设置网关地址环境变量。将NODEIP替换为节点池内任一节点的IP地址,NODEPORT替换为上一步查询到的端口号。

    export GATEWAY_HOST=NODEIP:NODEPORT

基于路径前缀匹配的HTTP路由

以下示例配置HTTPRoute匹配/get前缀,并进行测试验证。

  1. 创建HTTPRoute资源。将以下YAML内容保存为httproute.yaml,然后执行kubectl apply -f httproute.yaml命令。

    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: backend
    spec:
      parentRefs:
        - name: ack-gateway
      hostnames:
        - "www.example.com"
      rules:
        - backendRefs:
            - group: ""
              kind: Service
              name: backend
              port: 3000
              weight: 1
          matches:
            - path:
                type: PathPrefix
                value: /get
  2. 测试访问。

    curl -H "Host: www.example.com" http://$GATEWAY_HOST/get

    预期输出:

    {
     "path": "/get",
     "host": "www.example.com",
     "method": "GET",
     "proto": "HTTP/1.1",
     "headers": {
      "Accept": [
       "*/*"
      ],
      "User-Agent": [
       "curl/8.9.1"
      ],
      "X-Envoy-External-Address": [
       "115.XX.XXX.55"
      ],
      "X-Forwarded-For": [
       "115.XX.XXX.55"
      ],
      "X-Forwarded-Proto": [
       "http"
      ],
      "X-Request-Id": [
       "953b2f8f-26d3-4ba9-93ba-a482b197b1ff"
      ]
     },
     "namespace": "default",
     "ingress": "",
     "service": "",
     "pod": "backend-5bff7XXXXX-XXXXX"
     }

添加请求Header

更新HTTPRoute配置,为路由的请求添加Header。

  1. 更新HTTPRoute资源。将以下YAML内容保存为httproute.yaml,然后执行kubectl apply -f httproute.yaml命令。

    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: backend
    spec:
      parentRefs:
        - name: ack-gateway
      hostnames:
        - "www.example.com"
      rules:
      - matches:
        - path:
            type: PathPrefix
            value: /get
        backendRefs:
        - group: ""
          kind: Service
          name: backend
          port: 3000
          weight: 1
        filters:
        - type: RequestHeaderModifier
          requestHeaderModifier:
            add:
            - name: "added-header"
              value: "foo"
  2. 测试访问。

    curl -H "Host: www.example.com" http://$GATEWAY_HOST/get

    预期输出:由于示例应用会将请求信息作为响应返回,因此可以在响应中看到已添加的added-header,表明操作已成功。

    {
     "path": "/get",
     "host": "www.example.com",
     "method": "GET",
     "proto": "HTTP/1.1",
     "headers": {
      "Accept": [
       "*/*"
      ],
      "Added-Header": [
       "foo"
      ],
      "User-Agent": [
       "curl/8.9.1"
      ],
      "X-Envoy-External-Address": [
       "115.XX.XXX.55"
      ],
      "X-Forwarded-For": [
       "115.XX.XXX.55"
      ],
      "X-Forwarded-Proto": [
       "http"
      ],
      "X-Request-Id": [
       "d37f19e5-25c1-45cf-90e5-51453e7ae3ed"
      ]
     },
     "namespace": "default",
     "ingress": "",
     "service": "",
     "pod": "backend-5bff7XXXXX-XXXXX"
    }  

按比例分发请求

以下将再次更新HTTPRoute配置,增加backend-2的路由规则,并为backendbackend-2服务配置权重。

说明

Gateway API并不要求所有backendRef的权重之和为100。单个服务比例计算规则为:。所有请求的流量将按照此规则的比例进行分配。

  1. 更新HTTPRoute资源。将以下YAML内容保存为httproute.yaml,然后执行kubectl apply -f httproute.yaml命令。

    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: backend
    spec:
      parentRefs:
        - name: ack-gateway
      hostnames:
        - "www.example.com"
      rules:
      - matches:
        - path:
            type: PathPrefix
            value: /get
        backendRefs:
        - group: ""
          kind: Service
          name: backend
          port: 3000
          weight: 8
        - group: ""
          kind: Service
          name: backend-2
          port: 3000
          weight: 2
  2. 测试连续访问20次,查看两个服务的比例。

    以下命令进行了额外处理,使输出仅显示backendbackend-2
    for i in $(seq 1 20); do curl -sS -H "Host: www.example.com" http://$GATEWAY_HOST/get |grep backend; done | \
        sed -E 's/".*"(backend(-2)?)-[0-9a-zA-Z]*-.*/\1/'

    预期输出: 两个服务接收到的流量比例大致为80%和20%。

     backend-2
     backend
     backend
     backend
     backend
     backend
     backend
     backend
     backend
     backend
     backend
     backend-2
     backend-2
     backend
     backend
     backend-2
     backend
     backend
     backend
     backend

相关文档