我目前正在追踪自定义桥接网络上的 docker 容器的连接问题,我可以将其减少到 Linux 网络堆栈而无需 docker 参与。
我观察到的情况如下:我有一个桥接设备,它使用转发和伪装连接到外部世界(因此桥接不包含主机的传出网络接口)。此桥接器用于由长时间运行的进程执行网络操作,该进程使用网络命名空间和 veth 设备(正是 docker 内部所做的)限制在此桥接器上。我们看到的是,每次在桥接器中添加或移除(虚拟)网络设备时,长时间运行的进程的通信都会中断。
为了重现我们看到的行为,您可以使用以下代码:
#!/bin/bash
set -e
# teardown
function cleanup {
set +e
brctl delif brtest vethtest0
ip link del vethtest0
iptables -t nat -D POSTROUTING -j MASQUERADE -s 10.12.10.0/24 -d 0.0.0.0/0
iptables -D FORWARD -i brtest -o enp0s31f6 -j ACCEPT
iptables -D FORWARD -o brtest -i enp0s31f6 -j ACCEPT
ip link delete veth0
ip link set down brtest
brctl delbr brtest
ip netns del test
}
trap cleanup EXIT
ip netns add test
brctl addbr brtest
ip addr add 10.12.10.1/24 dev brtest
ip link set up dev brtest
ip link add veth0 type veth peer name veth1
ip link set veth1 netns test
brctl addif brtest veth0
ip link set up dev veth0
ip netns exec test ip addr add 10.12.10.42/24 dev veth1
ip netns exec test ip link set up dev veth1
ip netns exec test ip route add default via 10.12.10.1 dev veth1
# change external interface name
iptables -A FORWARD -o brtest -i enp0s31f6 -j ACCEPT
iptables -A FORWARD -i brtest -o enp0s31f6 -j ACCEPT
iptables -t nat -A POSTROUTING -j MASQUERADE -s 10.12.10.0/24 -d 0.0.0.0/0
while true; do ip netns exec test python3 -c "import socket; socket.gethostbyname('example.org')" && echo success; sleep 1; done
这将设置网桥、veth 设备、网络命名空间和 iptables 规则来复制网络设置,并通过在 while 循环中定期执行(易失性)UDP DNS 请求来模拟长时间运行的过程。
启动此脚本时,所有 DNS 请求都应成功,并且您应该会success
以规律的速度看到消息。
为了模拟设备加入和离开网桥,您可以在第二个 bash 中启动以下代码:
while true
do
echo "next"
ip link add vethtest0 type veth peer name vethtest1
brctl addif brtest vethtest0
sleep 2
brctl delif brtest vethtest0
ip link del vethtest0
sleep 1
done
一旦运行,你就会发现 DNS 请求会频繁延迟,有些甚至会失败。在并行 pcap 中,你会看到有时来自 DNS 查找的 UDP 包不会被路由到外部世界,而是最终到达桥接设备,而不会离开主机系统。
有人能解释一下这里发生了什么吗?为什么在网桥上添加和删除设备会导致连接问题?如何避免这些问题?
答案1
在联系了 Linux 内核的 netdev 邮件列表后,Ido Schimmel 提供了解决这个谜团的重要信息:
网桥的 MAC 地址(示例中为“brtest”)继承自 MAC 地址“最小”的网桥端口。因此,当您生成具有随机 MAC 的 veth 设备并将其绑定到网桥时,有时也会更改网桥的 MAC 地址。而且由于网桥是默认网关,因此有时数据包会发送到错误的 MAC 地址。