Envoy Gateway 任务

概述

完成主要配置流程(operator、网关、路由和策略)后,可执行以下 Envoy Gateway 高级任务。

在 Gateway API 中应用配置更改时,主要有三种可用方式:

  1. 修改标准 Gateway API 资源:直接编辑 Gateway、HTTPRoute、TCPRoute、UDPRoute 和其他核心资源。
  2. 通过 PolicyAttachment 附加策略:使用 SecurityPolicy、ClientTrafficPolicy、BackendTrafficPolicy 和其他策略资源扩展网关和路由行为。
  3. 配置全局设置:修改 EnvoyGatewayCtl,以更改 envoy-gateway 实例行为或其他影响所有网关的全局设置。

高级任务和特殊场景包括跨命名空间路由、可观测性配置、部署自定义和故障排除。

前提条件

  1. 配置 EnvoyGatewayCtl
  2. 配置网关
  3. 配置路由
  4. 配置 GatewayAPI 策略

高级任务

OpenTelemetry (OTel)

请按照 OpenTelemetry 集成 中的说明操作,但使用 EnvoyGatewayCtl 修改 envoy-gateway-config。

如何附加到在其他命名空间中创建的 Listener

在 Gateway 的 Listener 配置中,需要指定允许哪些命名空间将 Route 附加到该 Listener。

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
spec:
  listeners:
    - name: http-80
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: All # without limit
    - name: http-81
      protocol: HTTP
      port: 81
      allowedRoutes:
        namespaces:
          from: Same # only allow routes in the same namespace
    - name: http-82
      protocol: HTTP
      port: 82
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              team: frontend # only allow routes in the namespace with label team=frontend

有关详情,请参阅跨命名空间路由。

如何使用在另一个命名空间中创建的证书

若要使用在另一个命名空间中创建的证书,请在存储证书的命名空间中创建 ReferenceGrant。请按照跨命名空间证书引用和 referencegrant 中的说明操作。

NOTE

不能指定单个 secret 资源;必须允许整个命名空间

如何使用 SSL Passthrough

请按照以下说明操作:

如何更改最低 TLS 版本

请按照自定义网关 TLS 参数中的说明操作。

cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
  name: enforce-tls-13
  namespace: default
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: eg
  tls:
    minVersion: "1.3"
EOF

ClientTrafficPolicy 中的 .spec.tls 字段对应 clienttlssettings。 如果除了最低 TLS 版本之外还需要自定义密码套件,请参阅同一上游任务和 API 参考。

使用 NodePort Service 时如何指定 NodePort

使用 NodePort Service 时,Kubernetes 会为每个 Service 端口分配一个 NodePort 值。通过节点 IP 访问 Service 时,请使用分配的 NodePort,而不是 Service 端口。

有两种方式:

按照从 svc 端口获取 nodeport中的说明手动获取 NodePort 分配值。

在 EnvoyProxy 配置中手动指定 NodePort,而不是让 Kubernetes 自动分配。

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: demo
spec:
  ipFamily: DualStack
  provider:
    kubernetes:
      envoyDeployment:
        container:
          imageRepository: registry.alauda.cn:60080/acp/envoyproxy/envoy
      envoyService:
        patch:
          type: StrategicMerge
          value:
            spec:
              ports:
                - nodeport: 31888
                  port: 80
        type: NodePort
    type: Kubernetes
  1. 使用 patch 字段修改生成的 Service 资源,以指定 NodePort
NOTE

NodePort 只能位于特定范围内,通常为 30000-32767。如果希望 Gateway Listener 端口与 NodePort 保持一致,Listener 端口也必须位于 NodePort 范围内。

使用 MetalLB 时如何指定 VIP

使用 MetalLB 作为 LoadBalancer 提供程序时,可以通过 Service 注解为 Gateway Service 指定静态 VIP。

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: demo
  namespace: demo
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyService:
        type: LoadBalancer
        annotations:
          metallb.universe.tf/address-pool: production
          metallb.universe.tf/loadBalancerIPs: VIP_IP
  1. 在 envoyService.annotations 字段中添加 MetalLB 注解
  2. 指定用于分配 IP 的地址池名称
  3. 或者指定一个特定 IP 地址(必须位于地址池范围内)

可用注解:

注解说明
metallb.universe.tf/address-pool选择用于分配 IP 的地址池
metallb.universe.tf/loadBalancerIPs指定一个特定 IP 地址(支持多个 IP,以逗号分隔)
NOTE
  • 指定的 IP 必须位于已配置的 MetalLB 地址池中
  • 指定 VIP 之前,请确保已正确安装和配置 MetalLB
  • 有关 MetalLB 配置,请参阅配置 MetalLB

如何在 Envoy Gateway 中添加 Pod 注解

在 EnvoyProxy 的 .spec.provider.kubernetes.envoyDeployment.patch.value.spec.template.metadata.annotations 中设置 annotations。

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: demo
spec:
  infrastructure:
    parametersRef:
      group: gateway.envoyproxy.io
      kind: EnvoyProxy
      name: demo
  gatewayClassName: envoy-gateway-operator-cpaas-default
  listeners:
    - name: http
      port: 80
      protocol: HTTP
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: demo
spec:
  provider:
    kubernetes:
      envoyDeployment:
        replicas: 1
        patch:
          type: StrategicMerge
          value:
            spec:
              template:
                metadata:
                  annotations:
                    your-annotation-key: 'your-annotation-value'
        container:
          imageRepository: registry.alauda.cn:60080/acp/envoyproxy/envoy
    type: Kubernetes

如何为 envoy-gateway-operator 设置 NodeSelector 和 Tolerations

更新 Subscription 资源。

# example of nodeSelector and tolerations
kubectl patch subscription envoy-gateway-operator  -n envoy-gateway-operator   --type='merge' -p '
{
  "spec": {
    "config": {
      "nodeSelector": {
        "node-role.kubernetes.io/infra": ""
      },
      "tolerations": [
        {
          "effect": "NoSchedule",
          "key": "node-role.kubernetes.io/infra",
          "operator": "Equal",
          "value": "reserved"
        }
      ]
    }
  }
}'

如何为 envoy-gateway 设置 NodeSelector 和 Tolerations

更新 EnvoyGatewayCtl 资源。

# in default $NAME=cpaas-default and $NS=envoy-gateway-operator
kubectl patch envoygatewayctl $NAME -n $NS --type='merge' -p '
{
  "spec": {
    "deployment": {
      "pod": {
        "nodeSelector": {
          "node-role.kubernetes.io/infra": ""
        },
        "tolerations": [
          {
            "effect": "NoSchedule",
            "key": "node-role.kubernetes.io/infra",
            "operator": "Equal",
            "value": "reserved"
          }
        ]
      }
    }
  }
}'

如何为 envoy-proxy 设置 NodeSelector 和 Tolerations

更新 EnvoyProxy 资源。

kubectl patch envoyproxy $NAME -n $NS --type='merge' -p '
{
  "spec": {
    "provider": {
      "kubernetes": {
        "envoyDeployment": {
          "pod": {
            "nodeSelector": {
              "node-role.kubernetes.io/infra": ""
            },
            "tolerations": [
              {
                "effect": "NoSchedule",
                "key": "node-role.kubernetes.io/infra",
                "operator": "Equal",
                "value": "reserved"
              }
            ]
          }
        }
      }
    }
  }
}'

如何在 envoy-proxy 中使用 hostNetwork

使用 hostNetwork: true 可让 Envoy 代理 Pod 直接使用主机网络命名空间。这在以下场景中很有用:

  • 提升网络性能
  • 通过节点 IP 直接访问网关

注意事项:

  • 使用 hostNetwork 的 Pod 会直接绑定到主机的网络接口
  • 如果多个 Pod 尝试在同一节点上使用相同端口,可能会发生端口冲突
  • 由于 Pod 共享主机的网络命名空间,安全隔离性会降低
  • 应使用 nodeSelector 或 affinity 规则控制 Pod 的放置位置,避免端口冲突

根据是否要直接使用特权端口(< 1024),配置 hostNetwork 有两种方式:

方法 1:使用端口偏移(默认,推荐)

这是默认且推荐的方式。Envoy Gateway 会自动为特权端口增加 10000 的偏移量,从而无需特殊权限。

优点:

  • 无需特殊权限或功能
  • 以非 root 用户身份运行且无需额外权限,因此更安全
  • 配置更简单
  • 开箱即用

缺点:

  • 客户端必须使用偏移后的端口(10080、10443)

配置:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: demo
  namespace: demo
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        patch:
          type: StrategicMerge
          value:
            spec:
              template:
                spec:
                  hostNetwork: true                    # Enable host network mode
                  dnsPolicy: ClusterFirstWithHostNet   # Required for proper DNS resolution
        pod:
          nodeSelector:                                # Recommended: control pod placement to avoid conflict
            kubernetes.io/hostname: "demo"

访问:

  • 端口 80 → 通过 http://<node-ip>:10080 访问
  • 端口 443 → 通过 https://<node-ip>:10443 访问

方法 2:通过 useListenerPortAsContainerPort 直接使用特权端口

此方法允许 Envoy 直接绑定到特权端口(< 1024),例如端口 80 和 443。

优点:

  • 可直接使用标准端口(80 和 443)
  • 与预期使用标准端口的客户端兼容性更好

缺点:

  • 需要 NET_BIND_SERVICE 功能
  • 与端口偏移方式相比,安全性略低
  • 配置更复杂

配置:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: demo
  namespace: demo
spec:
  provider:
    type: Kubernetes
    kubernetes:
      useListenerPortAsContainerPort: true  # Disable port offset
      envoyDeployment:
        patch:
          type: StrategicMerge
          value:
            spec:
              template:
                spec:
                  hostNetwork: true
                  dnsPolicy: ClusterFirstWithHostNet
                  containers:
                  - name: envoy
                    command:
                      - /usr/local/bin/envoy-with-cap  # use envoy with filecap
                    securityContext:
                      capabilities:
                        add:
                        - NET_BIND_SERVICE  # Required for binding to privileged ports
        pod:
          nodeSelector:                                # Recommended: control pod placement to avoid conflict
            kubernetes.io/hostname: "demo"

访问:

  • 端口 80 → 通过 http://<node-ip>:80 访问
  • 端口 443 → 通过 https://<node-ip>:443 访问

如何从集群内部访问 LoadBalancer VIP

默认情况下,Envoy Gateway 创建的 LoadBalancer Service 使用 externalTrafficPolicy: Local。此策略会保留客户端源 IP 地址,但有一项重要限制:来自没有 Envoy Gateway Pod 的集群节点的请求会失败,因为流量不会转发到其他节点。

解决方案 1:使用 Service ClusterIP(建议用于集群内访问)

对于在集群内运行的应用,请使用 Service ClusterIP,而不是 LoadBalancer VIP。这样可以完全避免路由限制。

解决方案 2:更改为集群流量策略

如果需要从任意集群节点访问 LoadBalancer VIP,请将 externalTrafficPolicy 更改为 Cluster:

kubectl patch envoyproxy $GATEWAY_NAME -n $GATEWAY_NS --type='json' -p='[
  {"op": "replace", "path": "/spec/provider/kubernetes/envoyService/externalTrafficPolicy", "value": "Cluster"}
]'

如何配置 HSTS

HTTP Strict Transport Security (HSTS) 会告知浏览器仅通过 HTTPS 与某个主机名交互。ACP 使用 ClientTrafficPolicy .spec.headers.lateResponseHeaders 在 Gateway 上配置 HSTS。Gateway Listener 会在完成其他所有标头修改后应用延迟响应标头,因此每个 Gateway 响应都会包含 Strict-Transport-Security 标头,包括 Envoy 自身生成的响应(例如请求不匹配任何路由时返回的 404)。路由级别的 ResponseHeaderModifier 过滤器无法覆盖此类响应。

此功能适用于 ACP 4.5 及更高版本。

cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
  name: hsts-policy
  namespace: default
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: Gateway
    name: eg
  headers:
    lateResponseHeaders:
      set:
      - name: Strict-Transport-Security
        value: "max-age=31536000; includeSubDomains"
EOF

验证普通响应以及主机不匹配时 Envoy 生成的 404 响应中的标头:

curl -sk -v --resolve www.example.com:443:<gateway-ip> https://www.example.com/ 2>&1 | grep -i strict-transport
curl -sk -v https://<gateway-ip>/ 2>&1 | grep -E "^< HTTP|strict-transport"

注意:

  • 使用 set 而不是 add,以免后端降低标头值的安全级别。addIfAbsent 和 remove 也可在 lateResponseHeaders 中使用。
  • HSTS 不会将 HTTP 流量重定向到 HTTPS;它只会告知已通过 HTTPS 收到此标头的浏览器继续使用 HTTPS。如有需要,也请在路由上配置 HTTP 到 HTTPS 的重定向。
  • 浏览器会将此决定缓存 max-age 秒,在此期间无法从服务器撤销。正式部署一年期限前,请先使用较短的 max-age(例如 300)进行验证。
  • 有关完整 API,请参阅官方 Envoy Gateway HTTP 响应标头文档。

如何配置自定义错误页面

默认情况下,Gateway 会提供 Envoy 生成的纯文本错误响应正文(例如 503 时的 no healthy upstream),并原样传递后端错误响应。可通过两种机制返回自定义错误页面;两者均适用于 ACP 4.5 及更高版本:

  • BackendTrafficPolicy .spec.responseOverride[] 会重写与状态码匹配的响应。
  • HTTPRouteFilter .spec.directResponse 会为路由规则返回固定响应,而不联系任何后端。

responseOverride 可按状态码自定义错误页面。正文可以是内联字符串,也可以是 ConfigMap 的 response.body 键:

cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: error-page-policy
  namespace: default
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: backend
  responseOverride:
  - match:
      statusCodes:
      - type: Value
        value: 404
    response:
      contentType: text/html
      body:
        type: Inline
        inline: "<html><body><h1>Page not found</h1></body></html>"
  - match:
      statusCodes:
      - type: Value
        value: 500
    response:
      contentType: application/json
      body:
        type: ValueRef
        valueRef:
          group: ""
          kind: ConfigMap
          name: error-page-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: error-page-config
  namespace: default
data:
  response.body: '{"error": "Internal Server Error"}'
EOF

每条规则的可选字段 source 用于控制重写哪些响应:All(默认值)会重写后端和 Envoy 生成的响应;Backend 仅重写来自后端的响应;Local 仅重写 Envoy 自身生成的响应,例如路由匹配成功但后端没有就绪端点时产生的 503。使用具有相同状态码的 Local 和 Backend 规则,为网关错误和后端错误提供不同页面。规则还可以重写状态码(response.statusCode),或从另一个 URL 获取响应(redirect)。

directResponse 可以在不联系任何后端的情况下响应路径。通过 ExtensionRef 将此过滤器应用于 HTTPRoute 规则;HTTPRouteFilter 必须与 HTTPRoute 位于同一命名空间,且正文大小不能超过 4096 字节:

cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: backend
  namespace: default
spec:
  parentRefs:
  - name: eg
  hostnames:
  - www.example.com
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /maintenance
    filters:
    - type: ExtensionRef
      extensionRef:
        group: gateway.envoyproxy.io
        kind: HTTPRouteFilter
        name: maintenance-page
  - backendRefs:
    - name: backend
      port: 3000
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: HTTPRouteFilter
metadata:
  name: maintenance-page
  namespace: default
spec:
  directResponse:
    contentType: text/html
    statusCode: 503
    body:
      type: Inline
      inline: "<html><body><h1>Under maintenance</h1></body></html>"
EOF

验证重写后的页面:

curl -s -w '\n%{http_code}\n' --resolve www.example.com:80:<gateway-ip> http://www.example.com/missing-page

注意:

  • responseOverride 仅适用于经过目标 HTTPRoute 的响应。完全没有匹配到路由的请求会收到默认的 Envoy 404,且不会应用任何规则。
  • 直接响应由 Envoy 自身生成,因此同一 HTTPRoute 上具有匹配状态码(例如 503,且使用 source: Local 或默认的 source: All)的 responseOverride 规则会重写该直接响应。如果同时使用这两种机制,请将直接响应配置在单独的 HTTPRoute 上。
  • 有关完整 API,请参阅 Envoy Gateway 官方文档中的响应覆盖和直接响应。

相关文档

更多配置

请参阅 EnvoyGateway 任务

商标声明

Envoy® 是 The Linux Foundation 的注册商标。

MetalLB™ 是 The Linux Foundation 的商标。

Alauda 是一家独立供应商。本产品与 The Linux Foundation 无关,也未获得其认可或赞助。所有商标均为其各自所有者的财产,在此仅用于标识目的。