В настоящее время я отслеживаю проблемы с подключением контейнеров докеров к настраиваемой сети моста, которую я мог бы сократить до сетевого стека Linux без участия докеров.
Ситуация, которую я наблюдаю, следующая: у меня есть мостовое устройство, которое подключено к внешнему миру с использованием пересылки и маскарадинга (так что мост не содержит исходящий сетевой интерфейс хоста). Этот мост используется для выполнения сетевых операций длительным процессом, который ограничен этим мостом с использованием сетевых пространств имен и 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 для репликации настройки сети и имитирует длительный процесс, периодически выполняя (изменчивые) запросы UDP DNS в цикле while.
При запуске этого скрипта все запросы 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 вы увидите, что иногда пакеты UDP из поиска DNS не маршрутизируются во внешний мир, а вместо этого попадают на мостовое устройство, не покидая хост-систему.
Может ли кто-нибудь объяснить, что здесь происходит и почему добавление и удаление устройств к мосту приводит к проблемам с подключением? Как их избежать?
После обращения к списку рассылки netdev ядра Linux Идо Шиммель предоставил важную информацию, чтобы разгадать эту загадку:
MAC-адрес моста («brtest» в вашем примере) наследуется от порта моста с «наименьшим» MAC-адресом. Таким образом, когда вы генерируете veth-устройства со случайными MAC-адресами и порабощаете их мосту, вы также иногда меняете MAC-адрес моста. И поскольку мост является шлюзом по умолчанию, иногда пакеты отправляются не на тот MAC-адрес.