内容由AI生成,我觉得可以分享给像我一样的初学者
起因:我被三份教程搞晕了
最近在学 Kubernetes 的网络部分,Service 那一章我反复看了三遍,结果越看越糊涂。
第一份教程说:kube-proxy 默认用 iptables 模式,会生成KUBE-SERVICES、KUBE-SVC-XXXX这一堆链。
第二份教程说:iptables 模式在 Service 多了以后性能很差,推荐用 IPVS,还能选rr、wrr、lc这些调度算法,听起来高级得多。
第三份教程又说:IPVS 模式已经被弃用了,现在是 nftables 的时代。
我当时的第一反应是——这不互相矛盾吗?前脚说 IPVS 比 iptables 好,后脚 IPVS 就没了?那我到底该学哪个、用哪个?
更让我头大的是,这几份教程里 CentOS 的命令和 Ubuntu 的还不一样(yum和apt、firewalld和ufw),我得一边看一边转换。所以我干脆花了一整个晚上,把这三个东西从头理了一遍,下面就是我理清楚之后的理解。
一、先别急着比较,它们根本不是同一类东西
我一开始最大的误区,就是把 iptables、IPVS、nftables 当成"三个竞品",想着"哪个好就用哪个"。
这个思路是错的。
它们确实都和"数据包怎么走"有关,但所处的层次不一样:
- iptables / nftables:是配置 Linux 内核 Netfilter 框架的工具(前端),管的是过滤、NAT、包处理这一整套通用能力。
- IPVS:是内核里一个专门做四层负载均衡的子系统,它天生就是"一个虚拟地址 + 一组后端"这个模型。
把它们放在一起比,有点像拿"一套通用工具箱"和"一台专用分流机"比谁更好——取决于你要干什么活。
不过它们确实会在同一个地方碰面:Kubernetes 的 Service。所以我就从 Service 讲起。
二、Kubernetes Service 到底要解决什么问题
假设我有三个 Nginx Pod:
Pod 1:10.244.1.10:80 Pod 2:10.244.2.11:80 Pod 3:10.244.2.12:80问题很明显:Pod 的 IP 会随着重建而变化,客户端不可能直接依赖这些 IP。
于是 Kubernetes 创建了一个 Service:
Service:10.96.0.100:80这里有个我一开始没意识到的关键点:10.96.0.100通常不是任何一台机器网卡上的真实 IP。它更像是一个"由网络规则虚拟出来的地址"——没有任何进程真的监听它,它只是规则里的一条匹配条件。
当我访问10.96.0.100:80时,节点需要从三个 Pod 里挑一个,把数据包的目标地址改写成比如10.244.2.11:80。
这个"挑一个 + 改地址"的动作,就是由每个节点上的 kube-proxy 负责配置的规则来完成的。
而且这里有个很重要的认知转变:
kube-proxy 通常不是自己接收并转发每一个数据包。它是把规则写进 Linux 内核,然后让内核直接转发数据。
这也是为什么我在节点上看ps aux | grep kube-proxy,它的 CPU 占用平时并不高——它是个"配置员",不是"搬运工"。真正干活的是内核。
整个数据流是这样的:
Service 或 EndpointSlice 发生变化 │ ▼ kube-proxy 发现变化 │ ▼ 配置 iptables / IPVS / nftables │ ▼ Linux 内核按照规则转发数据包想清楚这一点之后,“三种 kube-proxy 模式"就不再神秘了——它们的目标完全一样,都是把访问 Service 的流量转发到某个后端 Pod,区别只是"用什么机制来表达和实现这套转发规则”。
三、地基:Netfilter
要理解 iptables 和 nftables,必须先认识Netfilter。
Netfilter 是 Linux 内核里的数据包处理框架。数据包进入、经过、或者离开 Linux 的时候,会经过几个固定的处理位置(hook):
本机进程 ▲ │ INPUT │ 网卡 ──► PREROUTING ──┼──► FORWARD ──► POSTROUTING ──► 网卡 │ │ OUTPUT ▼ 本机进程基于这些 hook,内核能做的事情包括:
- 防火墙过滤:允许或拒绝数据包;
- NAT:修改源地址或目标地址;
- 端口转发;
- 数据包标记;
- 连接跟踪(conntrack);
- Kubernetes Service 流量转发。
关键结论:
- iptables 和 nftables 都是用来配置 Netfilter 规则的机制(两个不同时代的前端);
- IPVS 是内核里的四层负载均衡机制,它做转发时会和 Netfilter、连接跟踪这些组件配合。
所以严格来说,iptables 和 nftables 是"同一层的两代工具",IPVS 是"另一个方向的东西"。
四、iptables
它的本质
iptables 是传统的 Linux 防火墙和 NAT 规则管理体系。最典型的用法:
sudoiptables-AINPUT-ptcp--dport80-jACCEPT翻译成人话就是:
进入本机的数据包 如果是 TCP 并且目标端口是 80 那么允许通过但 iptables 不只是防火墙,它还能做 NAT。比如把访问 Service IP 的流量改写到 Pod IP:
10.96.0.100:80 │ DNAT ▼ 10.244.1.10:80这里的DNAT 就是修改数据包的目标地址,这正是 Kubernetes Service 转发的基础。
kube-proxy 的 iptables 模式
在 iptables 模式下,kube-proxy 根据 Service 和 EndpointSlice 的信息生成一大堆规则:
Service 10.96.0.100:80 │ ├──► Pod 10.244.1.10:80 ├──► Pod 10.244.2.11:80 └──► Pod 10.244.2.12:80默认情况下,iptables 模式是按概率随机选择后端的。
我第一次在节点上iptables-save的时候,看到满屏的KUBE-*链确实有点懵。常见的有:
KUBE-SERVICES KUBE-NODEPORTS KUBE-SVC-XXXX KUBE-SEP-XXXX KUBE-POSTROUTING想自己看一眼的话:
sudoiptables-save|grepKUBE它真正的问题在哪
iptables 传统规则的结构是线性规则链。可以想象成:
规则 1:是不是 Service A? 规则 2:是不是 Service B? 规则 3:是不是 Service C? …… 规则 10000:是不是 Service Z?当集群里的 Service 和 Pod 规模上去以后:
- 规则数量暴涨;
- 匹配可能要走过更多规则;
- kube-proxy 更新大量规则很慢;
- 历史上规则同步的成本相当高。
这就是早期 Kubernetes 引入 IPVS 模式的主要原因。
不过这里我要纠正一个我自己的偏见:iptables 模式后来被优化了很多,现在的 iptables 模式已经不能简单等同于"性能非常差"。Kubernetes 官方也明确说过,它的性能比 IPVS 刚引入那会儿改善了很多。
五、IPVS
它的本质
IPVS 全称IP Virtual Server。
它是 Linux 内核里的第四层负载均衡器,主要根据这五元组来处理连接:
源 IP 目标 IP 源端口 目标端口 TCP/UDP 协议它的传统用途是实现Linux Virtual Server(LVS):把一个虚拟服务地址后面的流量分发给多台真实服务器:
客户端 │ ▼ 虚拟服务 192.168.1.100:80 │ ├──► Server A ├──► Server B └──► Server C我第一次看到这个图的时候就想:这不就是 Kubernetes Service 吗?
客户端 │ ▼ ClusterIP 10.96.0.100:80 │ ├──► Pod A ├──► Pod B └──► Pod C是的,本质上一模一样。所以 Kubernetes 当初的想法很自然:既然 IPVS 天生就是四层负载均衡器,那用它实现 Service 应该比堆一大堆 iptables 规则更合适。
它的调度算法
IPVS 提供多种后端选择算法,这也是它吸引我的一点:
| 算法 | 含义 |
|---|---|
rr | 轮询 |
wrr | 加权轮询 |
lc | 最少连接 |
wlc | 加权最少连接 |
sh | 源地址哈希 |
dh | 目标地址哈希 |
sed | 最短预期延迟 |
配置大概长这样:
mode:ipvsipvs:scheduler:rr为什么以前都说"IPVS 比 iptables 好"
主要就两个原因。
原因一:查找结构更高效。
IPVS 使用适合服务查找的数据结构;传统 iptables 则可能需要遍历较长的规则链。
iptables:从一长串规则中逐条匹配 IPVS: 根据虚拟 IP、端口等信息直接查找服务所以在拥有大量 Service 和 Endpoint 的集群里,IPVS 曾经通常具备:
- 更好的规则查找性能;
- 更高的连接转发能力;
- 更快的规则同步;
- 更适合海量 Service 的数据结构。
原因二:IPVS 天生就是负载均衡器。
iptables 主要是通用的包过滤和 NAT 系统;IPVS 本来就是四层负载均衡器,所以它天然支持更多调度算法。
所以——那些旧教程说"IPVS 比 iptables 好",在当时那个语境下是成立的,它指的是:
在大型 Kubernetes 集群中,IPVS 相对于当时的 iptables kube-proxy,通常有更好的规模化性能和规则同步性能。
但这句话不等于"任何规模的集群用 IPVS 都会明显更快"。
像我自己那种三台机器、几十个 Service 的学习集群:
iptables 与 IPVS 的实际性能差异通常很难感知六、那 IPVS 为什么又被弃用了?
这是我最想搞清楚的一点。先说一个必须精确区分的结论:
被弃用的是 Kubernetes kube-proxy 的 IPVS 模式,不是 Linux 内核里的整个 IPVS 功能。
Linux 的 IPVS 本身仍然可以正常用于其他四层负载均衡场景,它没坏。
Kubernetes 弃用 IPVS 模式,不是因为 IPVS 不会做负载均衡,而是因为:
IPVS 的模型无法自然、完整地表达 Kubernetes Service 的全部语义。
下面是我理解的几条原因。
原因一:IPVS 不能独立实现全部 Service 功能
Kubernetes Service 并不只是"一个 VIP + 多个相同后端",它还有一大堆行为:
- ClusterIP;
- NodePort;
- LoadBalancer;
- ExternalIP;
externalTrafficPolicy;internalTrafficPolicy;sessionAffinity;- 健康检查端口;
- 本地客户端和远程客户端的差异化行为;
- 本地 Endpoint 优先,或只允许本地 Endpoint;
- 源地址保留;
- SNAT / MASQUERADE;
- 不同流量来源对应不同 Endpoint 集合。
而 IPVS 的核心模型非常简洁:
虚拟服务 └── 一组后端服务器这两种模型并不完全匹配。
最终的结果是:
kube-proxy IPVS 模式 ├── 使用 IPVS 实现负载均衡 └── 仍然使用 iptables 补充剩余行为所以我发现了一个有点讽刺的事实:IPVS 模式其实并没有真正摆脱 iptables。Kubernetes 官方也明确指出,IPVS API 无法单独实现 Kubernetes Service,因此该模式底层仍然需要使用 iptables。
原因二:某些 Service 边缘场景很难正确实现
比如某个 Service 需要根据请求来自本节点还是其他节点,选择不同的 Endpoint 集合:
本节点客户端访问 └── 只能选择本节点 Pod 远程客户端访问 └── 可以选择另一组 Pod但 IPVS 通常把一个虚拟服务对应到固定的后端集合。为了实现 Kubernetes 的这些语义,kube-proxy 只能绕着 IPVS 再加额外规则和特殊处理。
官方的结论大意是:IPVS 模式虽然达成了提高性能的目标,但IPVS API 与 Kubernetes Service API 并不匹配,部分边缘情况一直很难完整、正确地实现。
原因三:需要维护额外的虚拟接口
IPVS 模式通常会创建一个虚拟接口:
kube-ipvs0并且把每一个 Service 的 ClusterIP 都绑定到这个接口上。查看方式:
ipaddr show kube-ipvs0Service 一多,这个接口上就挂着大量 IP 地址。这种架构对 kube-proxy 来说挺别扭的,也增加了实现和维护的复杂度。
原因四:复杂调度算法在 Kubernetes 里收益有限
这点是我之前完全没想到的。"最少连接"听起来当然比随机或轮询聪明,但问题是——每个节点上都有自己独立的 kube-proxy 和 IPVS 状态:
Node 1 的 IPVS:只看到经过 Node 1 的连接状态 Node 2 的 IPVS:只看到经过 Node 2 的连接状态 Node 3 的 IPVS:只看到经过 Node 3 的连接状态各节点的调度器不共享完整的全局连接状态。所以一个节点认为 Pod A 连接最少,不代表整个集群里 Pod A 的连接最少。
也就是说,IPVS 那些漂亮的调度算法,在 Kubernetes 这个分布式场景下并没有发挥出理论上的全部优势。这大概是我这次学到的最"反直觉"的一课。
原因五:nftables 出现了
过去的选择大致是:
iptables:兼容性好,但超大规模性能较差 IPVS: 性能较好,但 Service 语义不完全匹配现在多了第三个:
nftables:保持通用包处理能力,同时提高更新和查找效率nftables 有原生集合和映射,支持更高效的增量更新,既适合表达 Kubernetes Service 规则,又有不错的性能。
于是 IPVS 模式的独特优势就被挤压掉了:
IPVS 的性能优势 → nftables 也能提供 IPVS 的模型限制 → nftables 没有同样的问题 IPVS 依赖 iptables → 架构反而更复杂我的小结:IPVS 不是"不够快"才被淘汰的,而是"模型不对 + 优势被别人追平 + 架构更绕"。
七、nftables
它的本质
nftables 是 iptables 体系的现代继任者。
它和 iptables 一样,可以实现:
- 数据包过滤;
- NAT;
- 端口转发;
- 数据包修改;
- 连接跟踪;
- IPv4 和 IPv6 规则;
- Kubernetes Service 转发。
对应的用户命令是nft。查看全部规则:
sudonft list rulesetNetfilter 项目把 nftables 定义为 iptables、ip6tables、arptables 和 ebtables 的替代方案。它仍然复用 Linux 的 Netfilter hook、连接跟踪、NAT 和日志等基础设施——也就是说,地基还是那个 Netfilter,只是地上盖的房子换了。
相比 iptables 的几个优点
1. 原生集合与映射
iptables 传统写法可能是:
如果目标是 Service A,跳转到规则 A 如果目标是 Service B,跳转到规则 B 如果目标是 Service C,跳转到规则 C ……nftables 可以用 map 直接把 Service 地址映射到处理结果:
Service A → Endpoint A Service B → Endpoint B Service C → Endpoint C概念上更接近根据键直接查表,而不是遍历一大堆规则。
2. 增量更新更高效
假设集群里有 10,000 个 Service,现在只有一个 Pod 变了:
iptables: 可能仍需要提交与整个大规则集规模相关的更新 nftables: 主要提交本次发生变化的对象Kubernetes 官方说明,nftables API 允许 kube-proxy 更有效地执行增量更新;更新量主要与本次发生变化的 Service 和 Endpoint 数量有关,而不是始终与全部规则数量相关。这一点在超大规模集群里是实打实的差别。
3. 更适合多组件共存
nftables 允许不同组件创建自己的 table,减少所有组件争抢同一个全局规则修改路径的问题。
4. IPv4、IPv6 规则统一
iptables 时代通常是这样的分工:
iptables 管理 IPv4 ip6tables 管理 IPv6 ebtables 管理二层桥接 arptables 管理 ARPnftables 用统一的nft工具和规则模型管理这些类型,并且可以通过inetfamily同时处理 IPv4 和 IPv6。
八、一个更好记的类比
理清之后,我给自己编了个"餐厅前台分配座位"的类比,之后就没再忘过。
iptables = 一张从上到下的纸质规则表
如果是 A 客户,安排到 1 号桌 如果是 B 客户,安排到 2 号桌 如果是 C 客户,安排到 3 号桌 ……- 优点:历史悠久、兼容性好、非常成熟;
- 缺点:规则特别多的时候,管理和更新效率较低。
IPVS = 专门的叫号分流系统
后端桌位:1、2、3 调度算法:轮询 下一个客户:安排到 1 号桌- 优点:专门做四层负载均衡、查找和调度效率高、有多种调度算法;
- 缺点:Kubernetes Service 不只是简单的后端负载均衡;很多特殊条件还得靠 iptables 补;模型和 Kubernetes Service 并不完全一致。
nftables = 可编程的现代分配系统
普通客户 → 可用桌位集合 会员客户 → 会员桌位集合 特殊客户 → 指定桌位集合 按不同条件查询映射表- 优点:能表达复杂规则、原生支持集合和映射、更新效率高、兼顾通用性和规模性能、更贴合 Kubernetes Service 的规则模型。
九、Ubuntu 上那个特别容易混淆的坑
这一节是我这次折腾里最容易误解的地方,专门记下来。
在现代 Ubuntu 上运行:
sudoiptables-L不一定表示内核正在使用旧式 iptables 后端。
Ubuntu 从 20.10 开始,iptables这个命令默认通常是iptables-nft。也就是说:
你输入的是 iptables 语法 │ ▼ 兼容程序 iptables-nft │ ▼ 底层实际写入 nftables可以这样检查:
sudoupdate-alternatives--displayiptables iptables--version如果看到:
iptables v1.8.x (nf_tables)说明iptables命令用的是nftables 兼容后端。
如果看到:
iptables v1.8.x (legacy)说明用的是传统 iptables 后端。
Ubuntu 官方文档说明,iptables-nft后端从 Ubuntu 20.10 起成为默认选择。
但这不等于 kube-proxy 用了 nftables 模式
这是我当时最大的混淆点,也是最重要的区别:
iptables 命令使用 nft 后端和
kube-proxy:mode:nftables完全不是一回事。
前者是:
kube-proxy 使用 iptables 的规则模型 iptables-nft 把规则转换到底层 nftables API后者是:
kube-proxy 直接使用原生 nftables 规则模型和 API只有 kube-proxy 明确配置为mode: nftables,才是 Kubernetes 所说的原生 nftables kube-proxy 模式。
换句话说:我在 Ubuntu 上敲iptables看到的东西底层可能是 nftables,但这并不代表我的集群跑在 nftables 模式下。看命令的版本号,不如直接看 kube-proxy 的配置。
十、三者对比表
最后放一张我自己整理的对比表,方便以后回查:
| 项目 | iptables 模式 | IPVS 模式 | nftables 模式 |
|---|---|---|---|
| 主要定位 | 防火墙、NAT、包处理 | 四层负载均衡 | 现代防火墙、NAT、包处理 |
| kube-proxy 如何使用 | 创建 iptables 规则 | 创建 IPVS 服务并配合 iptables | 创建原生 nftables 规则 |
| 后端选择 | 默认概率随机 | rr、wrr、sh等 | 默认随机,可通过映射高效选择 |
| 大规模查找 | 传统上相对较慢 | 较快 | 较快 |
| 规则增量更新 | 受 iptables API 限制 | 较好 | 最适合增量更新 |
| Service 语义适配 | 完整、成熟 | 存在模型不匹配 | 较完整 |
| 兼容性 | 最广 | 旧教程常见 | 要求较新的系统与内核 |
| Kubernetes 方向 | 继续长期支持 | 正在弃用 | 推荐的新方向 |
写在最后
回到我最初的困惑——“为什么教程一会儿说 IPVS 好,一会儿又说 IPVS 被弃用了?”
现在我的答案是:这两句话都对,只是说的不是同一件事。
- 说 IPVS 好,是在性能维度上比:面对海量 Service,它比当年的 iptables 模式更省、更快;
- 说 IPVS 被弃用,是在模型维度上比:IPVS 的"一个 VIP + 一组固定后端"根本装不下 Kubernetes Service 的全部语义。
而 nftables 之所以成为新的方向,恰恰是因为它在两个维度上同时成立——既有现代的高效查找和增量更新,又能用集合、映射自然地表达 Kubernetes 那套复杂规则,还不用像 IPVS 那样回头再拉 iptables 来打补丁。
至于我自己那几台机器的小集群?说实话,三种模式的性能差异我根本感知不到。但搞清楚它们各自解决什么问题、又各自卡在哪里,比记住"用哪个更快"有用得多——因为下一次再遇到类似的技术取舍时,我知道该问的是什么问题了:这个工具的模型,和我要表达的东西,到底匹不匹配?
参考资料
- Kubernetes 官方文档:Virtual IPs and Service Proxies
https://kubernetes.io/docs/reference/networking/virtual-ips/ - Netfilter 项目:nftables
https://www.netfilter.org/projects/nftables/index.html - Ubuntu 文档:防火墙与 iptables/nftables 相关说明
https://documentation.ubuntu.com/server/how-to/networking/
注:本文记录的是我个人的理解与学习过程,具体的行为和默认值请以官方文档和你所用 Kubernetes 版本的实际配置为准。