AI时代的Kubernetes教程36招-第13招-ClusterIP、NodePort、LoadBalancer、Ingress和Gateway API怎么选

从ClusterIP到Gateway API:数字人平台流量入口的取舍与演进

四层能通,不代表七层能管;七层能管,不代表能分权。

[读者] 负责集群入口的平台工程师、SRE 与应用架构师
[痛点] Service 与 Ingress 混用,入口成本高、职责糊、UDP 无处安放
[现在读] 正准备把多团队共用的入口从 Ingress 迁向 Gateway API
[读完] 能分清五类入口的边界,并落地一次最小迁移与一次排障
[旧方案] NodePort 加自建 Nginx,后演变为每个服务一个 LoadBalancer
    |
    v
[新需求] 多业务线自服务路由,HTTP 与 UDP 混合入口,平台与应用分权
    |
    v
[冲突] LoadBalancer 是四层且不可共享,Ingress 是七层但没有所有权模型
    |
    v
[后果] 端口与证书压在平台团队,新业务上线排队,实时音视频长期裸奔

我是老李,在一个数字人产品团队做平台架构,日常工作是给业务团队提供“能用、能管、能追责”的运行时入口。我们线上跑着三块东西:数字人 Web 控制台和开放 API 走 HTTP/HTTPS;实时音视频走 WebRTC,媒体面以 UDP 为主、TCP 兜底;内部还有一批 gRPC 微服务做东西向调用。

这三块对“入口”的诉求根本不是一回事。Web/API 关心域名、路径、证书和灰度;音视频关心端口、协议、源 IP 和时延;内部微服务只关心在集群内能不能解析到一个稳定的名字。把它们塞进同一个抽象,早年没出问题,只是因为量小。

我们的入口方案也是这么长出来的:最早三个 NodePort 加一台自建 Nginx,后来上云改成“一个后端服务一个 LoadBalancer”,再后来为了域名和路径,又叠了一层 Ingress。三套东西同时在线,谁也没能替代谁。

那么,ClusterIP、NodePort、LoadBalancer、Ingress 和 Gateway API,到底是五个可以互相替代的选项,还是五层完全不同的职责?

当我们说“用 Gateway API 替换 Ingress”,被替换的究竟是数据面、控制面,还是资源的所有权关系?

如果一开始就把实时音视频也塞进七层入口,后面要还的债长什么样?

01、故事

事情是从一张工单开始的。业务要上一条新域名 avatar.example.com,路径 /live 指到新上线的直播编排服务,并且要能按 10% 灰度切量。

我接到的任务是:在不新增公网 IP、不新增证书的前提下,把这个新路由接进现有入口,并且让业务团队自己就能改灰度比例。

这个任务在当时的架构下几乎无解,因为我们的入口是三套东西叠出来的。

第一套是 NodePort。集群里一段高位端口,谁先占谁得。上线新服务时,运维在自建 Nginx 上写一条 upstream,指到三个节点的固定端口。它工作得很好,直到节点扩缩容,upstream 里的 IP 就成了历史遗迹。

第二套是 LoadBalancer。上云之后,为了方便,我们把“每个对外服务一个 LoadBalancer”当成了默认动作。好处是改一行 type: LoadBalancer 就有公网 IP,坏处是它只认端口,不认域名和路径。灰度?那得在服务自己的代码里做。

第三套是 Ingress。我们确实用它做过域名和路径,但整个集群只有一个 IngressClass,所有团队的规则挤在同一个控制器里。谁改了一条 rewrite 注解,可能把隔壁域名的静态资源全打歪。

于是任务变成了三方协调:业务改灰度要等应用团队发版,应用团队改路由要等运维动 Ingress,运维加一条路径要重新评估证书向谁签发。

而真正把矛盾顶到台面上的,是实时音视频。WebRTC 的媒体面是 UDP,Ingress 从模型上就不承载它。我们试过用自己的四层转发硬顶,结果是扩容、超时和重试的语义全都不对——七层代理那种“贴心的重试”,对媒体流是灾难。

变化就这样出现了:入口的诉求,从“能进得来”,变成了“多团队能各自改、多协议能各自走、权限能各自担”。

一个入口起
三问定边界
旧法先跑通
新需再验证

02、问题

旧方案不是突然坏的,是被三个同时发生的变化撑破的。

第一,入口资源随服务数量线性增长。每个对外服务一个 LoadBalancer,意味着每个服务一份公网 IP、一份证书、一份四层监听。IP 是稀缺资源,证书续期是运维日历上固定响起的闹钟。

第二,变更耦合。应用团队想改一条路径,得改集群共享的 Ingress 对象;平台团队想收紧权限,就得先保证没人依赖那些私有注解。双方被锁在同一个对象上。

第三,协议错配。七层入口的定义域是 HTTP(S),实时音视频的 UDP 从来不在里面。硬塞的结果,是把一个协议问题伪装成配置问题,排查时永远找不到真正的失败点。

业务影响很直接:一次小灰度需求要跨三个团队,上线时间由“谁的队列先空”决定;一次注解误改会波及其他域名;实时音视频的入口调整长期只能靠人工介入。

技术表现上,能观察到的信号有四个:集群里公网入口地址数量居高不下;同一个 Ingress 对象被多个团队编辑;实现私有的注解缺乏 schema 校验,错误只能靠事故发现;UDP 服务要么裸奔,要么被塞进一个语义不匹配的代理。

所以我们对“入口要变成什么样”写了四条可验证的完成标准:

  1. 应用团队在自己命名空间内创建一条路由资源即可生效,不需要平台团队介入;
  2. 对外 HTTP(S) 只占用一组共享入口地址,不再随服务数量增长;
  3. 监听端口与 TLS 证书由平台团队单点控制,应用团队无法覆盖;
  4. UDP/TCP 媒体面有独立且可评估的路径,不与 HTTP 抢占同一入口。

第四条尤其重要,它是“要不要把音视频也交给 Gateway API”这个问题的判据。答案是先评估,不是先迁移。

旧法随需变
端口渐成墙
标准先写死
变更才有度

03、原理

要回答前面的问题,先得把这几类资源的层级关系摆正。

ClusterIP、NodePort、LoadBalancer 是同一层的三种形态。 Kubernetes 官方文档对 Service 的定义是:它把一组 Pod 抽象成一个稳定的网络入口。ClusterIP 给的是集群内的虚拟 IP,转发规则由 kube-proxy 用 iptables、IPVS 或 nftables 之类的机制落地在节点上。NodePort 在其之上,于每个节点的一段端口范围(默认 30000–32767,具体范围以当前官方文档为准)开放同一个入口。LoadBalancer 又是 NodePort 之上的扩展:它依赖集群外的负载均衡实现,把流量先送到节点端口,再交给 Service。

也就是说,这三者不是“三个选项”,而是“三种暴露半径”:只在集群内、可以在节点上、可以被集群外访问。它们共同的盲区是——只认 IP 和端口,看不懂 Host 和 Path。

Ingress 与 Gateway API 是另一层:七层入口的控制面。 Ingress 引入了 Host/Path 匹配和 TLS 终止,让“一个入口地址承载多个域名”成为可能。但它的模型很扁平:一个 Ingress 对象加一个 IngressClass,扩展能力靠实现私有的注解。Kubernetes 官方文档已明确,Ingress 处于稳定状态但不再新增能力,Gateway API 是其继任者。

Gateway API 的真正增量在角色拆分。 它把入口拆成三层资源:GatewayClass 由基础设施提供方定义(“我用哪个实现”),Gateway 由集群运维或平台团队持有(“监听哪些端口、终止哪些证书、允许哪些命名空间挂上来”),HTTPRoute 由应用开发者在自己命名空间里声明(“我的域名和路径怎么转”)。跨命名空间引用后端,还需要 ReferenceGrant 显式授权。

反直觉判断:Gateway API 的核心价值不是“功能比 Ingress 多”,而是“把所有权写进了资源边界”。数据面完全可以是同一个代理进程,变的只是谁能改哪一层。

反直觉判断:ClusterIP 之所以稳定,恰恰因为它不允许你依赖它背后的具体 Pod。端点漂移由 kube-proxy 处理,应用侧只看到虚拟 IP 不变。这跟入口设计是同一个道理——稳定的对外契约,来自被隐藏的内部变化。

反直觉判断:把 UDP 塞进七层网关不是配置难题,是语义错配。七层网关擅长的是基于 HTTP 语义的匹配、重试、超时和灰度,而媒体流需要的是长连接、低时延和尽量少的中介状态。层次选错,配置再对也是错的。

还有一点必须提前说清楚:Gateway API 把 TCPRoute、UDPRoute、TLSRoute 放在 experimental 通道,standard 通道只包含 GatewayClass、Gateway、HTTPRoute、GRPCRoute、ReferenceGrant 这类资源。所以“用 Gateway API 统一收口 UDP”这件事,第一步不是写 YAML,而是确认你选的实现到底支持不支持。资源清单与通道划分请按当前官方文档核验。

四层看端口
七层看路径
反直觉之处
恰是分权根

04、架构

[输入] 公网 HTTP(S) 请求 / WebRTC 媒体流
    |
    v
[模块] 双平面入口:Gateway(七层控制面) + 四层负载均衡(媒体面)
    |
    v
[数据/状态] Listener 与 TLS 证书、Route 规则与所有权、Service Endpoint
    |
    v
[处理] 七层按 Host/Path 匹配转发;四层按端口转发至媒体 Pod
    |
    v
[输出] 数字人 Web/API 响应 / 实时音视频媒体流

边界:

  • 平台团队持有 Gateway 与证书:监听端口、TLS、allowedRoutes 白名单;
  • 应用团队持有 HTTPRoute:域名、路径、后端 Service、灰度权重;
  • 跨命名空间引用必须由目标命名空间的 ReferenceGrant 授权,不能靠默认放行;
  • 媒体面的 UDP/TCP 不进入七层控制面,走独立的四层负载均衡路径。

收益:入口地址从“每服务一个”收敛为“每集群一组”;路由变更从跨团队工单变成命名空间内的一次 apply;协议分层之后,七层网关的重试与超时语义不会污染媒体流。

代价:多了一层抽象,排障链路变长——一个 404 可能来自 Route 匹配、Listener 准入、后端 Endpoint 三段中的任何一段;同时 GatewayClass 与实现绑定,选实现等于选了一部分未来。

适用条件:多团队共用集群、需要按命名空间分权、HTTP 与 UDP/TCP 混合存在的场景收益最大。如果只有一两个服务、没有公网入口诉求,Service 加一台 Ingress 控制器反而更省心。

一图分两层
上下各司职
收益随边界
代价亦同行

05、实战一次

先跑一个最小闭环:一个集群、一个 Gateway、一条 HTTPRoute,验证所有权模型真的能跑通。以下命令未在当前环境实测,以下为预期结果;版本号请按各项目当前官方文档核验,输出为示例输出。

环境与依赖:

  • 一个本地 Kubernetes 集群(kind 或 minikube 均可);
  • Gateway API 的 standard 通道 CRD;
  • 一个符合 Gateway API 的实现(本文以 Envoy Gateway 为例,其他实现同样适用)。
kind create cluster --name dh-gateway

# 1) 安装 Gateway API standard 通道 CRD(版本以官方 release 页为准)
kubectl apply --server-side -f \
  https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml

# 2) 安装一个实现(这里以 Envoy Gateway 官方安装方式为例,具体命令以官方文档为准)
helm install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.1.0 \
  -n envoy-gateway-system --create-namespace
kubectl wait --timeout=5m -n envoy-gateway-system \
  --for=condition=Available deployment/envoy-gateway

业务命名空间与最小后端:

kubectl create namespace digital-human
kubectl label namespace digital-human gateway-access=true
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dh-api
  namespace: digital-human
spec:
  replicas: 2
  selector:
    matchLabels:
      app: dh-api
  template:
    metadata:
      labels:
        app: dh-api
    spec:
      containers:
      - name: api
        image: nginx:1.27-alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: dh-api
  namespace: digital-human
spec:
  selector:
    app: dh-api
  ports:
  - name: http
    port: 8080
    targetPort: 80

Gateway 由平台团队持有:

kubectl create namespace dh-infra
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: dh-gateway
  namespace: dh-infra
spec:
  gatewayClassName: eg
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            gateway-access: "true"

HTTPRoute 由应用团队持有:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: dh-web
  namespace: digital-human
spec:
  parentRefs:
  - name: dh-gateway
    namespace: dh-infra
  hostnames:
  - "web.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: dh-api
      port: 8080

启动与请求:

kubectl apply -f api.yaml -f httproute.yaml
kubectl wait -n digital-human --for=condition=Available deployment/dh-api --timeout=120s

ADDR=$(kubectl get gateway dh-gateway -n dh-infra \
  -o jsonpath='{.status.addresses[0].value}')

curl -s -o /dev/null -w '%{http_code}\n' \
  -H 'Host: web.example.com' "http://${ADDR}/api/"

示例输出:

# kubectl get gateway,httproute -A
NAMESPACE       NAME                                          CLASS   ADDRESS       PROGRAMMED   AGE
dh-infra        gateway.gateway.networking.k8s.io/dh-gateway  eg      172.18.0.4    True         40s

NAMESPACE       NAME                                      HOSTNAMES              AGE
digital-human   httproute.gateway.networking.k8s.io/dh-web  ["web.example.com"]   38s

# curl ...
200

验证清单:

  • kubectl get gatewayclass 中 eg 的 ACCEPTED 为 True;
  • Gateway 的 PROGRAMMED 为 True 且有 ADDRESS;
  • HTTPRoute 的 status.parents[].conditions 中 Accepted=True;
  • 未打 gateway-access=true 标签的命名空间创建同名 HTTPRoute,应被 Listener 挡下(这是很有价值的负向验证)。

UDP 的评估不放进这个闭环,而是单独进行:先确认实现是否支持 UDPRoute(standard 通道不含该资源),再决定是否使用。

apiVersion: v1
kind: Service
metadata:
  name: dh-rtc-media
  namespace: digital-human
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: dh-rtc
  ports:
  - name: rtc-udp
    protocol: UDP
    port: 3478
    targetPort: 3478
  - name: rtc-tcp
    protocol: TCP
    port: 443
    targetPort: 443

要点:媒体面走四层,源地址是否保留由 externalTrafficPolicy 决定,代价是流量只落到有 Pod 的节点;同一 Service 混用 TCP/UDP 端口的支持情况,需按当前官方文档与所用云实现核验。

V1 到这里是通的,但它留下了明确的问题:媒体面与 HTTP 面是两套独立入口,命名空间权限全靠标签和 selector,而且还没有真正验证“应用团队自己改灰度”这条路径。

先跑最小集
再谈规模化
未见实测者
只报预期果

06、排查

下面三个现象是这类迁移中真实会遇到的,按“现象 → 怀疑 → 检查 → 证据 → 根因 → 修复”展开。

现象一:HTTPRoute 创建成功,请求返回 404。

怀疑:域名不匹配;路径写错;或者 Route 根本没被 Listener 接受。

检查:

kubectl get httproute dh-web -n digital-human -o yaml
kubectl get svc -n digital-human dh-api -o wide
kubectl get endpointslice -n digital-human -l kubernetes.io/service-name=dh-api

证据:status.parents 中的条件是 Accepted=False、Reason=NotAllowedByListeners;同时 EndpointSlice 里端点正常。

根因:命名空间没有 gateway-access=true 标签,Gateway 的 allowedRoutes 选择器把它挡在了外面。路由对象是被写入的 Kubernetes 资源,但没被任何 Listener 接纳——这就是七层入口“能创建 ≠ 能生效”的典型表现。

修复:给命名空间补标签,或把 allowedRoutes 调整为显式命名空间白名单。

现象二:Gateway 一直没有 ADDRESS,PROGRAMMED 为 False。

怀疑:实现没装好;或者 GatewayClass 的 controllerName 与实现不匹配。

检查:

kubectl get gatewayclass
kubectl get gateway dh-gateway -n dh-infra -o jsonpath='{.status.conditions}'
kubectl get pods -n envoy-gateway-system

证据:GatewayClass 的 Accepted=False,message 指向一个没有任何控制器在监听的 controllerName。

根因:装的是 CRD,不是实现。CRD 只提供资源类型,真正把这些资源翻译成转发规则的是控制器。

修复:按官方文档安装控制器,并确认 Gateway 的 spec.gatewayClassName 指向一个 Accepted=True 的 GatewayClass。

现象三:后端服务看到的客户端地址全是节点 IP,风控和限流规则失效。

怀疑:四层转发的源地址保留策略。

检查:在后端打印连接的对端地址,或读取网关注入的转发头。

证据:对端地址落在节点网段,且不同节点各不相同。

根因:externalTrafficPolicy 使用默认的 Cluster 时,流量会在集群内再跳一次,源地址被 SNAT。

修复:媒体面服务改为 externalTrafficPolicy: Local,同时接受“流量只落到有 Pod 的节点”这一代价,并配合拓扑分布约束避免单节点热点。

错误尝试: 迁移之初,我们尝试把 Ingress 上的 nginx.ingress.kubernetes.io/rewrite-target 之类的注解原样搬到 HTTPRoute 上,期望改写行为一比一复刻。

为什么错:Ingress 的扩展面是实现私有的、没有 schema 的注解;Gateway API 用 filters 这类有类型定义的标准字段表达同一件事(URLRewrite、RequestHeaderModifier、RequestRedirect 等)。注解不会被当成“无害”,它会被静默丢弃——资源 Accepted=True,但行为完全不变。这种失败最危险,因为它把“配置写错”伪装成了“数据面故障”。

现象非根因
状态自会言
错误一尝试
胜过十猜想

07、优化

把第 06 章的三条证据放在一起看,根因其实只有两条:入口的所有权没有被写进资源边界,协议被放进了错误的层。

V2 的四项修改如下。

修改一:HTTP(S) 全部收口到 Gateway,Ingress 只减不增。

原因:Gateway 的 Listener 与 allowedRoutes 提供了可被审计的准入边界,而 Ingress 的边界是“谁有 RBAC 谁能改整个对象”。

新行为:平台团队维护 Gateway(端口、证书、命名空间白名单),应用团队在各自命名空间维护 HTTPRoute,新增域名不再产生公网 IP。

验证:kubectl auth can-i create httproute -n digital-human 为 yes,而 kubectl auth can-i update gateway -n dh-infra 为 no;两者同时成立,才算分权到位。

修改二:跨命名空间引用一律显式授权。

原因:parentRefs 指向别的命名空间的 Gateway、backendRefs 指向别的命名空间的 Service,都需要明确许可。

新行为:新增引用时同时提交 ReferenceGrant,让授权关系在代码审查里可见。

验证:删除 ReferenceGrant 后路由条件从 Accepted 变为被拒绝,恢复后重新生效。

修改三:媒体面独立为四层负载均衡,不进七层控制面。

原因:UDP 不在七层网关的定义域内,强行统一只会牺牲媒体质量。

新行为:RTC 媒体走独立的 LoadBalancer Service,配 externalTrafficPolicy: Local。

验证:kubectl get svc dh-rtc-media 的 EXTERNAL-IP 就绪后,用客户端做一次连接建立与端口探测,确认源地址为真实客户端地址。这些是验证步骤,不是已经拿到的指标。

修改四:把“被拒绝的原因”作为可观测项。

原因:分权之后,最常见的问题从“配置写错”变成“配置不被接受”。

新行为:对 GatewayClass、Gateway、HTTPRoute 的 status.conditions 建立告警,关注 Accepted、Programmed、ResolvedRefs 三类条件。

验证:人为制造一次 allowedRoutes 不匹配,确认告警能被触发。

这四项里,前两项解决所有权,第三项解决协议层次,第四项解决分权之后的可观测性。它们都不能靠换一个更贵的负载均衡器解决。

根因定改法
改法验根因
无据不下数
有证才称优

08、演进

[同一份输入:多团队、多协议的入口诉求]
    |
    +--[V1] NodePort / LoadBalancer / Ingress 叠用
    |      行为:能通,能按域名路由
    |      代价:入口随服务增长、注解即权限、UDP 无处安放
    |
    +--[V2] Gateway API 收口 HTTP + 四层平面承载媒体
    |      行为:所有权分离、单组入口地址、协议各归其层
    |      代价:多一层抽象、实现选择带来隐性依赖、排障链路变长
    |
[Trade-off]
得到:自服务路由、单点 TLS、可审计的准入
失去:单对象的简单心智、单一控制器的一站式排障
适用边界:多团队共用集群、HTTP 与 UDP/TCP 混合、需要按命名空间追责

逐项比较。

正确性:V2 并不比 V1 更“正确”,只是把原本靠约定维持的边界,变成了 API 强制的边界。约定会被遗忘,API 会报错。

稳定性:V1 的稳定性来自“没人敢改”;V2 的稳定性来自“改错了会被 status 挡住”。后者在团队规模扩大后更可靠。

复杂度:V2 增加了一个控制面抽象与若干 CRD,学习成本真实存在。收益只在多团队、多协议场景下才会超过成本。

成本:入口地址数量收敛是主要收益来源,但四层媒体面仍需独立的负载均衡资源。具体成本需结合所用云厂商的负载均衡计费方式评估。

适用范围:单团队小集群不建议迁移;多业务线共用集群且已经出现“路由变更排队”现象时,收益明显。

遗留问题:一是实现绑定,GatewayClass 一旦选定,换实现需要重新验证行为与性能;二是 experimental 通道的资源(TCPRoute、UDPRoute、TLSRoute)在 standard 通道中不存在,能力与实现强相关;三是四层与七层两套入口的证书与会话策略如何统一,仍需各自维护。

同一份输入
两条演进路
所得即所失
边界即自由

09、洞见

9.1 资源所有权比功能清单更能决定迁移成败

Ingress 与 Gateway API 在“能不能按域名路由”这件事上差别不大,真正的差别是谁能改哪一层。迁移失败的项目,多数不是功能不够,而是把 Gateway 也交给了应用团队,等于换了个名字继续耦合。评估一个入口方案时,先问“这份 YAML 归谁”,再问“它支持什么”。

9.2 反直觉判断:Gateway API 不会让你少写 YAML

引入角色拆分之后,一份入口配置从一个对象变成 GatewayClass、Gateway、HTTPRoute 三份,还可能再加一份 ReferenceGrant。写的东西变多了,但每一份都有了明确的归属和评审路径。抽象的成本不会消失,它只是从“协调成本”变成了“文件数量”。

9.3 反直觉判断:四层入口不是落后,而是媒体流的正确层

“能上七层就上七层”是 HTTP 世界的惯性。但七层网关的重试、超时、请求缓冲这些能力,对长连接的媒体流是负资产。给 UDP 找入口时,正确的方向是找一个懂四层的负载均衡实现,而不是给七层网关打补丁。

9.4 选择实现的自由度,是最大的隐性成本

Gateway API 只定义资源模型,不提供数据面。这意味着你可以在不同实现之间选择,也意味着行为差异、扩展方式、性能特征都取决于实现。选型时应当把“迁移到另一个实现的成本”写进决策文档,而不是只比较当前功能。

9.5 分权的前提是“拒绝可见”

把权力分出去之后,故障形态会从“改错了”变成“改对了但没生效”。因此 status.conditions 里的 Accepted、Programmed、ResolvedRefs 不是可选的可观测项,而是分权架构的必要配套。没有拒绝原因的透明化,分权只会把排障成本转嫁给应用团队。

功能易比较
权责难迁移
判在取舍处
见在边界间

10、系统落地

原来有什么:NodePort 加自建 Nginx、按服务数量增长的 LoadBalancer、一个共享的 Ingress 控制器,以及一套“谁申请谁找运维”的口头流程。

本篇新增了什么:一个由平台团队持有的 Gateway,负责监听端口、TLS 与命名空间准入;一套由应用团队在各自命名空间内维护的 HTTPRoute;一个独立于七层控制面的四层入口,专门承载实时音视频的 UDP/TCP;一组以 status.conditions 为核心的入口可观测项。

现在能做什么:应用团队可以在不申请公网 IP、不等待平台团队的情况下发布或回滚一条 HTTP 路由;平台团队可以用 YAML 回答“哪些命名空间允许挂到这个网关上”;实时音视频有了明确的协议归属,而不是继续挂在七层网关下面勉强运行。

还缺什么:证书的自动化签发与轮换尚未纳入 Gateway 的生命周期;多集群场景下入口如何就近接入没有结论;灰度能力目前依赖后端权重,尚未与路由层的流量切分打通;四层与七层两套入口的告警口径还没有统一。

下一步如何演进:先把证书管理收敛到 Gateway 的 Listener 上,让“新域名”不再触发人工签发流程;再把灰度从后端权重上移到 HTTPRoute 的流量切分能力;最后评估是否需要多集群入口,把单集群的入口模型扩展成“集群组”的模型。每一步都以当前官方文档与所选实现的能力为准,不提前引入没有落地路径的抽象。

原有只入口
新增是归属
能做什么事
还缺哪一环

11、小结

Q1 → ClusterIP / NodePort / LoadBalancer 是同一暴露层的三种形态,Ingress 与 Gateway API 是七层入口的控制面
Q2 → 被替换的主要是资源的所有权模型与扩展方式,数据面可以复用同一个代理进程
Q3 → 七层网关的超时、重试与扩容语义对媒体流是负资产,UDP 应留在四层
状态 → 可交付一套最小双平面入口:HTTP 走 Gateway + HTTPRoute,音视频走独立四层入口

三问皆已答
一图收全局
状态非终点
交付即闭环

12、作业

12.1 理解题:ClusterIP、NodePort、LoadBalancer 三者的关系是什么?

参考答案:它们是同一层暴露能力的三种半径。ClusterIP 只在集群内可达;NodePort 在 ClusterIP 之上于每个节点开放一段高位端口;LoadBalancer 又在 NodePort 之上,依赖集群外的负载均衡实现把外部流量送进来。三者都只认 IP 和端口,不承载 Host/Path 语义,因此不能替代 Ingress 或 Gateway API 的七层职责。

12.2 实战题:给出一个最小可用的 Gateway 与 HTTPRoute 组合,要求只有被显式标记的命名空间可以挂载路由。

参考答案:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: dh-gateway
  namespace: dh-infra
spec:
  gatewayClassName: eg
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            gateway-access: "true"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: dh-web
  namespace: digital-human
spec:
  parentRefs:
  - name: dh-gateway
    namespace: dh-infra
  hostnames:
  - "web.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: dh-api
      port: 8080

配套操作:kubectl label namespace digital-human gateway-access=true。若不加标签,HTTPRoute 仍然创建成功,但 status.parents[].conditions 会给出未被 Listener 接受的条件,这正是需要检查的地方。

12.3 排障题:HTTPRoute 创建成功但访问返回 404,按什么顺序排查?

参考答案:从外到内逐段缩小范围。第一步看路由是否被 Listener 接受,检查 status.parents 中的条件与 reason;第二步看 Host 与 Path 是否与请求一致,注意 PathPrefix 与 Exact 的差别;第三步看 backendRefs 指向的 Service 端口是否正确,以及 EndpointSlice 是否有就绪端点;第四步才怀疑数据面。跳过前两步直接重启网关控制器,是最常见的无效操作。

12.4 架构判断题:把 WebRTC 的 UDP 媒体流统一改为经 Gateway API 的 UDPRoute 进入集群,是否合理?

参考答案:默认不合理,除非所选实现明确支持且媒体服务质量已验证。原因有三:一是 UDPRoute 属于 experimental 通道,standard 通道不含该资源,能力完全取决于实现;二是七层网关的重试、超时与缓冲语义对长连接媒体流是负资产;三是媒体流通常需要源地址保留与拓扑感知,四层负载均衡配合 externalTrafficPolicy 更直接。合理做法是把 HTTP 面交给 Gateway API,媒体面独立评估四层路径。

理解定坐标
实战见真章
排障循证据
判断看边界

13、思考

回到最开始的那个任务:一条新域名、一条新路径、一个 10% 的灰度。在 V1 的架构下,它需要三个团队排队;在 V2 的架构下,它是应用团队自己命名空间里的一次提交。

变化的关键不在于选择了哪个负载均衡器,而在于我们把两件事写进了架构:每份配置都有唯一的主人,每种协议都待在自己该待的层。

ClusterIP、NodePort、LoadBalancer、Ingress、Gateway API 从来不是一道五选一的题。前三个决定流量从哪个半径进入集群,后两个决定进入之后怎么被理解和归属。把它们混在一起比较,就会不断得到“好像都行”的结论。

真正值得反复问的是三个问题:这份配置归谁?它失效时谁先知道?它被拒绝时能不能说出原因?这三个问题的答案,比任何一次选型都更能决定入口架构能撑多久。

一念辨边界
一证定根因
一改知取舍
一役见真章

posted @ 2026-10-12 10:20  李福春  阅读(4)  评论(0)    收藏  举报