ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

我搞懂了 iptables、IPVS 和 nftables:它们到底在 Kubernetes 里干什么

我搞懂了 iptables、IPVS 和 nftables:它们到底在 Kubernetes 里干什么 内容由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 PodPod 110.244.1.10:80 Pod 210.244.2.11:80 Pod 310.244.2.12:80问题很明显Pod 的 IP 会随着重建而变化客户端不可能直接依赖这些 IP。于是 Kubernetes 创建了一个 ServiceService10.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修改源地址或目标地址端口转发数据包标记连接跟踪conntrackKubernetes 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 IP10.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 ServerLVS把一个虚拟服务地址后面的流量分发给多台真实服务器客户端 │ ▼ 虚拟服务 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 多个相同后端它还有一大堆行为ClusterIPNodePortLoadBalancerExternalIPexternalTrafficPolicyinternalTrafficPolicysessionAffinity健康检查端口本地客户端和远程客户端的差异化行为本地 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 Proxieshttps://kubernetes.io/docs/reference/networking/virtual-ips/Netfilter 项目nftableshttps://www.netfilter.org/projects/nftables/index.htmlUbuntu 文档防火墙与 iptables/nftables 相关说明https://documentation.ubuntu.com/server/how-to/networking/注本文记录的是我个人的理解与学习过程具体的行为和默认值请以官方文档和你所用 Kubernetes 版本的实际配置为准。
返回列表