Welche Belastung der Server führt zu Netzwerkmechanismen?

Bei der Analyse des Betriebs des Netzwerksubsystems von Servern werden normalerweise Indikatoren wie Latenz, Durchsatz des Systems und die Anzahl der Pakete, die pro Sekunde verarbeitet werden können (PPS, Pakete pro Sekunde), berücksichtigt. Diese Indikatoren werden verwendet, um zu verstehen, unter welcher maximalen Last der untersuchte Computer arbeiten kann. Und obwohl diese Metriken wichtig sind und oft viel über das System aussagen können, liefern sie keine Informationen darüber, welche Auswirkungen die Verarbeitung von Netzwerkpaketen auf Programme hat, die auf dem Server ausgeführt werden. Dieses Material zielt darauf ab, die Last zu untersuchen, die durch Netzwerkmechanismen auf Servern erzeugt wird. Insbesondere werden wir darüber sprechen, wie viel Prozessorzeit die Lösung für Netzwerkprobleme von verschiedenen Prozessen „stehlen“ kann, die auf Linux-Systemen ausgeführt werden.





Netzwerkpaketverarbeitung unter Linux


Linux verarbeitet eine erhebliche Anzahl von Paketen im Kontext eines Prozesses, der vom Prozessor zum Zeitpunkt der Verarbeitung des entsprechenden IRQ ausgeführt wird. Die System Accounting Engine weist die dafür verwendeten Prozessorzyklen jedem Prozess zu, der gerade ausgeführt wird. Dies wird auch dann durchgeführt, wenn dieser Prozess nichts mit der Netzwerkpaketverarbeitung zu tun hat. Beispielsweise kann ein Team topangeben, dass ein Prozess anscheinend mehr als 99% der Ressourcen des Prozessors verbraucht. Tatsächlich werden jedoch 60% der Prozessorzeit für die Verarbeitung der Pakete aufgewendet. Dies bedeutet, dass der Prozess selbst, der seine eigenen Probleme löst, nur 40% der CPU-Ressourcen verbraucht.

Eingehender Handlernet_rx_actionin der Regel sehr, sehr schnell durchgeführt. Zum Beispiel in weniger als 25 μs. (Diese Daten stammen aus Messungen mit eBPF. Wenn Sie an den Details interessiert sind, lesen Sie hier net_rx_action .) Der Prozessor kann bis zu 64 Pakete pro NAPI-Instanz (NIC oder RPS) verarbeiten, bevor die Aufgabe auf einen anderen SoftIRQ-Zyklus verschoben wird. Nacheinander können ohne Unterbrechung bis zu 10 SoftIRQ-Zyklen folgen, was ungefähr 2 ms dauert (mehr dazu erfahren Sie, indem Sie darüber lesen __do_softirq). Wenn der SoftIRQ-Vektor nach Ablauf der maximalen Anzahl von Zyklen oder nach Ablauf der Zeit immer noch ungelöste Probleme aufweist, wird die Lösung dieser Probleme für die Ausführung im Thread verzögertksoftirqdspezifische CPU. In diesem Fall erweist sich das System als etwas transparenter im Sinne des Abrufs von Informationen über die durch Netzwerkoperationen erzeugte Prozessorlast (obwohl eine solche Analyse unter der Annahme durchgeführt wird, dass SoftIRQ untersucht wird, das sich auf die Paketverarbeitung bezieht und nicht auf etwas anderes). .

Eine Möglichkeit, die oben genannten Indikatoren zu erhalten, besteht darin, Folgendes zu verwenden 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

Hier ist das Ergebnis:

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

In diesem Fall ist der Prozessor inaktiv (daher das Auftreten von Einträgen swapperfür den Prozess), IRQ wird für die Rx-Warteschlange auf CPU 5 aufgerufen, die SoftIRQ-Verarbeitung wird zweimal aufgerufen, 64 Pakete werden zuerst verarbeitet, dann 27. Der nächste IRQ wird nach 229 μs aufgerufen und startet den Zyklus erneut.

Diese Daten wurden auf einem Leerlaufsystem erhalten. Auf dem Prozessor kann jedoch jede Aufgabe ausgeführt werden. In diesem Fall tritt die obige Abfolge von Ereignissen auf, die diese Aufgabe unterbrechen und IRQ / SoftIRQ-Aufgaben ausführen. Gleichzeitig schreibt das System Accounting dem unterbrochenen Prozess die vom Prozessor erzeugte Last zu. Infolgedessen sind Netzwerkpaketverarbeitungsaufgaben normalerweise vor herkömmlichen Tools zur Überwachung der Prozessorlast verborgen. Sie werden im Kontext eines zufällig ausgewählten Prozesses im Kontext des „Opferprozesses“ ausgeführt. Dies führt uns zu einigen Fragen. Wie kann die Zeit geschätzt werden, für die der Prozess für die Verarbeitung von Paketen unterbrochen wird? Wie kann man zwei verschiedene Netzwerklösungen vergleichen, um zu verstehen, welche davon weniger Einfluss auf verschiedene Aufgaben haben, die auf einem Computer gelöst werden?

Bei Verwendung von RSS-, RPS- und RFS-Mechanismen wird die Paketverarbeitung normalerweise auf die Prozessorkerne verteilt. Daher bezieht sich die obige Paketverarbeitungssequenz auf jede spezifische CPU. Mit zunehmender Paketankunftsrate (ich denke, wir können von Geschwindigkeiten von 100.000 Paketen pro Sekunde und höher sprechen) muss jede CPU Tausende oder Zehntausende von Paketen pro Sekunde verarbeiten. Die Verarbeitung so vieler Pakete wirkt sich unweigerlich auf andere auf dem Server ausgeführte Aufgaben aus.

Betrachten Sie eine Möglichkeit, diesen Effekt zu bewerten.

Deaktivieren der verteilten Paketverarbeitung


Stoppen wir zunächst die verteilte Verarbeitung von Paketen, indem wir RPS deaktivieren und Flusssteuerungsregeln einrichten, die darauf abzielen, die Verarbeitung aller Pakete zu organisieren, die sich auf eine bestimmte MAC-Adresse auf der einzigen uns bekannten CPU beziehen. Mein System verfügt über 2 Netzwerkkarten, die in einer 802.3ad-Konfiguration zusammengefasst sind. Netzwerkaufgaben werden einer einzelnen virtuellen Maschine zugewiesen, die auf einem Computer ausgeführt wird.

RPS auf Netzwerkadaptern ist wie folgt deaktiviert:

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

Als Nächstes richten wir die Flusssteuerungsregeln ein, um sicherzustellen, dass Pakete mit einer einzigen CPU in die virtuelle Testmaschine gelangen:

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

Durch Deaktivieren von RPS und Verwenden von Flusssteuerungsregeln können wir sicherstellen, dass alle für unsere virtuelle Maschine bestimmten Pakete auf derselben CPU verarbeitet werden. Um sicherzustellen, dass Pakete an die Warteschlange gesendet werden, an die sie gesendet werden sollen, können Sie einen Befehl wie ethq verwenden . Dann können Sie herausfinden, zu welcher CPU diese Warteschlange gehört /proc/interrupts. In meinem Fall wird Runde 2 mittels CPU 5 bearbeitet.

Öffnet den Geschwindigkeitsbefehl


Ich könnte Dienstprogramme verwenden perfoder SoftIRQ-Laufzeiten analysieren, die für die Verarbeitung des eingehenden Datenverkehrs verantwortlich sind bpf, aber dieser Ansatz ist ziemlich kompliziert. Darüber hinaus beeinflusst der Beobachtungsprozess selbst definitiv die Ergebnisse. Eine viel einfachere und verständlichere Lösung besteht darin, die durch die Netzwerkoperationen auf dem System erzeugte Last mithilfe einer Aufgabe zu identifizieren, die eine bekannte Last auf dem System erzeugt. Dies ist beispielsweise ein Befehl openssl speedzum Testen der OpenSSL-Leistung. Auf diese Weise können Sie herausfinden, wie viel Prozessorressourcen das Programm in der Realität erhält, und es mit der Menge der Ressourcen vergleichen, die es empfangen soll (dies hilft herauszufinden, wie viel Ressourcen für Netzwerkaufgaben ausgegeben werden).

Das Team ist openssl speedfast zu 100% ein User-Space-Team. Wenn Sie es an eine bestimmte CPU binden, verwendet es während der Ausführung von Tests alle verfügbaren Ressourcen. Das Team stellt den Timer auf das angegebene Intervall ein (hier dauert es beispielsweise 10 Sekunden, um die Berechnungen zu vereinfachen), führt den Test aus und ermittelt dann beim Auslösen des Timers times(), wie viel Prozessorzeit das Programm tatsächlich hat. Aus der Sicht syscallsieht es so aus:

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

Das heißt, es stellt sich heraus, dass zwischen dem Aufrufen alarm()und dem Überprüfen der Ergebnisse nur sehr wenige Systemaufrufe getätigt wurden. Wenn das Programm nicht oder nur sehr selten unterbrochen wurde, tms_utimestimmt die Zeit mit der Testzeit überein (in diesem Fall 10 Sekunden).

Da dies ein Test ist, der ausschließlich im Benutzerbereich durchgeführt wird, bedeutet jede Systemzeit, die in angezeigt times()wird, eine zusätzliche Belastung des Systems. Es stellt sich heraus, dass openssldies zwar ein Prozess ist, der auf der CPU ausgeführt wird, die CPU selbst jedoch möglicherweise mit etwas anderem beschäftigt ist. Beispiel: Verarbeiten von Netzwerkpaketen:

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

Hier sehen Sie, dass es opensslmöglich war, 7,49 Sekunden am Prozessor zu arbeiten (178 + 571 in Maßeinheiten entsprechend 0,01 s). Gleichzeitig aber 5,71 s. Dieses Intervall wird durch die Systemzeit dargestellt. Da er opensslmit keinem Geschäft im Kernelbereich beschäftigt ist, bedeutet dies, dass 5,71 s. - Dies ist das Ergebnis einer zusätzlichen Belastung des Systems. Das heißt, dies ist die Zeit, in der der Prozess "gestohlen" wurde, um die Anforderungen des Systems zu erfüllen.

Verwenden des Befehls openssl speed zum Erkennen der Systemlast, die durch Netzwerkmechanismen verursacht wird


Nachdem wir herausgefunden haben, wie das Team arbeitet openssl speed, werden wir uns die Ergebnisse ansehen, die es auf einem praktisch inaktiven Server erzielt:

$ 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

Wie Sie sehen können, wird uns mitgeteilt, dass das Programm 9,99 bis 10 Sekunden benötigt, um Blöcke unterschiedlicher Größe zu verarbeiten. Dies bestätigt, dass Systemmechanismen dem Programm keine Prozessorzeit entziehen. Mit netperfwird nun der Server geladen, indem Pakete aus zwei Quellen verarbeitet werden. Führen Sie den Test erneut aus:

$ 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

Die Ergebnisse unterscheiden sich stark von denen auf einem inaktiven Server. Es wird erwartet, dass jeder der Tests innerhalb von 10 Sekunden ausgeführt wird, es wird jedoch times()berichtet, dass die tatsächliche Ausführungszeit zwischen 1,78 und 2,1 Sekunden liegt. Dies bedeutet, dass die verbleibende Zeit zwischen 7,9 und 8,22 Sekunden für die Verarbeitung der Pakete aufgewendet wurde, entweder im Kontext des Prozesses openssloder in ksoftirqd.

Werfen wir einen Blick darauf, was das Team topbei der Analyse des gerade abgeschlossenen Starts herausgeben wird 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

Hier könnte man denken, dass es opensslungefähr 73% der Ressourcen von CPU 5 verbraucht und die ksoftirqdverbleibenden Ressourcen erhalten werden. In der Realität wird jedoch im Kontext die opensslVerarbeitung einer so großen Anzahl von Paketen durchgeführt, dass das Programm selbst nur 18 bis 21% der Prozessorzeit benötigt, um seine Probleme zu lösen.

Wenn Sie die Netzwerklast auf 1 Stream reduzieren, haben Sie openssldas Gefühl, dass 99% der Systemressourcen verbraucht werden.

  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

In Wirklichkeit stellt sich jedoch heraus, dass das im Benutzerbereich ausgeführte Programm von den erwarteten 10 Sekunden nur etwa 4 Sekunden erhält:

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

Herkömmliche Prozessüberwachungstools weisen darauf hin, dass das Programm fast alle Prozessorressourcen verwendet. In Wirklichkeit werden jedoch 55-80% der CPU-Ressourcen für die Verarbeitung von Netzwerkpaketen ausgegeben. Der Durchsatz des Systems sieht gleichzeitig gut aus (mehr als 22 Gbit / s pro 25 Gbit / s Leitung), dies hat jedoch enorme Auswirkungen auf die in diesem System ausgeführten Prozesse.

Zusammenfassung


Hier haben wir ein Beispiel untersucht, wie Paketverarbeitungsmechanismen Prozessortakte von einem einfachen und nicht sehr wichtigen Benchmark „stehlen“. Auf einem realen Server können Prozesse, die in ähnlicher Weise betroffen sind, alles sein. Dies können virtuelle Prozessoren, Emulator-Threads und vhost-Threads von virtuellen Maschinen sein. Dies können unterschiedliche Systemprozesse sein, deren Auswirkungen sich unterschiedlich auf die Leistung dieser Prozesse und des gesamten Systems auswirken können.

Berücksichtigen Sie bei der Analyse Ihrer Server die Auswirkungen der mit dem Netzwerkbetrieb verbundenen Last auf deren tatsächliche Leistung?


All Articles