
Я настроил две микровиртуальные машины с помощью Firecracker на моем хосте Ubuntu 18.04 LTS и подключил устройство tun/tap к каждой из них. Теперь я пытаюсь установить задержку между двумя виртуальными машинами (скажем, 100 мс), используя tc
так, чтобы получить RTT между ними в 200 мс, но, похоже, я не могу заставить это работать должным образом.
В идеале я бы хотел указать оба устройства напрямую, tc
но, похоже, для этого мне придется указать их сети.
Вот как устроены краны (в соответствии сДокументация по фейерверкам), по одному для каждой машины A и B:
# create a tap device
sudo ip tuntap add tapA mode tap
sudo ip tuntap add tapB mode tap
# set up tap device ip address
sudo ip addr add 10.0.0.29/30 dev tapA
sudo ip addr add 10.0.0.33/30 dev tapB
# set up tap device
sudo ip link set tapA up
sudo ip link set tapB up
# enable forwarding
sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
# add iptables config
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i tapA -o eth0 -j ACCEPT
sudo iptables -A FORWARD -i tapB -o eth0 -j ACCEPT
Как вы видите, я даю каждой машине небольшую выделенную сеть ( 10.0.0.28/30
для A и 10.0.0.32/30
для B) и указываю IP-адреса, которые получат ответвители на стороне хоста. Я загружаю машины с адресами 10.0.0.30/30
для A и 10.0.0.34/30
для B (конфигурация устанавливается при загрузке) и могу успешно пинговать их друг с друга и с хоста.
В результате моих исследований я нашел tc
команды, которые, как я ожидаю, будут работать:
# create a qdisc for tapA
tc qdisc add dev tapA root handle 1: htb default 1
tc class add dev tapA parent 1: classid 1:1 htb rate 10.0Gbit
tc class add dev tapA parent 1: classid 1:2 htb rate 10.0Gbit ceil 10.0Gbit
# add a delay of 100ms
tc qdisc add dev tapA parent 1:2 handle 2: netem delay 100.0ms
# only apply that delay when the packet comes from A's network and goes to B's network
tc filter add dev tapA protocol ip parent 1: prio 5 u32 match ip dst 10.0.0.32/30 match ip src 10.0.0.28/30 flowid 1:2
# do the same for tapB
tc qdisc add dev tapB root handle 1: htb default 1
tc class add dev tapB parent 1: classid 1:1 htb rate 10.0Gbit
tc class add dev tapB parent 1: classid 1:2 htb rate 10.0Gbit ceil 10.0Gbit
tc filter qdisc add dev tapB parent 1:2 handle 2: netem delay 100.0ms
# but this time going from B's network to A's network
tc filter add dev tapB protocol ip parent 1: prio 5 u32 match ip dst 10.0.0.28/30 match ip src 10.0.0.32/30 flowid 1:2
Я ожидал, что это добавит задержку для всех исходящих пакетов от A до B и наоборот, но ничего не происходит. Как ни странно, установка этих фильтров работает:
# matching destination of A on A's tap
# this should match incoming packets from B to A on tapA
tc filter add dev tapA protocol ip parent 1: prio 5 u32 match ip dst 10.0.0.28/30 match ip src 10.0.0.32/30 flowid 1:2
# and vice versa for tapB
tc filter add dev tapB protocol ip parent 1: prio 5 u32 match ip dst 10.0.0.32/30 match ip src 10.0.0.28/30 flowid 1:2
Если я правильно понимаю, это означает, что tc
проверяет входящие пакеты и применяет правильный фильтр. Но это не совсем то, чего я ожидал, я бы предпочел, чтобы исходящие пакеты были отфильтрованы и задержаны. Или я что-то упускаю?
решение1
Я провел еще несколько исследований и, кажется, начинаю понимать, что здесь происходит. Если кто-то уверен в деталях, пожалуйста, оставьте ответ ниже, но для тех, кто может столкнуться с этим, я также оставлю свое объяснение здесь.
Я думал, что tap выглядит одинаково для microVM и хоста, но я не думаю, что это совсем верно. Как вы можете видеть на рисунке (показывающем шаги простой команды ping
), когда я отправляю ping с машины A на машину B, пакет исходит с точки зрения A, но входит с точки зрения хоста, поскольку tapA
устройство хоста подключено к eth0
устройству в A. Оба устройства вместе фактически образуют tun/tap
. Следовательно, пакет также исходит на `tapB`` и (это имеет смысл) входит на B.
Поскольку tc
я могу управлять только исходящими пакетами, но не входящими (по крайней мере, если вы не перенаправляете их через какой-либо другой виртуальный интерфейс), я могу применить задержку A->B только к tapB
(когда пакет исходит в B) и задержку B->A к tapA
.
На данный момент я придерживаюсь этого решения, пока не появится что-то лучшее.