入口控制器水平扩展时,连接表上限并非由单一组件决定,而是由内核Conntrack表、控制器节点规格与负载均衡模式三者共同作用,实践中通常以单节点100万条会话作为常见规划基线。
连接表上限的真实瓶颈在Linux内核而非控制器进程
水平扩展Nginx Ingress或APISIX这类入口控制器时,很多人误以为增加Pod数量就能线性提升连接承载能力,实际观察会发现,无论控制器副本怎么加,单机连接数总卡在某个阈值上不去,业内专家指出,超过95%的情况是Linux内核的Conntrack表先达到上限,控制器进程本身几乎没到瓶颈。
Conntrack表的基本工作原理
连接跟踪表(nf_conntrack)是Linux内核Netfilter框架的组成部分,负责记录每一个经过主机的TCP、UDP或ICMP会话状态,入口控制器接收的每个请求,无论是来自客户端的入站连接,还是转发到后端Pod的出站连接,都会在Conntrack表中创建条目。
该表使用哈希结构存储,默认大小由内核参数nf_conntrack_max决定,在多数主流发行版中,这个值的默认配置并不高,例如CentOS 7系列默认仅为65536条,Ubuntu 20.04及之后的版本通常为262144条,对于生产环境入口控制器而言,这个数字很容易被打满。
查看当前Conntrack使用情况的操作方法
要确认系统当前是否触及了连接表上限,可以执行以下命令序列:
# 查看当前跟踪的连接数 cat /proc/sys/net/netfilter/nf_conntrack_count # 查看连接表最大值 cat /proc/sys/net/netfilter/nf_conntrack_max # 实时监控连接数变化 watch -n 1 'cat /proc/sys/net/netfilter/nf_conntrack_count'
当nf_conntrack_count接近nf_conntrack_max时,新连接会直接丢弃,表现为入口控制器返回502或504错误,同时dmesg日志中会出现nf_conntrack: table full, dropping packet字样。
调整Conntrack表的典型参数组合
修改/etc/sysctl.conf是持久化调整的常规路径:
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_buckets = 262144
net.netfilter.nf_conntrack_tcp_timeout_established = 7200
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 60
执行sysctl -p生效后,通过sysctl -a | grep conntrack验证,需要注意的是,nf_conntrack_max的合理上限受限于系统内存,据不完全统计,每跟踪一条连接大约消耗约300字节内存,100万条连接对应约300MB,如果节点内存充裕,调整到200万甚至更高并无问题。
水平扩展节点数后连接表上限的分配策略
当入口控制器从单副本扩展到多个节点时,连接表的上限并非简单相加,需要区分两种部署形态,这直接决定了你该如何规划容量。
使用LoadBalancer类型服务时的连接分布
在云环境或裸机负载均衡器场景下,流量入口是SLB或F5等外部设备,再转发到各Ingress节点,此时连接表上限是所有节点之和,因为外部LB会将连接分散到不同的节点IP上。
假设你有3台Ingress节点,每台Conntrack上限为100万条,理论上整体上限为300万条并发连接,但实际规划时需要考虑单节点故障的冗余能力,行业中通常按N+1冗余规划,即3台节点实际承载200万并发,预留一台的容量。
使用Keepalived虚拟IP时的连接分布
不少自建Kubernetes集群采用Keepalived + HAProxy或直接Keepalived绑定Ingress Pod,此时所有流量都集中在同一台持有虚拟IP的节点上,水平扩展的节点再多,同一时间只有一台在干活。
这种模式下连接表上限等于单台节点的上限,其他节点仅作为故障转移备用,这种情况需要优先提升单机规格,而不是简单增加节点数量,此时选择配置更高内存的机型往往比增加副本更有效。
云场景中VPC内网型SLB普遍支持千万级并发,瓶颈基本不在负载均衡器本身,而集中在Ingress节点的Conntrack配置上,具体规划时建议参考云厂商的规格说明文档,多数云厂商在售卖的规格中已标明最大连接数,这是选型时的直接依据。
连接表上限与入口控制器自身配置的平衡
调整完内核参数,还需要确认控制器自身的配置没有成为短板,以Nginx Ingress为例,主要涉及工作模式与超时参数。
开启线程池模式对连接表的影响
Nginx Ingress默认启用--enable-thread-pool参数,开启后每个请求由线程池处理,避免阻塞事件循环,但这并不减少Conntrack表条目数,只是加快连接销毁速度,如果连接表已满,线程池反而会因为等待新连接而堆积队列。
一般建议保持开启,同时监控thread_pool_queue_length指标,该指标可从Prometheus的nginx_ingress_controller_nginx_process_resident_memory_bytes等相关指标中侧面观察,更直接的监控路径是查看/metrics端点的nginx_ingress_controller_connections_current项。
调整超时参数以减少连接表驻留时间
Conntrack表中大量TIME_WAIT状态的连接会长期占用条目,通过缩短相关超时值可以快速释放空间:
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 30
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 30
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 10
入口控制器的keep-alive-timeout参数也应同步调小,例如从默认的75秒调整为30秒,避免客户端复用空闲连接导致后端关联连接长期存活,调整后可用ss -s对比TIME_WAIT连接数量变化。
使用IPVS模式替代iptables模式缓解连接表压力
Kube-proxy的iptables模式在转发时依赖Conntrack表,而IPVS模式本身使用哈希表管理服务映射,不消耗Conntrack条目,切换方式:
kubectl edit configmap -n kube-system kube-proxy # 将 mode: "" 改为 mode: "ipvs" kubectl rollout restart daemonset kube-proxy -n kube-system
切换后观察连接跟踪表计数,会发现同样访问压力下nf_conntrack_count数值降低不少,这是生产环境中提升入口控制器整体连接承载能力的有效手段之一。
从实际运维角度看,连接表上限问题往往在流量突增时暴露,例如灰度发布或秒杀活动期间,大量短暂连接快速创建又销毁,内核来不及回收就会触顶,针对这类场景,可以考虑调整回收老化更激进的内核参数。
单节点连接表上限的调优操作路径
以一台8核16GB内存的通用Kubernetes节点为例,运行Nginx Ingress控制器,规划承载200万并发连接的实施步骤如下。
第一步:确认当前内核版本与默认值
uname -r sysctl -a | grep nf_conntrack_max
不同内核版本对Conntrack的性能表现有明显差异,5.x内核在多核并发下处理连接跟踪的效率通常优于4.x系列。
第二步:修改sysctl配置
编辑/etc/sysctl.d/99-ingress-tuning.conf,写入以下配置:
net.netfilter.nf_conntrack_max = 2097152
net.netfilter.nf_conntrack_buckets = 524288
net.netfilter.nf_conntrack_tcp_timeout_established = 3600
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
本地测试的完整操作路径为:先编辑文件,然后执行sysctl --system重新加载全部配置,最后用sysctl -a | grep nf_conntrack验证所有参数是否生效。
第三步:验证调整结果
free -m cat /proc/net/stat/nf_conntrack
观察insert_failed字段是否为0,该字段非零说明曾经发生过插入失败,即连接表溢出,通过压测工具如wrk或ab模拟高并发后再次检查该字段与nf_conntrack_count实时值是否超出配置上限。
手动调整时还有一个细节需要注意,nf_conntrack_buckets的值通常设置为nf_conntrack_max的1/4,这样哈希链长度既不会太长也不会浪费内存,修改nf_conntrack_buckets需要先清空现有的连接跟踪表:
echo 0 > /proc/sys/net/netfilter/nf_conntrack_max echo 524288 > /proc/sys/net/netfilter/nf_conntrack_buckets echo 2097152 > /proc/sys/net/netfilter/nf_conntrack_max
Conntrack连接表耗尽时的故障现象识别
线上出现连接表打满时,表现往往具有迷惑性,容易和其他故障混淆。
新连接间歇性失败
用户访问时有时成功有时超时,刷新页面又恢复正常,这是因为Conntrack表在满与接近满之间波动,部分新连接能插入条目,部分插入失败被丢弃,查看Ingress控制器日志,可见大量upstream timed out与connect() failed (110: Connection timed out)交替出现。
CPU使用率升高但QPS下降
Conntrack表满时,内核需要反复扫描哈希表尝试插入新条目,导致软中断CPU使用率上升,此时Ingress控制器Pod的CPU使用率并不高,但宿主机top命令中si字段数值较高,这是典型的nf_conntrack锁竞争与哈希遍历导致的异常开销。
防火墙规则失效
使用iptables或firewalld的管理员会发现,原本正常放行的端口突然无法访问,这是因为Conntrack表满后,新的连接无法建立状态记录,后续的state RELATED,ESTABLISHED规则无法匹配,数据包被默认策略丢弃。
排查效率最高的方式是执行dmesg -T | tail -50,看到nf_conntrack: table full信息即可基本确认问题方向。
多地部署场景下,不同云厂商的内存规格与内核参数默认值差异较大,同一套配置在一家可用在另一家不一定适用,建议在测试环境先行压测验证,中小规模集群中,入口控制器的连接瓶颈还可能与Service后端Pod数量较少有关,此时即使增加Ingress节点,后端Pod的转发能力仍是卡点。
常见问题解答
提升连接表上限后是否一定能支撑更多并发请求?
不完全是,Conntrack表只解决连接跟踪能力问题,实际吞吐还取决于Ingress控制器的worker进程数、后端应用的处理速度以及带宽限制,连接表扩大但后端处理不过来,只是把压力往后转移,整体可用性未必提升,同时建议同步调整net.core.somaxconn与net.ipv4.tcp_max_syn_backlog,避免连接跟踪后仍在Accept队列丢失。
入口控制器连接数打满与Pod水平自动扩展有什么关联?
如果使用的是HPA基于nginx_ingress_controller_connections_current指标进行扩容,连接表打满时指标确实会上升,导致副本数增加,但新增Pod仍分布在原有节点上,如果各节点Conntrack已满,扩容无法解决问题,需要将HPA与节点级指标结合,例如对节点nf_conntrack_count / nf_conntrack_max比率超过80%时触发节点池扩容,而非仅调整副本数。
裸机环境与云环境在连接表规划上是否存在差异?
存在差异,裸机环境使用Keepalived虚拟IP时只有一个节点承载流量,连接表上限等于单机上限,建议按2倍峰值规划单机规格,同时保留备用节点,云环境使用SLB或ALB时流量默认分发到所有节点,上限按节点数量累加计算,部分云厂商的SLB实例规格文档中明确标注了最大并发连接数,例如千万级并发连接规格,这种场景下高端规格的Ingress节点加上扩容后的Conntrack表才能匹配整体吞吐需求。
连接表上限本质上是一个可以预期和管理的资源边界,提前做好内核参数调优与容量规划,比故障发生后再排查要省力得多。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/776032.html


