Gateway API 与 HTTPRoute 公网入口

入站与出站先分清

本项目同时出现两个“Gateway”:

  • Shared Envoy Gateway:管理外部请求如何进入 CPA;
  • Shared Mihomo Gateway:管理 CPA 的请求如何通过代理出去。

名字相似,方向相反:

1
2
互联网用户 → Envoy Gateway → CPA
CPA → Mihomo Gateway → 互联网目标

本文只讲入站的 Gateway API。

为什么不为每个 Pool 创建 LoadBalancer

如果每个号池都创建一个 LoadBalancer Service,会带来:

  • 每个 Pool 一份云负载均衡成本;
  • 大量公网 IP;
  • DNS 和证书分散;
  • 暴露 cpa-manager 的误配置风险;
  • 入口策略难以统一审计。

项目选择共享入口:集群只维护 Shared Envoy Gateway,各 Pool 通过自己的 hostname 和 HTTPRoute 绑定到它。

1
2
3
demo-pool.example →┐
team-a.example    →┼→ Shared Gateway → 各自 CPA Service
team-b.example    →┘

这叫“共享基础设施、租户自带路由”。

Gateway API 的三个核心对象

GatewayClass:谁来实现 Gateway

项目声明:

1
2
3
4
5
6
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: subpool-envoy
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller

GatewayClass 是 cluster-scoped,类似 StorageClass:它说明哪种 controller 负责实现 Gateway。

仓库不会安装 Envoy Gateway controller。若集群中没有对应 controller,GatewayClass 和 Gateway 即使能创建,也不会真正产生可工作的入口。

Gateway:共享入口与 listener

项目在 subpool-gateway Namespace 创建 subpool-edge

1
2
3
4
5
6
spec:
  gatewayClassName: subpool-envoy
  listeners:
    - name: http
      protocol: HTTP
      port: 80

Gateway 描述一个基础设施入口,listener 描述它监听的协议、端口和允许哪些 Route 绑定。

当前仓库 listener 是 HTTP 80。README 提到生产需要通配 DNS/TLS,但当前 manifest 没有直接配置 HTTPS listener 和证书,因此生产 TLS 仍属于必须补齐并验证的环境能力,不能因为有 Gateway 对象就假定 HTTPS 已完成。

HTTPRoute:某个 hostname 去哪个后端

当 SubPool 使用 Gateway 模式时,Operator 在 Pool Namespace 创建名为 cpa 的 HTTPRoute:

1
2
3
hostname: <pool-id>.<baseDomain>
parent: subpool-gateway/subpool-edge listener http
backend: 同 Namespace 的 Service cpa:8317

HTTPRoute 属于租户 Namespace,Gateway 属于共享基础设施 Namespace。二者通过 parent reference 建立关系。

跨 Namespace Route 如何获得准入

Gateway listener 配置:

1
2
3
4
5
6
allowedRoutes:
  namespaces:
    from: Selector
    selector:
      matchLabels:
        subrelay.example.com/managed: "true"

只有带 managed label 的 Namespace,其 HTTPRoute 才能绑定这个 listener。

这些 label 由 Operator 在创建 SubPool Namespace 时添加。因此任意普通 Namespace 不能随便提交一个 Route 劫持共享入口域名。

这体现了 Gateway API 的角色分工:

  • 基础设施管理员管理 Gateway 和 listener 策略;
  • 应用或 Operator 管理自己 Namespace 中的 HTTPRoute;
  • Gateway status 明确告诉双方 Route 是否被接受。

完整数据路径

Gateway 模式下,外部请求大致经过:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
客户端请求 demo-pool.subrelay.example.com
  ├─ DNS 解析到共享入口地址
Envoy Gateway listener
  │ 根据 hostname 匹配 HTTPRoute
subpool-demo-pool/cpa HTTPRoute
  │ backendRef
subpool-demo-pool/cpa Service:8317
  │ selector + EndpointSlice
runtime-0 Pod 的 CPA 容器

任何一段缺失,外部访问都可能失败。Gateway API 不负责自动购买域名,也不保证云安全组、LoadBalancer、DNS 和证书已经正确配置。

Operator 如何判断 ExposureReady

Gateway 模式下,Operator 分两步检查。

先检查 HTTPRoute status 中目标 parent:

  • parent name 必须是配置指定的 Gateway;
  • parent Namespace 必须匹配;
  • sectionName 必须匹配 http listener;
  • Accepted=True
  • ResolvedRefs=True

再检查 Shared Gateway:

  • Programmed=True

只有这些条件同时成立,ExposureReady=True

Accepted 表示 Gateway controller 接受该 Route;ResolvedRefs 表示后端等引用能成功解析;Programmed 表示 Gateway 配置已经下发到数据面。它们比“对象存在”提供了更强证据。

四个常见 reason

Reason含义优先检查
RouteMissingOperator 尚未看到 HTTPRoutereconcile、权限、受管资源冲突
RouteNotAcceptedRoute 未被目标 listener 接受allowedRoutes、parentRef、backendRef status
GatewayMissing配置指定的共享 Gateway 不存在deploy/gateway 是否安装、名称与 Namespace
GatewayNotProgrammedGateway controller 尚未准备好数据面Envoy Gateway controller、Gateway status 和 Event

排查命令:

1
2
3
kubectl -n subpool-demo-pool get httproute cpa -o yaml
kubectl -n subpool-gateway get gateway subpool-edge -o yaml
kubectl get gatewayclass subpool-envoy -o yaml

重点看 status.parents[].conditionsstatus.conditions,而不只是 spec。

Internal 切换到 Gateway 会发生什么

修改 spec.exposure.modeGateway 后,Operator 会创建 HTTPRoute,并在 status 中写入派生 hostname。

从 Gateway 切回 Internal,Operator 会删除 HTTPRoute,但保留 CPA ClusterIP Service 和运行数据。

暂停 Pool 时也会删除 HTTPRoute并把 StatefulSet 缩容为 0,避免一个没有后端的公共路由继续挂在共享入口上。

cpa-manager 为什么没有 HTTPRoute

项目明确把 CPA 定义为 Pool Endpoint,cpa-manager 只是管理员看板。Manager 始终保持 ClusterIP,只通过受控 port-forward 使用。

这是一条安全边界:

1
2
外部用户流量 → CPA
管理员临时访问 → cpa-manager

不能因为 Gateway 模式已经存在,就顺手给 Manager 再加一条 Route。那会扩大攻击面,并绕过项目既定的管理员访问控制。

DNS、TLS 与 Gateway 对象不是一回事

Gateway status 正常并不能自动证明:

  • <pool-id>.<baseDomain> 已解析到入口 IP;
  • 通配符 DNS 已配置;
  • HTTPS listener 已存在;
  • TLS Secret 或证书 controller 正常;
  • 云 LoadBalancer 可从互联网访问;
  • 防火墙或安全组已开放。

这些属于 Cluster Bootstrap 和环境运维。Operator 只根据已有共享 Gateway 和 Route status 判断 Kubernetes 内部的入口前提。

Gateway API 相比旧式 Ingress 的思维变化

Ingress 常把基础设施与应用路由挤在同一种对象里,并大量依赖 controller-specific annotations。Gateway API 把职责拆成 GatewayClass、Gateway 和 Route:

1
2
3
实现选择 → GatewayClass
共享入口 → Gateway
应用路由 → HTTPRoute

这种分层非常适合本项目:平台团队控制 Shared Envoy,SubPool Operator 只创建每 Pool 的 HTTPRoute。

安全的排障原则

如果 ExposureReady=False,应修复 Shared Gateway、listener、allowedRoutes、DNS 或 TLS,而不是:

  • 把 CPA Service 改成 LoadBalancer;
  • 给每个 Pool 新建公网 IP;
  • 删除 managed Namespace selector;
  • 直接暴露 cpa-manager;
  • 放开 CPA 出站绕过 Mihomo。

绕过架构可能让一次临时排障变成长期安全债务。按照 DNS → Gateway → Route → Service → Endpoint → Pod 的顺序逐段验证,才能修复真正的断点。

使用 Hugo 构建
主题 StackJimmy 设计