容器流量重定向

需求: 对某个network下的全部容器执行特殊流量策略, 例如调整默认gateway

一种朴素的方式是在容器启动后, 服务启动前, 通过在host侧使用ip netns或在容器中使用ip route命令调整默认gateway. 但这些方法都比较重, 对容器创建过程有很高的要求, 而且需要保证容器内服务能够配合改造.

我们决定采用另一种方式, 首先通过sudo podman network create mustredirect创建一个network. 可以看到提示CNI配置文件在这里 /etc/cni/net.d/mustredirect.conflist

配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
"cniVersion": "0.4.0",
"name": "mustredirect",
"plugins": [
{
"type": "bridge",
"bridge": "cni-podman5",
"isGateway": true,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "host-local",
"routes": [
{
"dst": "0.0.0.0/0"
}
],
"ranges": [
[
{
"subnet": "10.89.4.0/24",
"gateway": "10.89.4.1"
}
]
]
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
},
{
"type": "firewall",
"backend": ""
},
{
"type": "tuning"
},
{
"type": "dnsname",
"domainName": "dns.podman",
"capabilities": {
"aliases": true
}
}
]
}

我们需要编辑这个文件, 调整 ipMasqfalse, 这样能够阻止CNI生成iptables MASQUERADE规则, 从而保证我们的流量在打到网关的时候, 源IP为容器的IP而不是节点的IP, 这样可以方便我们在网关层做流量拆分.

之所以需要手动编辑是因为podman v3.4.4目前还不支持在创建的时候指定ipMasq选项, 直接用--opt指定会报错.

接下来创建iptables规则, 因为CNI组件默认调整的是filter和nat表, 为了保证我们的流量策略优先级更高, 我们会把如下流量策略加入到raw表:

sudo iptables -t raw -A PREROUTING -s 10.89.4.0/24 -j MARK --set-mark 0x888

然后添加ip rule规则:

sudo ip rule add fwmark 0x888 lookup 0x888

然后为table 0x888添加默认网关

sudo ip route add default via <独立网关IP> table 0x888

在网关服务器上, 先给10.89.4.0/24的流量添加路由:

sudo ip route add 10.89.4.0/24 via <运行容器的NodeIP>

接下来添加流量策略:

sudo ip rule add 10.89.4.0./24 lookup table 0x888

为table 0x888添加默认网关, 此处假设要求流量必须从gre0发送给上游

sudo ip route add default gre0 ens18 table 0x888

最后, 在网关层添加iptables规则, 保证上游收到的IP不再是容器的IP. 当然这里如果是和上游一起组网, 也可以考虑不添加MASQUERADE, 而是直接用OSPF协议同步路由

sudo iptables -t nat -A POSTROUTING -o gre0 -j MASQUERADE

这样, 我们就能保证来自mustredirect网络下的容器的流量, 全部经过这个新的网关发往上游gre0对端, 且不需要单独配置回程. 所有挂在mustredirect网络下的容器不需要再单独设置即可满足需求. 而且这里完全不再需要再有一层类似ipip/gre/vxlan的封装.

注: 编辑CNI网络文件时请保证没有容器连接到这个网络, 即Podman不会调用CNI创建这个interface.

这个配置流程的流程受到了 K8S Cilium BGP 路由的启发.

参考

How Container Networking Works: Practical Explanation

CNI - bridge plugin

podman - cni/README.md

podman - cmd/podman/networks/create.go 可以看到目前还无法支持ipMasq选项