ingress四层负载的本质是基于IP和端口转发的流量入口,它在Kubernetes集群边缘承担L4层数据包分发职责,与大家熟知的基于域名和路径路由的七层Ingress是完全不同的物种,一句话结论:四层ingress不关心HTTP头,只看“你要连哪个IP和端口”,直接把流量原样搬给后端Pod。
ingress四层负载和七层区别到底在哪
很多朋友第一次接触ingress四层负载时,脑子里全是问号,因为K8s官方文档里提到的Ingress资源,默认描述的是七层HTTP路由规则,你写一个host: foo.com、path: /bar,那是给Nginx Ingress Controller读的,它根据域名和URL路径帮你转发,但四层负载呢?它根本不看这些。
从OSI模型看两者的分工
- 四层工作在传输层,核心是TCP和UDP协议,它拿到数据包,看一眼目标IP和端口,直接转发,不拆包,不关心里面是HTTP、HTTPS还是自定义的RPC协议。
- 七层工作在应用层,解析HTTP报文,看Host头、看URI路径、看Cookie,做精细化路由。
行业内有个很形象的比喻:四层是快递分拣中心的分拣员,只看面单上的城市和站点编号;七层是前台文员,还要拆开信封看看内容再决定送到哪个部门,所以ingress四层负载的性能天花板远高于七层,因为它少了解析报文这一大段耗时操作。
一个容易混淆的点:Service类型和Ingress的关系
在K8s里,LoadBalancer类型的Service是云厂商提供的四层负载均衡,而ingress四层负载,通常指在Ingress Controller这一层实现TCP/UDP代理,以社区用的最多的nginx-ingress为例,它在启动时会读取一个ConfigMap(通常叫tcp-services或udp-services),把特定端口映射到集群内部的Service上,这就是典型的四层ingress工作模式。
为什么需要单独搞一套四层ingress
有人会问:既然Service的NodePort或LoadBalancer能暴露端口,何必再用ingress四层负载?
非HTTP协议的业务入口
比如你集群里跑着MySQL、Redis、Kafka、gRPC服务,gRPC虽然基于HTTP/2,但很多网关对它的支持并不完美,尤其是需要长连接和双向流时,直接走四层转发最省心,MySQL和Redis更是明确要求走TCP透传,不可能用七层去代理。
性能敏感型业务
据业界普遍的基准测试结论,同硬件条件下,四层代理的吞吐量通常是七层代理的
数倍,因为七层代理每收到一个请求都要做完整的HTTP解析、Header处理、路由匹配,CPU开销很大,而四层ingress只是改一下目标地址,转发效率极高,对于日活百万级的网关服务,这差距直接体现在云主机账单上。
统一流量入口管理
公司有多个K8s集群,或者混合云架构,运维希望所有入站流量都经过固定的几个入口节点,便于做安全审计和IP白名单,此时用ingress四层负载,可以把入口IP收拢,后端任意扩缩容都不影响客户端访问。
实战:如何配置ingress四层负载
以nginx-ingress-controller为例,操作路径非常清晰。
第一步:修改Controller启动参数
你需要给nginx-ingress-controller的Deployment加上两个命令行参数:
args: - /nginx-ingress-controller - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
不添加这两个参数,Controller完全不会监听四层端口,很多初学者卡在这一步,以为装好默认就能用,结果配置了ConfigMap毫无反应。
第二步:创建ConfigMap
假设你要把集群外部的3306端口转发给mysql-service的3306端口:
apiVersion: v1 kind: ConfigMap metadata: name: tcp-services namespace: ingress-nginx data: "3306": "default/mysql-service:3306"
注意格式:端口号是Controller监听的外部端口,值的格式是命名空间/Service名称:目标端口。
第三步:让Controller监听端口
修改Controller的Service,把3306端口暴露出去,如果是LoadBalancer类型,云厂商会帮你分配公网IP并放通安全组,如果是NodePort,则每个节点都会监听一个30000以上的随机端口。
完成这三步,外部客户端访问任意节点的3306端口,流量就会被转发到后端的MySQL Pod,整个过程Controller完全不会解析MySQL协议,纯粹是透传。
一次完整的ingress四层负载请求流程
我们把整个链路拆开来看,这样你对ingress四层负载是什么会有一个更立体的认知。
数据包的旅途
- 客户端发起TCP连接,目标地址是入口节点的公网IP,端口是
3306。 - 节点上的kube-proxy或云厂商LB把流量导到nginx-ingress-controller Pod所在的节点。
- Controller的nginx进程监听
3306端口,收到SYN包后,根据ConfigMap的映射关系,选一个后端的Pod IP。 - nginx与后端Pod建立新的TCP连接(或者复用已有连接池),把原始数据原封不动地丢过去。
- 后端Pod处理完业务,响应数据原路返回。
与会话保持相关的坑
四层负载最大的痛点是会话保持,因为nginx转发时基于upstream的轮询或哈希算法,如果客户端IP变化(比如移动网络切换基站),两次请求可能落到不同的后端Pod,对于需要本地会话状态的服务,必须在业务层做会话共享,比如把Session存到Redis里,指望四层ingress做粘滞会话,配置起来非常别扭,要么用nginx.org/affinity注解,要么用IP哈希,但都会带来负载不均的新问题。
ingress四层负载选型:主流方案对比
说实话,没有一个方案是十全十美的,关键看你的业务容忍度,行业共识认为,中小规模集群用nginx-ingress就够了,大规模高并发场景直接上MetalLB或云厂商LB。
| 方案 | 协议支持 | 性能表现 | 运维复杂度 | 适用规模 |
|---|---|---|---|---|
| nginx-ingress (四层模式) | TCP/UDP | 中上 | 低,ConfigMap维护即可 | 中小集群 |
| MetalLB + 自建LB | TCP/UDP | 高 | 中,需要熟悉BGP或ARP | 物理机/裸金属 |
| 云厂商LB (SLB/ELB) | TCP/UDP | 极高 | 极低,控制台点选 | 大规模生产 |
如何选择
- 如果你的集群跑在简米云、酷番云、AWS上,直接用云厂商的负载均衡挂到NodePort,别自己造轮子,云LB自带DDoS防护、健康检查、自动容灾,性价比远高于自建。
- 如果是裸金属机房,MetalLB是个好选择,它能把多个节点虚构成一个VIP,后端接nginx-ingress的NodePort,兼顾了性能和灵活性。
- 如果你的业务全是HTTP/HTTPS,那老老实实走七层,别为了“炫技”强行用四层,四层没有超时重试、灰度发布、熔断限流这些高级功能,真要实现这些得靠业务代码自己扛。
排障思路:ingress四层负载连不上怎么办
这是我们在工单里遇到最多的问题,按照以下顺序排查,能解决绝大多数场景。
先确认Controller真的监听了端口
kubectl get svc -n ingress-nginx kubectl exec -it <controller-pod> -- netstat -tlnp | grep 3306
如果Controller Pod里查不到监听端口,先检查启动参数是否加了--tcp-services-configmap。配置了ConfigMap但没加启动参数,是最常见的失误。
再确认ConfigMap的namespace写对了
很多人的Service在default命名空间,但ConfigMap里的值写成了kube-system/mysql-service:3306,这种低级错误,报错信息还特别隐晦,nginx启动时不会报错,只是转发失败,建议在ConfigMap的data里使用命名空间/Service名:端口的完整格式,不要省略命名空间。
最后看后端Service的Endpoint是否存在
kubectl get endpoints mysql-service
如果Endpoint列表为空,说明Service的Selector没匹配到任何Pod,或者Pod没通过就绪探针,四层ingress转发到不存在的Endpoint,TCP连接直接卡住或报RST,表现为客户端连接超时。
Q&A:关于ingress四层负载大家常问的
ingress四层负载比七层快多少?
多数情况下,四层比七层的吞吐量高30%到一倍以上,具体取决于报文大小和连接数,小包场景下差异更明显,因为七层每包都要解析,CPU成为瓶颈,但如果你只是跑一个日活几千的小网站,这点性能差异根本感知不到,选哪个都行。
哪些业务必须用ingress四层负载?
数据库、缓存、消息队列、NAS挂载这类非HTTP协议的业务,以及所有需要TCP长连接实时推送的场景,还有一个冷门场景:某些老旧系统只允许客户端访问固定IP和端口,没有域名,也没有HTTP概念,此时七层Ingress完全无用武之地。
云厂商的LB和自建的ingress四层负载怎么配合?
最常见的架构是:云LB做第一层公网接入,把流量转发给节点的NodePort,NodePort再交给nginx-ingress-controller,Controller最后转发给业务Pod,这样做的目的是利用云LB的DDoS清洗和高可用能力,同时保留ingress的灵活路由能力,代价是多一跳网络延迟,但内网开销微乎其微。
ingress四层负载是K8s流量管理拼图中不可替代的一块,它没有七层的花哨功能,但胜在简单、高效、通用,对于运维人来说,搞清楚它的工作边界和配置细节,比盲目追随新框架重要得多。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/580530.html




