本文已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已不正确。
Kubernetes 的 IPTables 链不是 API
一些 Kubernetes 组件(例如 kubelet 和 kube-proxy)在操作过程中会创建 iptables 链和规则。这些链从未打算成为任何 Kubernetes API/ABI 保证的一部分,但一些外部组件仍然使用其中的一些(特别是使用 KUBE-MARK-MASQ
来标记需要进行伪装的数据包)。
作为 v1.25 版本的一部分,SIG Network 明确声明:(除一个例外)Kubernetes 创建的 iptables 链仅供 Kubernetes 内部使用,第三方组件不应假设 Kubernetes 会创建任何特定的 iptables 链,或者这些链如果存在,将包含任何特定规则。
然后,在未来的版本中,作为 KEP-3178 的一部分,我们将开始逐步淘汰 Kubernetes 本身不再需要的某些链。在 Kubernetes 外部使用 KUBE-MARK-MASQ
、KUBE-MARK-DROP
或其他 Kubernetes 生成的 iptables 链的组件应立即开始迁移并停止使用它们。
背景
除了各种特定于服务的 iptables 链之外,kube-proxy 还创建一些通用 iptables 链,用于服务代理。过去,kubelet 也将 iptables 用于一些功能(例如为 Pod 设置 hostPort
映射),因此它也冗余地创建了一些相同的链。
然而,随着 Kubernetes 在 1.24 版本中移除 dockershim,kubelet 现在不再出于自身目的使用任何 iptables 规则;它过去使用 iptables 进行的操作现在始终是容器运行时或网络插件的责任,kubelet 没有理由创建任何 iptables 规则。
与此同时,尽管 iptables
仍然是 Linux 上默认的 kube-proxy 后端,但它不太可能永远保持默认设置,因为相关的命令行工具和内核 API 本质上已弃用,并且不再进行改进。(如果您使用 iptables API,即使通过 iptables-nft
,RHEL 9 也会记录警告。)
尽管截至 Kubernetes 1.25 iptables kube-proxy 仍然很流行,并且 kubelet 继续创建它历史上创建的 iptables 规则(尽管不再使用它们),但第三方软件不能假设核心 Kubernetes 组件将来会继续创建这些规则。
即将发生的变更
从现在开始的几个版本,kubelet 将不再在 nat
表中创建以下 iptables 链
KUBE-MARK-DROP
KUBE-MARK-MASQ
KUBE-POSTROUTING
此外,filter
表中的 KUBE-FIREWALL
链将不再具有当前与 KUBE-MARK-DROP
相关联的功能(它最终可能会完全消失)。
此更改将通过 IPTablesOwnershipCleanup
功能门逐步引入。该功能门可用,可以在 Kubernetes 1.25 中手动启用以进行测试。目前的计划是在 Kubernetes 1.27 中默认启用,但这可能会延迟到以后的版本。(不会早于 Kubernetes 1.27。)
如果您使用 Kubernetes 的 iptables 链,该怎么办
(尽管下面的讨论侧重于仍然基于 iptables 的短期修复,但您可能还应该开始考虑最终迁移到 nftables 或其他 API)。
如果您使用 KUBE-MARK-MASQ
...
如果您正在使用 KUBE-MARK-MASQ
链来导致数据包被伪装,则有两种选择:(1) 重写规则以直接使用 -j MASQUERADE
,(2) 创建您自己的替代“标记为伪装”链。
kube-proxy 使用 KUBE-MARK-MASQ
的原因是,在许多情况下,它需要在数据包上同时调用 -j DNAT
和 -j MASQUERADE
,但在 iptables 中不可能同时执行这两者;DNAT
必须从 PREROUTING
(或 OUTPUT
)链中调用(因为它可能会更改数据包将路由到的位置),而 MASQUERADE
必须从 POSTROUTING
中调用(因为它选择的伪装源 IP 取决于最终的路由决策)。
理论上,kube-proxy 可以有一组规则来匹配 PREROUTING
/OUTPUT
中的数据包并调用 -j DNAT
,然后有第二组规则来匹配 POSTROUTING
中的相同数据包并调用 -j MASQUERADE
。但为了提高效率,它只在 PREROUTING
/OUTPUT
期间匹配一次,此时它调用 -j DNAT
,然后调用 -j KUBE-MARK-MASQ
在内核数据包标记上设置一位,以提醒自己。然后,稍后在 POSTROUTING
期间,它有一个规则来匹配所有先前标记的数据包,并在它们上调用 -j MASQUERADE
。
如果您有很多需要像 kube-proxy 一样对相同的数据包应用 DNAT 和伪装的规则,那么您可能需要类似的安排。但在许多情况下,使用 KUBE-MARK-MASQ
的组件只是因为它复制了 kube-proxy 的行为,而没有理解 kube-proxy 这样做的原因。许多这些组件可以很容易地重写为仅使用单独的 DNAT 和伪装规则。(在没有发生 DNAT 的情况下,使用 KUBE-MARK-MASQ
的意义更小;只需将规则从 PREROUTING
移动到 POSTROUTING
并直接调用 -j MASQUERADE
。)
如果您使用 KUBE-MARK-DROP
...
KUBE-MARK-DROP
的基本原理与 KUBE-MARK-MASQ
的基本原理类似:kube-proxy 希望在 nat
KUBE-SERVICES
链中与其他决策一起做出数据包丢弃决策,但您只能从 filter
表中调用 -j DROP
。因此,它使用 KUBE-MARK-DROP
来标记稍后要丢弃的数据包。
通常,删除对 KUBE-MARK-DROP
的依赖的方法与删除对 KUBE-MARK-MASQ
的依赖的方法相同。在 kube-proxy 的情况下,实际上很容易用 filter
表中对 DROP
的直接调用来替换 nat
表中对 KUBE-MARK-DROP
的使用,因为 DNAT 规则和丢弃规则之间没有复杂的交互,因此丢弃规则可以简单地从 nat
移动到 filter
。
在更复杂的情况下,可能需要在 nat
和 filter
中“重新匹配”相同的数据包。
如果您使用 Kubelet 的 iptables 规则来确定 iptables-legacy
与 iptables-nft
...
从容器内部操作主机网络命名空间 iptables 规则的组件需要某种方式来确定主机是使用旧的 iptables-legacy
二进制文件还是较新的 iptables-nft
二进制文件(它们在底层与不同的内核 API 通信)。
iptables-wrappers
模块提供了一种让此类组件自动检测系统 iptables 模式的方法,但在过去,它通过假设 Kubelet 将在任何容器启动之前创建“一堆”iptables 规则来做到这一点,因此它可以猜测主机文件系统中的 iptables 二进制文件使用的模式,方法是查看哪种模式定义了更多规则。
在未来的版本中,Kubelet 将不再创建许多 iptables 规则,因此基于计算规则数量的启发式方法可能会失败。
但是,从 1.24 版本开始,Kubelet 始终在其正在使用的任何 iptables 子系统的 mangle
表中创建一个名为 KUBE-IPTABLES-HINT
的链。组件现在可以查找此特定链以了解 Kubelet(因此,大概是系统的其余部分)正在使用哪个 iptables 子系统。
(此外,自 Kubernetes 1.17 以来,kubelet 在 mangle
表中创建了一个名为 KUBE-KUBELET-CANARY
的链。虽然此链将来可能会消失,但在旧版本中它当然仍然存在,因此在任何最近的 Kubernetes 版本中,至少会存在 KUBE-IPTABLES-HINT
或 KUBE-KUBELET-CANARY
中的一个。)
iptables-wrappers
包已经使用此新的启发式方法进行了更新,因此如果您之前使用过该包,则可以使用该包的更新版本重建容器镜像。
进一步阅读
清理 iptables 链所有权并弃用旧链的项目由 KEP-3178 跟踪。