Quel type de charge sur les serveurs crée des mécanismes de réseau?

Lors de l'analyse du fonctionnement du sous-système réseau de serveurs, l'attention est généralement portée sur des indicateurs tels que la latence, le débit du système et le nombre de paquets pouvant être traités par seconde (PPS, Packets Per Second). Ces indicateurs sont utilisés pour comprendre sous quelle charge maximale l'ordinateur à l'étude peut fonctionner. Et bien que ces mesures soient importantes et puissent souvent en dire long sur le système, elles ne fournissent pas d'informations sur l'impact du traitement des paquets réseau sur les programmes exécutés sur le serveur. Ce matériel vise à étudier la charge créée par les mécanismes de réseau sur les serveurs. En particulier, nous parlerons du temps processeur que la solution aux problèmes de réseau peut «voler» à divers processus exécutés sur les systèmes Linux.





Traitement des paquets réseau sous Linux


Linux traite un nombre important de paquets dans le contexte de tout processus exécuté par le processeur au moment du traitement de l'IRQ correspondant. Le moteur de comptabilité système attribuera les cycles de processeur utilisés à cet effet à tout processus en cours d'exécution. Cela sera fait même si ce processus n'a rien à voir avec le traitement des paquets réseau. Par exemple, une équipe toppeut indiquer qu'un processus semble utiliser plus de 99% des ressources du processeur, mais en fait 60% du temps du processeur sera consacré au traitement des paquets. Et cela signifie que le processus lui-même, résolvant ses propres problèmes, n'utilise que 40% des ressources du processeur.

Gestionnaire entrantnet_rx_actiongénéralement effectué très, très rapidement. Par exemple, en moins de 25 μs. (Ces données ont été obtenues à partir de mesures utilisant eBPF. Si vous êtes intéressé par les détails, regardez ici net_rx_action .) Le processeur peut traiter jusqu'à 64 paquets par instance NAPI (NIC ou RPS) avant de reporter la tâche à un autre cycle SoftIRQ. L'un après l'autre, sans interruption, jusqu'à 10 cycles SoftIRQ peuvent suivre, ce qui prend environ 2 ms (vous pouvez en savoir plus à ce sujet en lisant à propos de __do_softirq). Si le vecteur SoftIRQ, après le nombre maximal de cycles ou le temps écoulé, a toujours des problèmes non résolus, la solution de ces problèmes est retardée pour l'exécution dans le threadksoftirqdCPU spécifique. Lorsque cela se produit, le système se révèle un peu plus transparent dans le sens d'obtenir des informations sur la charge du processeur créée par les opérations réseau (bien qu'une telle analyse soit effectuée en supposant que c'est SoftIRQ qui est étudié, qui est lié au traitement des paquets, et non à autre chose) .

Une façon d'obtenir les indicateurs ci-dessus consiste à utiliser perf:

sudo perf record -a \
        -e irq:irq_handler_entry,irq:irq_handler_exit
        -e irq:softirq_entry --filter="vec == 3" \
        -e irq:softirq_exit --filter="vec == 3"  \
        -e napi:napi_poll \
        -- sleep 1

sudo perf script

Voici le résultat:

swapper     0 [005] 176146.491879: irq:irq_handler_entry: irq=152 name=mlx5_comp2@pci:0000:d8:00.0
swapper     0 [005] 176146.491880:  irq:irq_handler_exit: irq=152 ret=handled
swapper     0 [005] 176146.491880:     irq:softirq_entry: vec=3 [action=NET_RX]
swapper     0 [005] 176146.491942:        napi:napi_poll: napi poll on napi struct 0xffff9d3d53863e88 for device eth0 work 64 budget 64
swapper     0 [005] 176146.491943:      irq:softirq_exit: vec=3 [action=NET_RX]
swapper     0 [005] 176146.491943:     irq:softirq_entry: vec=3 [action=NET_RX]
swapper     0 [005] 176146.491971:        napi:napi_poll: napi poll on napi struct 0xffff9d3d53863e88 for device eth0 work 27 budget 64
swapper     0 [005] 176146.491971:      irq:softirq_exit: vec=3 [action=NET_RX]
swapper     0 [005] 176146.492200: irq:irq_handler_entry: irq=152 name=mlx5_comp2@pci:0000:d8:00.0

Dans ce cas, le processeur est inactif (d'où l'apparition d'entrées swapperpour le processus), l'IRQ est appelé pour la file d'attente Rx sur le CPU 5, le traitement SoftIRQ est appelé deux fois, 64 paquets sont traités en premier, puis 27. L'IRQ suivante est appelée après 229 μs et recommence le cycle.

Ces données ont été obtenues sur un système inactif. Mais sur le processeur, n'importe quelle tâche peut être effectuée. Dans ce cas, la séquence d'événements ci-dessus se produit, interrompant cette tâche et effectuant des tâches IRQ / SoftIRQ. Dans le même temps, la comptabilité système attribue au processus interrompu la charge créée par le processeur. Par conséquent, les tâches de traitement des paquets réseau sont généralement masquées des outils classiques de surveillance de la charge du processeur. Ils sont exécutés dans le cadre d'un processus choisi au hasard, dans le contexte du «processus victime». Cela nous amène à quelques questions. Comment estimer le temps pendant lequel le processus est interrompu pour le traitement des paquets Comment comparer 2 solutions réseau différentes afin de comprendre laquelle a le moins d'effet sur les différentes tâches résolues sur un ordinateur?

Lors de l'utilisation des mécanismes RSS, RPS, RFS, le traitement des paquets est généralement réparti entre les cœurs du processeur. Par conséquent, la séquence de traitement de paquets ci-dessus est liée à chaque CPU spécifique. À mesure que le taux d'arrivée des paquets augmente (je pense que nous pouvons parler de vitesses de 100 000 paquets par seconde et plus), chaque CPU doit traiter des milliers ou des dizaines de milliers de paquets par seconde. Le traitement d'un si grand nombre de paquets affectera inévitablement d'autres tâches effectuées sur le serveur.

Envisagez une façon d'évaluer cet effet.

Désactivation du traitement distribué des paquets


Pour commencer, arrêtons le traitement distribué des paquets en désactivant RPS et en configurant des règles de contrôle de flux visant à organiser le traitement de tous les paquets liés à une adresse MAC spécifique sur le seul CPU que nous connaissons. Mon système possède 2 cartes réseau agrégées dans une configuration 802.3ad. Les tâches réseau sont attribuées à une seule machine virtuelle exécutée sur un ordinateur.

RPS sur les cartes réseau est désactivé comme suit:

for d in eth0 eth1; do
    find /sys/class/net/${d}/queues -name rps_cpus |
    while read f; do
            echo 0 | sudo tee ${f}
    done
done

Ensuite, nous avons configuré les règles de contrôle de flux pour garantir que les paquets entrent dans la machine virtuelle de test à l'aide d'un seul processeur:

DMAC=12:34:de:ad:ca:fe
sudo ethtool -N eth0 flow-type ether dst ${DMAC} action 2
sudo ethtool -N eth1 flow-type ether dst ${DMAC} action 2

La désactivation de RPS et l'utilisation de règles de contrôle de flux nous permettent de garantir que tous les paquets destinés à notre machine virtuelle sont traités sur le même CPU. Afin de vous assurer que les paquets sont envoyés dans la file d'attente vers laquelle ils doivent être envoyés, vous pouvez utiliser une commande comme ethq . Ensuite, vous pouvez savoir à quel processeur cette file d'attente appartient /proc/interrupts. Dans mon cas, le virage 2 est traité au moyen de la CPU 5.

Commande de vitesse openssl


Je pourrais utiliser des utilitaires perfou analyser les runtimes SoftIRQ responsables du traitement du trafic entrant bpf, mais cette approche est assez compliquée. De plus, le processus d'observation lui-même affecte définitivement les résultats. Une solution beaucoup plus simple et plus compréhensible consiste à identifier la charge créée par les opérations réseau sur le système à l'aide d'une tâche, celle qui crée une charge connue sur le système. Par exemple, il s'agit d'une commande openssl speedutilisée pour tester les performances d'OpenSSL. Cela vous permettra de savoir combien de ressources processeur le programme obtient en réalité et de le comparer avec la quantité de ressources qu'il est censé recevoir (cela vous aidera à savoir combien de ressources sont dépensées pour les tâches réseau).

L'équipe est openssl speedpresque 100% une équipe de l'espace utilisateur. Si vous le liez à un certain processeur, alors, lors de l'exécution des tests, il utilise toutes ses ressources disponibles. L'équipe travaille en réglant la minuterie à l'intervalle spécifié (ici, par exemple, pour faciliter les calculs, cela prend 10 secondes), en exécutant le test, puis, lorsque la minuterie est déclenchée, en l'utilisant times()pour savoir combien de temps processeur le programme a réellement obtenu. Du point de vue, syscallcela ressemble à ceci:

alarm(10)                               = 0
times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 1726601344
--- SIGALRM {si_signo=SIGALRM, si_code=SI_KERNEL} ---
rt_sigaction(SIGALRM, ...) = 0
rt_sigreturn({mask=[]}) = 2782545353
times({tms_utime=1000, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 1726602344

Autrement dit, il s'avère que alarm()très peu d'appels système ont été effectués entre l'appel et la vérification des résultats. Si le programme n'a pas été interrompu, ou très rarement interrompu, le temps tms_utimecoïncidera avec le temps de test (dans ce cas, 10 secondes).

Puisqu'il s'agit d'un test effectué exclusivement dans l'espace utilisateur, toute heure système qui apparaît times()signifie une charge supplémentaire sur le système. Il s'avère que bien que opensslce processus s'exécute sur le CPU, le CPU lui-même peut être occupé par autre chose. Par exemple, le traitement des paquets réseau:

alarm(10)                               = 0
times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 1726617896
--- SIGALRM {si_signo=SIGALRM, si_code=SI_KERNEL} ---
rt_sigaction(SIGALRM, ...) = 0
rt_sigreturn({mask=[]}) = 4079301579
times({tms_utime=178, tms_stime=571, tms_cutime=0, tms_cstime=0}) = 1726618896

Ici, vous pouvez voir qu'il a été opensslpossible de travailler sur le processeur pendant 7,49 secondes (178 + 571 en unités de mesure correspondant à 0,01 s.). Mais en même temps 5,71 s. cet intervalle est représenté par l'heure système. Comme il n'est openssloccupé par aucune activité dans l'espace du noyau, cela signifie que 5,71 s. - Ceci est le résultat d'une charge supplémentaire sur le système. C'est-à-dire que c'est le moment où le processus a été "volé" afin de répondre aux besoins du système.

Utilisation de la commande openssl speed pour détecter la charge du système causée par les mécanismes du réseau


Maintenant que nous avons compris comment fonctionne l'équipe openssl speed, nous allons examiner les résultats qu'elle produit sur un serveur pratiquement inactif:

$ taskset -c 5 openssl speed -seconds 10 aes-256-cbc >/dev/null
Doing aes-256 cbc for 10s on 16 size blocks: 66675623 aes-256 cbc's in 9.99s
Doing aes-256 cbc for 10s on 64 size blocks: 18096647 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 256 size blocks: 4607752 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 1024 size blocks: 1162429 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 8192 size blocks: 145251 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 16384 size blocks: 72831 aes-256 cbc's in 10.00s

Comme vous pouvez le voir, nous sommes informés que le programme passe de 9,99 à 10 secondes pour traiter des blocs de différentes tailles. Cela confirme que les mécanismes du système ne prennent pas de temps processeur du programme. Maintenant, en utilisant netperf, nous allons charger le serveur en traitant les paquets provenant de deux sources. Relancez le test:

$ taskset -c 5 openssl speed -seconds 10 aes-256-cbc >/dev/null
Doing aes-256 cbc for 10s on 16 size blocks: 12061658 aes-256 cbc's in 1.96s
Doing aes-256 cbc for 10s on 64 size blocks: 3457491 aes-256 cbc's in 2.10s
Doing aes-256 cbc for 10s on 256 size blocks: 893939 aes-256 cbc's in 2.01s
Doing aes-256 cbc for 10s on 1024 size blocks: 201756 aes-256 cbc's in 1.86s
Doing aes-256 cbc for 10s on 8192 size blocks: 25117 aes-256 cbc's in 1.78s
Doing aes-256 cbc for 10s on 16384 size blocks: 13859 aes-256 cbc's in 1.89s

Les résultats sont très différents de ceux obtenus sur un serveur inactif. Il est prévu que chacun des tests soit exécuté dans les 10 secondes, mais times()indique que le temps d'exécution réel est de 1,78 à 2,1 secondes. Cela signifie que le temps restant, variant de 7,9 à 8,22 secondes, a été consacré au traitement des paquets, soit dans le contexte du processus, opensslsoit dans ksoftirqd.

Voyons ce que l'équipe donnera toplors de l'analyse du lancement qui vient de se terminer openssl speed.

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND              P 
 8180 libvirt+  20   0 33.269g 1.649g 1.565g S 279.9  0.9  18:57.81 qemu-system-x86     75
 8374 root      20   0       0      0      0 R  99.4  0.0   2:57.97 vhost-8180          89
 1684 dahern    20   0   17112   4400   3892 R  73.6  0.0   0:09.91 openssl              5    
   38 root      20   0       0      0      0 R  26.2  0.0   0:31.86 ksoftirqd/5          5

Ici, vous pourriez penser qu'il opensslutilise environ 73% des ressources du processeur 5, et les ksoftirqdressources restantes sont obtenues. Mais en réalité, dans le contexte openssl, le traitement d'un si grand nombre de packages est effectué que le programme lui-même ne prend que 18 à 21% du temps du processeur pour résoudre ses problèmes.

Si vous réduisez la charge réseau à 1 flux, vous opensslavez l' impression que 99% des ressources système sont consommées.

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND              P
 8180 libvirt+  20   0 33.269g 1.722g 1.637g S 325.1  0.9 166:38.12 qemu-system-x86     29
44218 dahern    20   0   17112   4488   3996 R  99.2  0.0   0:28.55 openssl              5
 8374 root      20   0       0      0      0 R  64.7  0.0  60:40.50 vhost-8180          55
   38 root      20   0       0      0      0 S   1.0  0.0   4:51.98 ksoftirqd/5          5

Mais en réalité, il s'avère que le programme en cours d'exécution dans l'espace utilisateur obtient, sur les 10 secondes attendues, seulement environ 4 secondes:

Doing aes-256 cbc for 10s on 16 size blocks: 26596388 aes-256 cbc's in 4.01s
Doing aes-256 cbc for 10s on 64 size blocks: 7137481 aes-256 cbc's in 4.14s
Doing aes-256 cbc for 10s on 256 size blocks: 1844565 aes-256 cbc's in 4.31s
Doing aes-256 cbc for 10s on 1024 size blocks: 472687 aes-256 cbc's in 4.28s
Doing aes-256 cbc for 10s on 8192 size blocks: 59001 aes-256 cbc's in 4.46s
Doing aes-256 cbc for 10s on 16384 size blocks: 28569 aes-256 cbc's in 4.16s

Les outils de surveillance de processus conventionnels indiquent que le programme utilise presque toutes les ressources du processeur, mais en réalité, il s'avère que 55 à 80% des ressources du processeur sont consacrées au traitement des paquets réseau. Le débit du système en même temps semble excellent (plus de 22 Gb / s par ligne de 25 Gb / s), mais cela a un impact énorme sur les processus en cours d'exécution dans ce système.

Sommaire


Ici, nous avons examiné un exemple de la façon dont les mécanismes de traitement des paquets «volent» les horloges des processeurs à partir d'une référence simple et peu importante. Mais sur un vrai serveur, les processus qui sont affectés de la même manière peuvent être n'importe quoi. Il peut s'agir de processeurs virtuels, de threads d'émulation, de threads vhost de machines virtuelles. Il peut s'agir de processus système différents, dont l'impact peut avoir un impact différent sur les performances de ces processus et sur l'ensemble du système.

Considérez-vous, en analysant vos serveurs, l'impact sur leurs performances réelles de la charge associée aux opérations réseau?


All Articles