Résoudre la fragmentation du réseau avec MTU

Lors de la mise en œuvre des charges de travail OpenStack, un problème courant est la fragmentation sur l’ensemble du réseau, entraînant des problèmes de performances imprévus. La fragmentation est normalement difficile à résoudre car les réseaux peuvent devenir complexes, de sorte que le chemin des paquets peut être difficile à tracer ou à prévoir.
OpenStack lance la configuration de la carte d’interface réseau (NIC) lors de la configuration initiale du cluster ou lorsque de nouveaux nœuds sont ajoutés. La configuration de l’unité de transfert de message (MTU) est également générée à ce stade. La modification de la configuration après le déploiement du cluster n’est pas recommandée. Normalement, l’intégrateur système s’attend à ce que le chemin de bout en bout soit correctement configuré avant de déployer et de configurer le réseau pour la pile afin d’éviter les changements constants de MTU uniquement pour les tests.
Les réseaux de neutrons sont créés après le déploiement de l’OSP. Cela permet aux administrateurs de créer des réseaux de 1 500 MTU pour les instances. Cependant, le nœud de calcul lui-même est toujours défini sur le MTU, de sorte qu’une fragmentation peut toujours se produire. Dans les charges de travail de télécommunications, par exemple, la valeur MTU la plus courante pour toutes les instances est 9 000. Il est donc facile de provoquer par inadvertance une fragmentation après la création de réseaux et d’instances.
Cadres géants
Voici un exemple d’instance (déployée dans OSP 16.1.5) configurée avec des trames jumbo (8996), mais vous pouvez voir que le chemin réseau n’a pas également de trames jumbo configurées. Cela provoque une fragmentation car les paquets système utilisent 8996 comme MTU.
$ ping 10.169.252.1 -M do -s 8968
PING 10.169.252.1 (10.169.252.1) 8968(8996) bytes of data.--- 10.169.252.1 ping statistics ---
7 packets transmitted, 0 received, 100% packet loss, time 5999ms
Cela montre 100 % de perte de paquets lorsqu’aucune fragmentation n’est autorisée. La sortie identifie efficacement le problème et révèle un problème avec le MTU dans le chemin du réseau. Si vous autorisez la fragmentation, vous pouvez voir qu’il y a un ping réussi.
$ ping 10.169.252.1 -M dont -s 8968
PING 10.169.252.1 (10.169.252.1) 8968(8996) bytes of data.
8976 bytes from 10.169.252.1: icmp_seq=1 ttl=255 time=3.66 ms
8976 bytes from 10.169.252.1: icmp_seq=2 ttl=255 time=2.94 ms
8976 bytes from 10.169.252.1: icmp_seq=3 ttl=255 time=2.88 ms
8976 bytes from 10.169.252.1: icmp_seq=4 ttl=255 time=2.56 ms
8976 bytes from 10.169.252.1: icmp_seq=5 ttl=255 time=2.91 ms--- 10.169.252.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 2.561/2.992/3.663/0.368 m
Une fois le problème confirmé, vous devrez peut-être attendre que l’équipe réseau résolve le problème. En attendant, la fragmentation existe et impacte votre système. Vous ne devriez pas mettre à jour la pile pour vérifier si le problème a été résolu, donc dans cet article, je partage un moyen sûr de réduire le MTU de bout en bout à l’intérieur du nœud de calcul.
Réglage du MTU
Étape 1 : Identifiez l’hyperviseur sur lequel votre instance s’exécute
Tout d’abord, vous devez obtenir des informations sur votre instance. Faites cela depuis l’Overcloud en utilisant le openstack
commander:
(overcloud)[director]$ openstack server \
show 2795221e-f0f7-4518-a5c5-85977357eeec \
-f json
{
"OS-DCF:diskConfig": "MANUAL",
"OS-EXT-AZ:availability_zone": "srvrhpb510-compute-2",
"OS-EXT-SRV-ATTR:host": "srvrhpb510-compute-2.localdomain",
"OS-EXT-SRV-ATTR:hostname": "server-2",
"OS-EXT-SRV-ATTR:hypervisor_hostname": "srvrhpb510-compute-2.localdomain",
"OS-EXT-SRV-ATTR:instance_name": "instance-00000248",
"OS-EXT-SRV-ATTR:kernel_id": "",
"OS-EXT-SRV-ATTR:launch_index": 0,
"OS-EXT-SRV-ATTR:ramdisk_id": "",
"OS-EXT-SRV-ATTR:reservation_id": "r-ms2ep00g",
"OS-EXT-SRV-ATTR:root_device_name": "/dev/vda",
"OS-EXT-SRV-ATTR:user_data": null,
"OS-EXT-STS:power_state": "Running",
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"OS-SRV-USG:launched_at": "2021-12-16T18:57:24.000000",
<...>
"volumes_attached": ""
}
Étape 2 : Connectez-vous à l’hyperviseur et videz le XML de l’instance
Ensuite, vous avez besoin d’un vidage du XML (en utilisant le virsh dumpxml
command) qui définit votre instance. Vous pouvez donc le filtrer à l’étape suivante, rediriger la sortie dans un fichier :
[compute2]$ sudo podman \
exec -it nova_libvirt bash(pod)[compute2]# virsh \
list --all
Id Name State
-----------------------------------
6 instance-00000245 running
7 instance-00000248 running(pod)[compute2]# virsh dumpxml instance-00000245 | tee inst245.xml
<domain type='kvm' id='6'>
<name>instance-00000245</name>
<uuid>1718c7d4-520a-4366-973d-d421555295b0</uuid>
<metadata>
<nova:instance xmlns:nova="http://openstack.org/xmlns/libvirt/nova/1.0">
<nova:package version="20.4.1-1.20201114041747.el8ost"/>
<nova:name>server-1</nova:name>
<nova:creationTime>2021-12-16 18:57:03</nova:creationTime>
[...]
</domain>
Étape 3 : Examiner la sortie XML
Une fois que vous avez la sortie XML, utilisez votre pager ou éditeur de texte préféré pour obtenir les informations d’interface réseau pour l’instance.
<interface type='bridge'>
<mac address='fa:16:3e:f7:15:db'/>
<source bridge='br-int'/>
<virtualport type='openvswitch'>
<parameters interfaceid='da128923-84c7-435e-9ec1-5a000ecdc163'/>
</virtualport>
<target dev='tap123'/>
<model type='virtio'/>
<driver name='vhost' rx_queue_size='1024'/>
<mtu size='8996'/>
<alias name='net0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>
À partir de cette sortie, filtrez le pont source (sur le nœud de calcul) et le périphérique cible (l’interface physique dans le nœud de calcul).
Cette sortie peut changer en fonction du type de pare-feu que vous utilisez ou si vous utilisez des groupes de sécurité où le flux est un peu différent, mais toutes les interfaces hôtes sont affichées et les étapes suivantes s’appliquent à toutes.
Étape 4 : Regardez l’appareil cible
Dans ce cas, tap123
sur le nœud de calcul se trouve le périphérique cible, alors examinez-le avec la commande ip :
[compute2]$ ip addr show tap123tap123: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 8996
inet6 fe80::fc16:3eff:fef7:15db prefixlen 64 scopeid 0x20<link>
ether fe:16:3e:f7:15:db txqueuelen 10000 (Ethernet)
[...]
Vous pouvez voir que le MTU est 8996, comme prévu. Vous pouvez également trouver l’adresse MAC (fe:16:3e:f7:15:db), de sorte que vous pouvez éventuellement confirmer le port à l’aide des commandes de port OpenStack.
Vous pouvez également vérifier que cette interface se trouve dans le pont br-int :
Bridge br-int
[...]
Port tap123
tag: 1
Interface tap123
C’est également comme prévu, car cela autorise le trafic sud et nord pour cette instance en utilisant le réseau externe.
Étape 5 : Modifier le MTU
Appliquez un changement de MTU commun sur l’hôte spécifiquement pour votre interface cible (tap123
dans cet exemple).
[compute2]$ sudo ifconfig tap123 mtu 1500
[compute2]$ ip addr show tap123 | grep mtu
tap123: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
Étape 6 : Répétez
Répétez maintenant la procédure à l’intérieur de l’instance pour déplacer le mtu de 8996 à 1500. Cela couvre la partie hyperviseur, car neutron est toujours configuré avec des trames jumbo.
[localhost]$ sudo ip link set dev eth0 mtu 1500
[localhost]$ ip addr show eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.169.252.186 netmask 255.255.255.255 broadcast 0.0.0.0
inet6 fe80::f816:3eff:fef7:15db prefixlen 64 scopeid 0x20<link>
ether fa:16:3e:f7:15:db txqueuelen 1000 (Ethernet)
RX packets 1226 bytes 242462 (236.7 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 401 bytes 292332 (285.4 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Validation
Maintenant, le chemin à l’intérieur du réseau local a un MTU de 1500. Si vous essayez d’envoyer un paquet plus grand que cela, une erreur devrait s’afficher :
[localhost]$ ping 10.169.252.1 -M do -s 1500
PING 10.169.252.1 (10.169.252.1) 1500(1528) bytes of data.
ping: local error: Message too long, mtu=1500
ping: local error: Message too long, mtu=1500
ping: local error: Message too long, mtu=1500
ping: local error: Message too long, mtu=1500--- 10.169.252.1 ping statistics ---
4 packets transmitted, 0 received, +4 errors, 100% packet loss, time 3000ms
Ce ping ajoute 28 octets à l’en-tête, tentant d’envoyer une charge utile de 1 500 octets + 28 octets. Le système ne peut pas l’envoyer car il dépasse le MTU. Une fois que vous avez réduit la charge utile à 1472, vous pouvez envoyer avec succès le ping dans une seule trame.
[localhost]$ ping 10.169.252.1 -M do -s 1472
PING 10.169.252.1 (10.169.252.1) 1472(1500) bytes of data.
1480 bytes from 10.169.252.1: icmp_seq=1 ttl=255 time=1.37 ms
1480 bytes from 10.169.252.1: icmp_seq=2 ttl=255 time=1.11 ms
1480 bytes from 10.169.252.1: icmp_seq=3 ttl=255 time=1.02 ms
1480 bytes from 10.169.252.1: icmp_seq=4 ttl=255 time=1.12 ms--- 10.169.252.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 1.024/1.160/1.378/0.131 ms
Voici comment mettre fin aux problèmes de fragmentation lorsque la plate-forme envoie des paquets de 9 000 octets au réseau, mais que la fragmentation se produit toujours dans certains composants du réseau. Vous avez maintenant résolu les problèmes de retransmission, la perte de paquets, la gigue, la latence et d’autres problèmes connexes.
Lorsque l’équipe réseau résout les problèmes de réseau, vous pouvez rétablir les commandes MTU à la valeur précédente. C’est ainsi que vous résolvez les problèmes de réseau sans avoir à redéployer la pile.
Simulation de bout en bout
Voici comment simuler le problème dans un scénario de bout en bout pour voir comment cela fonctionne. Au lieu d’envoyer un ping à la passerelle, vous pouvez envoyer un ping à une deuxième instance. Vous devez observer comment une incompatibilité de MTU provoque des problèmes, en particulier lorsqu’une application marque des paquets comme non fragmentés.
Supposons que vos serveurs aient les spécifications suivantes :
Serveur 1 :
Nom d’hôte : serveur1
IP : 10.169.252.186/24
MTU : 1500
Serveur 2 :
Nom d’hôte : serveur2
IP : 10.169.252.184/24
MTU : 8996
Se connecter à serveur1 et envoyer un ping à serveur2:
[server1]$ ping 10.169.252.184
PING 10.169.252.184 (10.169.252.184) 56(84) bytes of data.
64 bytes from 10.169.252.184: icmp_seq=1 ttl=64 time=0.503 ms
64 bytes from 10.169.252.184: icmp_seq=2 ttl=64 time=0.193 ms
64 bytes from 10.169.252.184: icmp_seq=3 ttl=64 time=0.213 ms--- 10.169.252.184 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.193/0.303/0.503/0.141 ms
Se connecter à serveur1 et envoyer un ping à serveur2 sans fragmentation avec un MTU de 1500 :
[server1]$ ping 10.169.252.184 -M do -s 1472
PING 10.169.252.184 (10.169.252.184) 1472(1500) bytes of data.
1480 bytes from 10.169.252.184: icmp_seq=1 ttl=64 time=0.512 ms
1480 bytes from 10.169.252.184: icmp_seq=2 ttl=64 time=0.293 ms
1480 bytes from 10.169.252.184: icmp_seq=3 ttl=64 time=0.230 ms
1480 bytes from 10.169.252.184: icmp_seq=4 ttl=64 time=0.268 ms
1480 bytes from 10.169.252.184: icmp_seq=5 ttl=64 time=0.230 ms
1480 bytes from 10.169.252.184: icmp_seq=6 ttl=64 time=0.208 ms
1480 bytes from 10.169.252.184: icmp_seq=7 ttl=64 time=0.219 ms
1480 bytes from 10.169.252.184: icmp_seq=8 ttl=64 time=0.229 ms
1480 bytes from 10.169.252.184: icmp_seq=9 ttl=64 time=0.228 ms--- 10.169.252.184 ping statistics ---
9 packets transmitted, 9 received, 0% packet loss, time 8010ms
rtt min/avg/max/mdev = 0.208/0.268/0.512/0.091 ms
Le MTU de serveur1 est de 1500, et serveur2 a une taille MTU supérieure à cela, donc une application s’exécutant sur serveur1 envoyer des paquets à serveur2 n’a pas de problèmes de fragmentation. Ce qui se passe si serveur2L’application de est également définie sur Non-Fragment, mais utilise un MTU de 9000 ?
[localhost]$ ping 10.169.252.186 -M do -s 8968
PING 10.169.252.186 (10.169.252.186) 8968(8996) bytes of data.--- 10.169.252.186 ping statistics ---
10 packets transmitted, 0 received, 100% packet loss, time 8999ms
La fragmentation se produit et les paquets envoyés ont été perdus.
Pour corriger cela, répétez le correctif MTU afin que les deux serveurs aient le même MTU. À titre de test, revenez serveur1:
[compute2]$ sudo ip link set dev tap123 mtu 8996
[compute2]$ ip addr show tap123 | grep mtu
tap123: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 8996[server1]$ sudo ip link set dev eth0 mtu 8996
[server1]$ ip addr show eth0 | grep mtu
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 8996
[...]
Répétez maintenant le ping de charge utile de 9 000 octets sans fragmentation autorisée :
[server2]$ ping 10.169.252.186 -M do -s 8968
PING 10.169.252.186 (10.169.252.186) 8968(8996) bytes of data.
8976 bytes from 10.169.252.186: icmp_seq=1 ttl=64 time=1.60 ms
8976 bytes from 10.169.252.186: icmp_seq=2 ttl=64 time=0.260 ms
8976 bytes from 10.169.252.186: icmp_seq=3 ttl=64 time=0.257 ms
8976 bytes from 10.169.252.186: icmp_seq=4 ttl=64 time=0.210 ms
8976 bytes from 10.169.252.186: icmp_seq=5 ttl=64 time=0.249 ms
8976 bytes from 10.169.252.186: icmp_seq=6 ttl=64 time=0.250 ms--- 10.169.252.186 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5001ms
rtt min/avg/max/mdev = 0.210/0.472/1.607/0.507 ms
Dépannage de la MTU
Il s’agit d’une solution de contournement simple pour aider les administrateurs réseau à résoudre les problèmes de MTU sans avoir besoin d’une mise à jour de la pile pour déplacer les MTU d’avant en arrière. Toutes ces configurations MTU sont également temporaires. Un redémarrage de l’instance ou du système entraîne le retour de toutes les interfaces à la valeur d’origine (et configurée).
Cela ne prend également que quelques minutes à exécuter, donc j’espère que vous trouverez cela utile.