L2 模式下 MetalLB 的微调路由

L2 模式下 MetalLB 的微调路由
不久前,我面临着一项非常不寻常的任务,即为 MetalLB 设置路由。 一切都会好起来的,因为…… 通常 MetalLB 不需要任何额外的操作,但在我们的例子中,我们有一个相当大的集群,并且网络配置非常简单。

在本文中,我将告诉您如何为集群的外部网络配置基于源和基于策略的路由。

我不会详细介绍 MetalLB 的安装和配置,因为我假设您已经有一些经验。 我建议开门见山,即设置路由。 所以我们有四种情况:

情况一:无需配置时

我们来看一个简单的案例。

L2 模式下 MetalLB 的微调路由

当 MetalLB 发布的地址与您的节点地址位于同一子网时,不需要额外的路由配置。

例如,您有一个子网 192.168.1.0/24,它有一个路由器 192.168.1.1,并且您的节点接收地址: 192.168.1.10-30,那么对于 MetalLB 您可以调整范围 192.168.1.100-120 并确保它们无需任何额外配置即可工作。

这是为什么? 因为您的节点已经配置了路由:

# ip route
default via 192.168.1.1 dev eth0 onlink 
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10

同一范围内的地址将重用它们,无需任何其他操作。

情况2:需要额外定制时

L2 模式下 MetalLB 的微调路由

只要您的节点没有配置 IP 地址或没有到 MetalLB 为其发布地址的子网的路由,您就应该配置其他路由。

我会更详细地解释一下。 每当 MetalLB 输出一个地址时,它都可以与一个简单的赋值进行比较,例如:

ip addr add 10.9.8.7/32 dev lo

注意:

  • a) 地址分配有前缀 /32 也就是说,路由不会自动添加到它的子网中(它只是一个地址)
  • b) 该地址附加到任何节点接口(例如环回)。 这里值得一提的是Linux网络堆栈的特性。 无论你将地址添加到哪个接口,内核总是会处理 arp 请求并向其中任何一个发送 arp 响应,这种行为被认为是正确的,而且在 Kubernetes 这样的动态环境中得到了广泛的应用。

此行为可以自定义,例如通过启用严格的 arp:

echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce

在这种情况下,仅当接口明确包含特定 IP 地址时才会发送 arp 响应。 如果您计划使用 MetalLB 并且您的 kube-proxy 在 IPVS 模式下运行,则需要此设置。

但是,MetalLB 并不使用内核来处理 arp 请求,而是在用户空间中自行处理,因此该选项不会影响 MetalLB 的运行。

让我们回到我们的任务。 如果您的节点上不存在发布地址的路由,请提前将其添加到所有节点中:

ip route add 10.9.8.0/24 dev eth1

案例 3:当您需要基于源的路由时

当您通过单独的网关(而不是默认配置的网关)接收数据包时,您将需要配置基于源的路由,因此响应数据包也应该通过同一网关。

例如,您有相同的子网 192.168.1.0/24 专用于您的节点,但您想使用 MetalLB 发布外部地址。 假设您有来自子网的多个地址 1.2.3.0/24 位于 VLAN 100 中,并且您希望使用它们来外部访问 Kubernetes 服务。

L2 模式下 MetalLB 的微调路由

联系时 1.2.3.4 您将从不同的子网发出请求 1.2.3.0/24 并等待答复。 当前是 MetalLB 发行地址的主节点 1.2.3.4,将从路由器接收数据包 1.2.3.1,但他的答案必然走同样的路线,通过 1.2.3.1.

由于我们的节点已经配置了默认网关 192.168.1.1,那么默认情况下响应将发送给他,而不是发送给他 1.2.3.1,通过它我们收到了包裹。

遇到这种情况该如何应对呢?

在这种情况下,您需要准备所有节点,以便它们准备好为外部地址提供服务,而无需进行额外的配置。 即对于上面的例子,需要提前在节点上创建VLAN接口:

ip link add link eth0 name eth0.100 type vlan id 100
ip link set eth0.100 up

然后添加路由:

ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100

请注意,我们将路由添加到单独的路由表中 100 它将仅包含通过网关发送响应数据包所需的两条路由 1.2.3.1,位于接口后面 eth0.100.

现在我们需要添加一个简单的规则:

ip rule add from 1.2.3.0/24 lookup 100

其中明确表示:如果数据包的源地址位于 1.2.3.0/24,那么就需要使用路由表 100。 我们已经在其中描述了送他经过的路线 1.2.3.1

案例 4:当您需要基于策略的路由时

网络拓扑与前面的示例相同,但假设您还希望能够访问外部池地址 1.2.3.0/24 从您的 pod 中:

L2 模式下 MetalLB 的微调路由

特殊之处在于,当访问任何地址时 1.2.3.0/24,响应数据包到达节点并且源地址在范围内 1.2.3.0/24 会乖乖的送到 eth0.100,但我们希望 Kubernetes 将其重定向到我们的第一个 pod,该 pod 生成了原始请求。

解决这个问题被证明是困难的,但是由于基于策略的路由,它变得可能:

为了更好地理解该过程,下面是 netfilter 框图:
L2 模式下 MetalLB 的微调路由

首先,像前面的示例一样,让我们​​创建一个额外的路由表:

ip route add 1.2.3.0/24 dev eth0.100 table 100
ip route add default via 1.2.3.1 table 100

现在让我们向 iptables 添加一些规则:

iptables -t mangle -A PREROUTING -i eth0.100 -j CONNMARK --set-mark 0x100
iptables -t mangle -A PREROUTING  -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -m mark ! --mark 0 -j RETURN
iptables -t mangle -A POSTROUTING -j CONNMARK --save-mark

这些规则将标记到接口的传入连接 eth0.100,用标签标记所有数据包 0x100,同一连接内的响应也将被标记为相同的标签。

现在我们可以添加路由规则:

ip rule add from 1.2.3.0/24 fwmark 0x100 lookup 100

即所有带有源地址的数据包 1.2.3.0/24 和标签 0x100 必须使用表进行路由 100.

因此,在另一个接口上收到的其他数据包不受此规则的约束,这将允许使用标准 Kubernetes 工具对它们进行路由。

还有一件事,在Linux中有一个所谓的反向路径过滤器,它破坏了整个树莓派;它执行一个简单的检查:对于所有传入的数据包,它用发送者地址更改数据包的源地址,并检查是否数据包可以通过接收它的同一接口离开,如果不是,它将过滤掉它。

问题是在我们的例子中它无法正常工作,但我们可以禁用它:

echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth0.100/rp_filter

请注意,第一个命令控制 rp_filter 的全局行为;如果未禁用它,第二个命令将不起作用。 但是,其余接口将保留启用 rp_filter 的状态。

为了不完全限制过滤器的操作,我们可以使用netfilter的rp_filter实现。 使用rpfilter作为iptables模块,可以配置相当灵活的规则,例如:

iptables -t raw -A PREROUTING -i eth0.100 -d 1.2.3.0/24 -j RETURN
iptables -t raw -A PREROUTING -i eth0.100 -m rpfilter --invert -j DROP

在接口上启用rp_filter eth0.100 对于所有地址,除了 1.2.3.0/24.

来源: habr.com

添加评论