☰
K8s集群入口流量实践:Gateway API + Istio + MetalLB 落地与排障
2026/10/10 16:08:07 网站建设 项目流程

年后刚开始折腾集群网络时,我接到了一个看起来不起眼、实际上很磨人的需求:把一套 K8s v1.35 集群里的服务通过 Gateway API 暴露出去,并且让 Istio 接管南北向流量,同时还要让外部访问入口拥有一个固定的 VIP——这正好是 MetalLB 最擅长的场景。本来以为照着文档配置几个小时就能收工,结果一路踩坑踩到怀疑人生。今天把整个落地过程、关键配置和排查思路完整整理出来,希望对同样在折腾 k8s、gateway、istio、metalb 这套组合的朋友有点参考价值。

先说清楚这套方案的核心价值:Gateway API 正在逐步替代传统 Ingress,它用更标准化的资源模型定义流量的路由和转发规则;Istio 作为成熟的服务网格,能基于 Gateway API 处理更细粒度的南北向流量治理;而 MetalLB 则是在裸金属环境里为 LoadBalancer 类型服务分配免静态 IP 的 VIP。三者组合,基本就是当前在自建 K8s 集群里做入口流量的主流选择之一,适合需要精细化流量管理且没有云厂商自带 LB 的场景。

1. 方案拆解:为什么是 Gateway API + Istio + MetalLB

1.1 传统 Ingress 与 Gateway API 的差异

K8s 里的 Ingress 资源已经用了很多年,但它的问题也相当明显:Ingress 只有一种标准资源,路由规则却分散在不同的 Ingress Controller 注解里,比如 nginx 用nginx.ingress.kubernetes.io/rewrite-target,traefik 用traefik.ingress.kubernetes.io/rule-type。一旦想从 nginx 切成 traefik,或者换成其他 Controller,就得改一批注解,可移植性很差。

Gateway API 把这类问题分成了三个独立层级:GatewayClass描述基础设施提供方的能力,Gateway描述具体的入口资源(对应一个负载均衡器实例),HTTPRoute等路由资源描述具体流量如何到达后端服务。这种分层带来的直观好处是,路由规则不再和某个具体实现强绑定,不同团队可以分别管理不同层级。

还有一个容易忽略的点,Ingress 规范本身对四层协议的支持比较弱,而 Gateway API 原生支持 TCPRoute、UDPRoute 等更丰富的协议类型。虽然 Istio 的 Ingress Gateway 本身很强,但如果你想用更标准的方式管理 L4/L7 流量,Gateway API 显然更符合长期演进方向。

1.2 Istio 在其中扮演的关键角色

Istio 在这套方案里不只是做简单的流量转发,它同时承担了 Gateway 数据面的实际实现和更复杂的流量治理能力。通过 Istio 内置的istio-ingressgateway组件作为 Gateway API 的基础数据面,你可以同时获得金丝雀发布、基于请求头的分流、熔断限流、mTLS 加密等高级能力。

很多第一次接触 Istio 的朋友会误以为 Gateway API 和 Istio 是二选一的关系,其实两者是互补的:Gateway API 定义“入口长什么样”,Istio 负责“入口背后的策略怎么执行”。启用 Istio 的 Gateway API 支持后,你创建 Gateway 资源时,Istio 会自动同步生成对应的 Kubernetes Service,并绑定相应的数据面工作负载。

1.3 MetalLB 解决 VIP 分配问题

裸金属环境没有云厂商的负载均衡器,MetalLB 就成了最方便的选择。它允许你把一段未被占用的 IP 地址池配置到集群,在检测到 LoadBalancer 类型 Service 时,自动从 IP 池中分配一个虚拟 IP(VIP)并广播到二层网络或通过 BGP 通告出去。用户访问这个 VIP,流量会到达节点,再通过 kube-proxy 或其它机制转发到实际 Pod。

选择 MetalLB 的原因很直接:它部署简单,只需要一个 Helm Chart 或者 YAML 文件;支持 L2 和 BGP 两种模式;兼容性足够好,不依赖特定网络插件。L2 模式下,VIP 绑定在某个节点上,该节点作为入口转发流量,因此故障切换时不可避免会有短暂的 ARP 切换时间,但对大多数业务场景完全够用。

2. 环境准备与工具链搭建

2.1 集群版本与网络前提

动手之前,务必确认集群版本信息。我这边用的是 kubeadm 部署的 v1.35 集群,这个版本比较新,你在参考时要注意自己的环境差异,尤其是某些 API 资源版本是否已经变更。可以用下面的命令检查版本和节点状态。

kubectl version --short kubectl get nodes

在配置网络之前,需要确保底层网络插件工作正常。以 Calico 为例,检查calico-node是否完全 Running。如果网络插件异常,后续 MetalLB 广告 VIP 时经常会出现不可达的情况。

2.2 安装必要工具:kubectl、helm、istioctl

kubectl 一般随集群管理环境自带,重点检查 helm 和 istioctl。helm 用于快速部署 MetalLB,istioctl 则是安装和管理 Istio 的常用工具。如果没有安装,可以这样处理:

curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get_helm-3 chmod 700 get_helm.sh ./get_helm.sh # 安装 istioctl,注意选择与 K8s v1.35 兼容的版本 curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.24.1 sh - export PATH=$PWD/istio-1.24.1/bin:$PATH

注意:istio 版本和 Kubernetes 版本的兼容矩阵值得花两分钟核对。Istio 官方文档里会标注每个版本经过测试的 K8s 版本范围,比如 Istio 1.24.x 对 1.28 到 1.35 的支持情况。版本跨度太大时,可能出现 webhook 注入异常或网关资源无法创建等奇怪问题。

2.3 准备命名空间与标签

后续所有测试资源最好统一放在独立命名空间里,方便清理。我习惯用istio-system放控制面相关资源,用demo放测试业务。

kubectl create namespace demo kubectl label namespace demo istio-injection=enabled

istio-injection=enabled这个标签很关键,它让后续创建的 Pod 可以自动注入 Envoy Sidecar,这样应用流量才会被 Istio 的数据面接管。

3. 核心配置实现详解

这一节是全文的重点。我会按照从底层到上层的顺序,依次配置 MetalLB、Istio Gateway API 以及路由规则,每一步都说明背后逻辑。

3.1 部署 MetalLB 并配置 IP 池

MetalLB 的部署非常简洁,使用 Helm 即可:

helm repo add metallb https://metallb.github.io/metallb helm install metallb metallb/metallb -n metallb-system --create-namespace

等待 MetalLB 控制器和 Speaker 都运行起来后,接下来配置 IP 池。这里需要注意,IP 池必须是你所在子网里没有被占用的地址,并且需要预留出足够的地址给后续多个 Service 使用。假设你的业务网段是 192.168.1.0/24,我习惯预留一段 100 到 200 的地址。

创建IPAddressPool资源:

apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: production-pool namespace: metallb-system spec: addresses: - 192.168.1.100-192.168.1.200

同时创建L2Advertisement来广播这些 IP:

apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: l2-advertise namespace: metallb-system spec: ipAddressPools: - production-pool

提示:检查资源是否创建成功可以用kubectl get ippool -n metallb-system。如果没看到列出production-pool,说明 MetalLB CRD 没有正确安装,可能是 Helm 仓库源不是官方最新版。MetalLB 从 0.13 版本起,配置从 ConfigMap 切换成了 CRD,旧文档里的方式基本不适用了。

3.2 启用 Istio 的 Gateway API 支持

先安装 Istio base 和控制面,然后单独启用 Gateway API 的支持。

istioctl install --set profile=demo --skip-confirmation istioctl install --set profile=gateway --skip-confirmation

第一步会安装 Istiod 控制面和默认的 Ingress Gateway;第二步安装 Gateway API 所需的 CRD。如果只想使用 Gateway API 而不要传统 Ingress Gateway,可以不加profile=gateway。但既然通过 Gateway API 最终会生成 LoadBalancer 类型的 Service,而数据面还是由 Istio 网关控制器管理,所以装一个并无坏处。

安装完成后,验证 Istio 托管的 CRD 是否就位:

kubectl get crd | grep gateway

正常情况下可以看到gateways.gateway.networking.k8s.io、httproutes.gateway.networking.k8s.io、gatewayclasses.gateway.networking.k8s.io这几个 CRD。如果缺少,说明 Gateway API 版本没有正确安装,需要手动补装对应版本的 CRD。

3.3 创建 GatewayClass 与 Gateway 资源

Gateway API 引入的第一层资源是GatewayClass,它描述了“用哪一种实现来做网关”。这里我们指定 Istio 作为实现方。

apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: istio-internal spec: controllerName: istio.io/gateway-controller

创建GatewayClass后,再创建Gateway。这里要注意的是,Gateway中声明监听端口时,需要指定 80 端口(HTTP)和 443 端口(HTTPS)。为了让 MetalLB 给这个 Gateway 分配 VIP,还需要设置Service注解,让生成的 Service 类型为 LoadBalancer。

apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: demo-gateway namespace: demo spec: gatewayClassName: istio-internal listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: All - name: https protocol: HTTPS port: 443 tls: certificateRefs: - kind: Secret name: demo-tls allowedRoutes: namespaces: from: All

当这个 Gateway 创建出来后,Istio 会为它生成一个 LoadBalancer 类型的 Service,MetalLB 侦测到后会自动分配 VIP。过一会儿执行:

kubectl get gateway -n demo kubectl get svc -n demo

demo-gateway-istio这个 Service 的EXTERNAL-IP大概率会显示为 192.168.1.100 之类的地址,这个就是你的 VIP。

3.4 配置 HTTPRoute 与应用路由

Gateway 本身只是一个入口,具体流量如何分流要靠HTTPRoute规则。假设你有一个nginx服务在 demo 命名空间,那么我们来定义一条路由,把访问 VIP 的/路径转发给它。

apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: demo-route namespace: demo spec: parentRefs: - name: demo-gateway hostnames: - "demo.example.com" rules: - matches: - path: type: PathPrefix value: / backendRefs: - name: nginx port: 80

这里指定了hostnames,意味着只有当请求头中Host是demo.example.com时才会匹配这条路由。没有域名或者不想用域名时,可以去掉hostnames字段,这样所有到 VIP 的请求都会匹配该路由。

创建后,可以通过查看 HTTPRoute 状态确认它是否已经被 Gateway 接受:

kubectl describe httproute demo-route -n demo

如果状态里出现Accepted: True,说明配置已生效。

3.5 验证 VIP 与流量转发

在客户端机器上使用 VIP 访问。假设你不需要真实域名解析,可以在/etc/hosts中临时加入一条192.168.1.100 demo.example.com。如果你没有配置 TLS,直接访问 HTTP 端口,注意不要强制跳转 HTTPS。

curl -v --resolve demo.example.com:80:192.168.1.100 http://demo.example.com/

如果看到 200 响应,说明整条链路已经打通:客户端到 VIP,再到 Istio 数据面网关,再按 HTTPRoute 规则转发到后端 Nginx Pod。

4. 真实环境踩坑与排查记录

以下这些问题是我在这次配置过程中实际遇到过的,也是社区里高频出现的。整理出来希望能帮你少走点弯路。

4.1 控制平面初始化异常:The API server is not healthy

标题里的热搜词出现了这样的报错,说明不少人在 kubeadm 初始化时遇到过:

[wait-control-plane] waiting for the API server to be healthy after 4m0.00747357s

这个问题的根源不止一种,最常见的包括:

apiserver 容器启动失败:通常是缺少镜像或者容器运行时异常。可以用crictl ps -a查看容器状态,重点关注 kube-apiserver 是否不断重启。如果是镜像拉取问题,提前在节点上拉好镜像再初始化能有效避免。

端口被占用或防火墙拦截:API Server 默认端口是 6443。如果宿主机有防火墙规则,或者某个进程占用 6443 端口,初始化就会一直等不到健康状态。可以在初始化前执行ss -lntp | grep 6443确认端口空闲。

kubelet 未正常启动:控制面组件是静态 Pod,由 kubelet 负责拉起。如果 kubelet 本身没有跑起来,API Server 自然无法健康。执行systemctl status kubelet查看日志,常会遇到证书路径不对或 kubelet 配置里缺少node-ip。

不健康的另一个常见原因:如果你使用 containerd 作为运行时,有时镜像仓库访问超时导致 sandbox 镜像拉不下来。可以手动调整 pause 镜像版本,或者把镜像换成内网可访问的地址。

当你解决完根因后,一般不用重新初始化。如果确认是节点配置问题,可以这样清理后重新加入或初始化:

kubeadm reset -f iptables -F && iptables -t nat -F systemctl restart kubelet kubeadm init ...

提示:kubeadm reset后再次初始化,务必清掉/etc/kubernetes下的旧配置,否则可能出现新旧证书冲突。

4.2 意外的 502 Bad Gateway

标题里的热搜词也提到了502 Bad Gateway,这种现象在 Istio + Gateway API 场景下非常常见,最常出现的问题是服务没监听在正确的端口,或者后端 Pod 没有就绪。

先确认 Service 对应 Endpoints 是否有地址:

kubectl get endpoints nginx -n demo

如果 Endpoints 为空,一般是 Pod 健康检查未通过或者标签选择器不匹配。再确认你的 HTTPRoute 里backendRefs写的端口是否与 Service 端口一致。

如果 Endpoints 正常,那问题可能出在 Envoy Sidecar 和后端应用的连接上。此时进入网关 Pod 检查日志比直接看 Kubernetes 事件更直观:

kubectl logs -l gateway.networking.k8s.io/gateway-name=demo-gateway -n demo --tail=100

日志里常见的报错是uvm432.cc switch local proxy failed这类底层连接异常,碰到这种就重点检查后端 Service 是否暴露了正确的 containerPort,以及链路里是否有其它代理干扰。

还有一种容易被忽略的情况:业务 Pod 本身关闭了 HTTP 的 keep-alive 超时,Envoy 空闲连接回收时会出现偶发 502。适当调整应用层和 Envoy 的IdleTimeout参数能缓解。

4.3 VIP 无法被外部访问的排查角度

Gateway 成功创建,VIP 也分配了,外部却 ping 不通或者 curl 超时。这时不要只盯着 Istio,先排查 MetalLB 是否真的宣告了 VIP:

kubectl get svc demo-gateway-istio -n demo -o yaml

看 Service 的metadata.annotations以及status.loadBalancer字段。MetalLB 会在 Service 上加metallb.universe.tf/ip注解。如果注解中有分配的 IP,继续检查 Speaker 日志:

kubectl logs -n metallb-system -l app=metallb --tail=50

另一个常见原因是 calico 或 flannel 之类 CNI 的 IP 冲突,VIP 和 Pod 网段重叠,导致数据包转发路径异常。务必确认 VIP 在物理网络里是空闲的,并且没有与容器网段重叠。

对于 L2 模式来说,VIP 实际绑定在某一台节点上,你可以查看 Speaker 日志确认哪台节点在担任 Leader。如果那台节点本身无法访问业务容器,也会导致 VIP 不通。这就得回到网络策略层面检查,确认节点到 Pod 的转发链路没有被容器网络策略拦截。

4.4 Gateway 资源状态一直显示 Not Accepted

有时候 GatewayClass 和 Gateway 资源都创建了,但kubectl get gateway显示状态不是Accepted,这多半是 controllerName 写错,或者指定的实现没有正确注册。执行以下命令查看详细事件:

kubectl describe gateway demo-gateway -n demo kubectl describe gatewayclass istio-internal

常见错误是 controllerName 写成istio.io/gateway-controller的旧格式或者大小写不一致,和 Istio 期望的 controller 名称对不上。这时检查 Istio 控制器日志,一般能更快定位。

4.5 Gateway 与 Istio Ingress Gateway 同时存在的冲突

如果你在集群里同时安装了传统 Istio Ingress Gateway,又创建了 Gateway API 的 Gateway 资源,两者可能会都尝试监听同一端口,或者资源抢占同一 IP。最好的实践是:生产环境明确指定入口方向,要么全部走 Gateway API,要么继续使用传统 Ingress Gateway,不要长期共存。如果你只是临时测试,可以给两类 Service 分别规划不同 IP 池,避免 IP 冲突。

5. 性能调优与个人实践心得

5.1 MetalLB 模式选择:L2 还是 BGP

MetalLB 支持 L2 和 BGP 两种模式。L2 模式部署简单,不需要路由器支持,但所有入口流量都会先到同一个节点,再转发到后端,这会导致单节点成为瓶颈。数据量不大还好,一旦流量规模上来,跨节点转发延时会增加。

BGP 模式需要网络设备支持 BGP 协议,并且需要在路由器上配置对等关系,但流量更均衡,每台节点都可能成为入口。生产环境流量较大时,可以优先考虑 BGP 模式;如果只是中小型集群,L2 模式的运维成本更划算。

我个人在测试环境通常使用 L2,生产环境则会建议客户评估 BGP。如果你打算走 BGP,还需要额外创建BGPPeer和BGPAdvertisement资源,这一步和常见的网络设备配置有关,务必提前和网络同事沟通好。

5.2 Istio 网关性能和 Sidecar 开销

Istio 网关本身本质上是 Envoy,性能取决于 CPU 和内存配额。建议为网关 Deployment 设置足够的资源限制,否则 CPU 受限时,并发连接上升后延迟会明显抖动。

resources: requests: cpu: 500m memory: 512Mi limits: cpu: 2 memory: 2Gi

此外,如果后端业务 Pod 都注入了 Sidecar,那流量链路是 客户端 -> VIP -> Envoy 网关 -> Envoy Sidecar -> 应用。每一跳都有额外的 CPU 开销。对性能极其敏感的非观测业务,可以评估关闭 Sidecar 注入,仅保留 Gateway 层。

5.3 一条很实用的经验:先握手后调优

整套方案落地后,不要急着做全链路压测或者复杂分流策略,先保证最小路径跑通。我的习惯是:先部署一个最简单的 Nginx Echo 头部信息服务,通过它确认 VIP 可达、HTTPRoute 生效、Istio 头处理正常,然后再上真实业务。这种“握手”式验证能快速区分问题出在底层网络、数据面转发还是应用本身,避免把时间浪费在错误的方向上。

5.4 关于 v1.35 版本的注意事项

K8s v1.35 是相对较新的版本,很多第三方组件未必完成了全面适配。如果你的集群里出现某些 CRD 需要使用新的v1或 Beta 版本 API,尽量参考官方文档中对应组件的兼容性说明。不要因为教程里的 YAML 适用于旧版本就直接照抄,一定要先确认 API 版本和 CRD 存在情况。比如 MetalLB 的新版本对 CRD 的字段校验更严格,写错一个字段就会提示schema validation error,这类问题只能靠多看一眼状态事件才能发现。

结尾的几句话

根据我实际折腾下来的体验,这套方案的稳定性其实相当不错,只要网络基础没问题,后续维护成本比起传统 Ingress 并没有高出太多。Gateway API 给了你更清晰的分层定义,Istio 提供了丰富的流量管理能力,MetalLB 则让 VIP 在裸金属环境里不再是奢侈品。如果你正准备在自己的集群里做同样的尝试,我建议先从最小链路开始,不要一上来就搞 HTTPS、证书、多集群等多个变量同时叠加。流量入口这种事,哪怕只是证书配置稍有差错,排查起来也可能花掉一整个下午。等最小链路跑通后,再逐步加上 TLS、域名、监控告警,最后再考虑优化性能,这样整个过程的节奏会更稳妥。希望这篇记录能给你提供一些有用的参考,如果碰到类似问题,顺着我上面给的排查顺序走,多半能顺利找到解决方案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询