Indice dei contenuti dell'articolo:
La rete TCP/IP è un argomento complesso e diventa davvero complicato quando si tenta di definire problemi con le prestazioni o risolvere un problema. Aiuta a disporre di strumenti in grado di sondare il tuo sistema e confermare i tuoi sospetti o, meglio ancora, che non ci sono problemi.
Uno di questi strumenti è l’open source iperf3. Ecco la sua descrizione da GitHub:
iperf è uno strumento per la misura attiva della larghezza di banda massima raggiungibile su reti IP. Supporta l’ottimizzazione di vari parametri relativi a tempi, protocolli e buffer. Ciascun test riporta il throughput/bitrate misurato, la perdita e altri parametri.
Questo articolo mostra come:
- Esamina i problemi di larghezza di banda tra due endpoint con iperf 3
- Testare la connettività multicast UDP (User Datagram Protocol) (utilizzato da Precision Time Protocol e altri protocolli per la sincronizzazione dell’ora)
- Scopri gli errori del controllo di ridondanza ciclica (CRC) su un’interfaccia di rete
- Usa ethtool e tcpdump per confermare che un’interfaccia di rete o un cavo difettosi interrompono il traffico
- Scrivi script più complessi usando Python 3
Spiegherò anche brevemente l’affinità della CPU e perché potrebbe essere importante per iperf3.
Inizia con iperf3
Per seguire questo tutorial, avrai bisogno di:
- Una distribuzione Linux (ho eseguito i miei esempi su un server Fedora)
- La possibilità di eseguire comandi come root (usando sudo , per esempio)
- Una comprensione di base dei principi di rete
Esegui il comando per installare iperf3. Su Fedora:
$ sudo dnf install -y iperf3
Iperf3 funziona eseguendo un client e un server che dialogano tra loro. Ecco alcuni termini da conoscere prima di iniziare a usarlo:
- Il throughput misura quanti pacchetti arrivano alle destinazioni con successo.
- La larghezza di banda di rete è la massima capacità di trasferimento dati di una rete.
- Il jitter è il ritardo di tempo tra quando un segnale viene trasmesso e quando viene ricevuto. Le buone connessioni hanno un tempo di risposta costante.
- TCP sta per Transmission Control Protocol . È un protocollo affidabile che garantisce l’arrivo dei pacchetti nello stesso ordine in cui sono stati inviati tramite una stretta di mano .
- UDP non ha un protocollo di handshake come TCP. È più veloce di TCP, ma se un pacchetto viene perso, non verrà rispedito e non vi è alcuna garanzia che i pacchetti arrivino nell’ordine inviato.
Nella dimostrazione in questo articolo:
- Il client e il server si collegano all’interfaccia Ethernet cablata. (Non userò le interfacce wireless perché sono più soggette a jitter a causa del rumore esterno.)
- Il mio test utilizza le impostazioni predefinite (porta, connessione TCP a meno che non venga sovrascritta con il flag
--udp
sul client).
La dimostrazione conferma se:
- Lo switch tra le due macchine supporta connessioni a 1.000 Mbit/sec e le interfacce sono state configurate a tale capacità.
- La modalità full-duplex è abilitata per inviare e ricevere dati sulla scheda contemporaneamente. Lo confermerai più avanti nell’articolo con un altro strumento chiamato ethtool.
Senza ulteriori indugi, inizierò.
Misura la larghezza di banda e il jitter
Ecco i comandi iniziali sul server:
[server ~]$ sudo ethtool eth0|rg -e 'Speed|Duplex'
Speed: 1000Mb/s
Duplex: Full
[server ~]$ ip --oneline address|rg 192
2: eth0 inet 192.168.1.11/24 brd 192.168.1.255 scope global dynamic eth0\ valid_lft 2090sec preferred_lft 2090sec
[server ~]$ iperf3 --server --bind 192.168.1.11 -affinity 1
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
E ora il client:
[client ~]$ sudo ethtool eno1|rg -e 'Speed|Duplex'
Speed: 1000Mb/s
Duplex: Full
[client ~]$ iperf3 --client raspberrypi --bind 192.168.1.28 --affinity 1
Connecting to host raspberrypi, port 5201
[ 5] local 192.168.1.28 port 47609 connected to 192.168.1.11 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 111 MBytes 932 Mbits/sec 0 2.79 MBytes
[ 5] 1.00-2.00 sec 110 MBytes 923 Mbits/sec 0 2.98 MBytes
...
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 1021 MBytes 857 Mbits/sec 0 sender
[ 5] 0.00-9.95 sec 1020 MBytes 860 Mbits/sec receiver
iperf Done.
Analizzo i risultati:
- Zero tentativi (colonna Retr). Questo è buono e previsto.
- Il bitrate è di circa 860 Mbit/sec. La velocità del collegamento è vicina alla larghezza di banda teorica. Gli switch hanno un limite alla quantità di traffico che il backplane può gestire.
- Il TCP garantisce perdite di trasmissione dei pacchetti, quindi il jitter non viene riportato qui.
Se inverti il
Testare la larghezza di banda UDP
Per testare UDP, eseguire le seguenti operazioni solo sul client:
[client ~]$ iperf3 --client raspberrypi --bind 192.168.1.28 --udp --affinity 1
Connecting to host raspberrypi, port 5201
[ 5] local 192.168.1.28 port 47985 connected to 192.168.1.11 port 5201
[ ID] Interval Transfer Bitrate Total Datagrams
[ 5] 0.00-1.00 sec 129 KBytes 1.05 Mbits/sec 91
[ 5] 1.00-2.00 sec 127 KBytes 1.04 Mbits/sec 90
[ 5] 2.00-3.00 sec 129 KBytes 1.05 Mbits/sec 91
...
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams
[ 5] 0.00-10.00 sec 1.25 MBytes 1.05 Mbits/sec 0.000 ms 0/906 (0%) sender
[ 5] 0.00-9.99 sec 1.25 MBytes 1.05 Mbits/sec 0.028 ms 0/906 (0%) receiver
Ecco i risultati:
- Il bitrate è molto più vicino alla larghezza di banda teorica. Inoltre, non c’è perdita di pacchetti, il che è fantastico.
- UDP non garantisce la perdita di pacchetti, quindi vengono segnalati datagrammi e jitter persi (e hanno buoni valori).
Potresti chiederti cosa sia quella --affinity
bandiera. Non è davvero necessario qui per testare la larghezza di banda su questo semplice esempio, ma mi dà una scusa per parlare di affinità.
Deviazione veloce: affinità CPU, NUMA, isolcpus
Se eri curioso e hai controllato la documentazione e gli esempi di iperf, probabilmente hai visto riferimenti all’affinità della CPU o del processore .
Quindi, cos’è? Da Wikipedia:
L’affinità del processore, o blocco della CPU o “affinità della cache”, consente l’associazione e l’annullamento dell’associazione di un processo o di un thread a un’unità di elaborazione centrale (CPU) o a un intervallo di CPU, in modo che il processo o il thread venga eseguito solo sulla CPU designata o CPU anziché qualsiasi CPU.
Perché vorresti aggiungere un processo a un gruppo specifico di CPU?
Nessuna istanza con CPU bloccate può utilizzare le CPU di un’altra istanza bloccata, impedendo così la contesa di risorse tra le istanze. L’accesso alla memoria non uniforme (NUMA) consente a più CPU di condividere le cache L1, L2 e L3 e la memoria principale.
Puoi utilizzare l’hardware NUMA per assicurarti di utilizzare sempre la memoria più vicina alla CPU.
Che aspetto ha un server con diversi nodi NUMA? Puoi scoprirlo con lscpu| rg NUMA
:
[client ~]$ lscpu|rg NUMA
NUMA node(s): 2
NUMA node0 CPU(s): 0-7
NUMA node1 CPU(s): 8-15
Questo è un server a 16 CPU con due nodi NUMA (questo è un esempio semplificato, una macchina con HyperThreading abilitato ha un aspetto diverso. A seconda dell’applicazione, potresti decidere di disabilitarlo.
Ricorda che puoi utilizzare l’affinità della CPU non solo per aumentare le prestazioni di rete ma anche le prestazioni del disco.
Tornando a iperf3, puoi aggiungerlo a una CPU specifica usando -A
o --affinity
. Ad esempio, la CPU 3 (numerata da 0 a n-1) si presenta così:
# Equivalent of running iperf3 with numactl: /bin/numactl --physcpubind=2 iperf3 -c remotehost
iperf3 --affinity 2 --client remotehost
Ricorda che potresti anche dover dire al sistema operativo di evitare di eseguire processi host su queste CPU, quindi se usi Grubby, puoi farlo con isolcpus :
# Find the default kernel
$ sudo grubby --default-kernel
# Use that information and add isolcpus parameter, then reboot
$ sudo grubby --update-kernel=/boot/vmlinuz-5.14.18-100.fc33.x86_64 --args="isolcpus=2"
sudo shutdown -r now 'Updated kernel isolcpus, need to reboot'
Ancora una volta, questo non è necessario per risolvere un problema di rete, ma può tornare utile se vuoi fare in modo che iperf3 si comporti come una delle tue applicazioni ottimizzate.
L’ottimizzazione è un argomento complesso, quindi prendi una tazza di caffè (o due) e preparati a iniziare a leggere.
Usa iperf3 per rilevare i pacchetti persi e gli errori CRC
Un errore CRC è causato da un dispositivo fisico difettoso (scheda di rete, porta dello switch, cavo) o da una mancata corrispondenza nelle configurazioni full e half duplex tra due dispositivi. Questi a volte sono difficili da tracciare sugli switch con modalità cut-through, in cui lo switch inoltra gli errori ricevuti a tutte le porte.
Questo è uno scenario semplificato per garantire che una nuova connessione della scheda di rete funzioni senza CRC o errori ricevuti/trasmessi (Rx/Tx) (il che significa che la scheda, il cavo e la porta dello switch sono OK).
Con questo in mente, potresti fare un semplice test per assicurarti che lo stato del collegamento sia buono:
- Acquisisci lo stato del CRC e gli errori di pacchetti eliminati sulla scheda di rete in fase di test.
- Esegui iperf3 in modalità TCP per un tempo più lungo del solito.
- Recupera le statistiche CRC della scheda di rete.
Se la differenza è maggiore di zero, allora:
- Controllare la modalità full-duplex sia sulla scheda che sulla porta dello switch (ethtool).
- Sostituire il cavo.
- Riposizionare o sostituire la scheda di rete.
- Cambia la porta sullo switch.
Ottieni l’immagine; iperf3 aiuterà a “bruciare” il collegamento e ad attivare qualsiasi comportamento indesiderato prima di utilizzare questa interfaccia in produzione.
Ecco il processo in azione. Supponiamo di fare la prima istantanea sul server iperf3:
[server ~]$ sudo ethtool --statistics eth0| rg -i -e 'dropped|error'
rx_errors: 0
tx_errors: 0
rx_dropped: 0
tx_dropped: 0
rxq0_errors: 0
rxq0_dropped: 0
rxq1_errors: 0
rxq1_dropped: 0
rxq2_errors: 0
rxq2_dropped: 0
rxq3_errors: 0
rxq3_dropped: 0
rxq16_errors: 0
rxq16_dropped: 0
Quindi il cliente:
[client ~]$ sudo ethtool --statistics eno1| rg -i -e 'dropped|errors'
tx_errors: 0
rx_errors: 0
align_errors: 0
Esegui il iperf3
server:
[server ~]$ iperf3 --server --bind 192.168.1.11
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Esegui iperf3
sul client per 120 secondi:
[client ~]$ iperf3 --client raspberrypi --bind 192.168.1.28 --time 120
Connecting to host raspberrypi, port 5201
[ 5] local 192.168.1.28 port 41337 connected to 192.168.1.11 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 111 MBytes 934 Mbits/sec 0 2.94 MBytes
[ 5] 1.00-2.00 sec 111 MBytes 933 Mbits/sec 0 2.95 MBytes
[ 5] 2.00-3.00 sec 111 MBytes 933 Mbits/sec 0 2.95 MBytes
...
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-120.00 sec 11.0 GBytes 787 Mbits/sec 0 sender
[ 5] 0.00-119.70 sec 11.0 GBytes 789 Mbits/sec receiver
# Measure again ...
[client ~]$ sudo ethtool --statistics eno1| rg -i -e 'dropped|errors'
tx_errors: 0
rx_errors: 0
align_errors: 0
Ora parlerò di un altro strumento utile per ottenere le statistiche dell’interfaccia di rete, ethtool.
Cos’è ethtool?
Come spiega Wikipedia :
ethtool è il mezzo principale nei sistemi operativi basati su kernel Linux (principalmente Linux e Android) per visualizzare e modificare i parametri dei controller di interfaccia di rete (NIC) e il software del driver di dispositivo associato dai programmi applicativi in
esecuzione nello spazio utente.
Ecco un paio di domande per te dopo aver finito di controllare la pagina man di ethtool :
- Cosa fa il
sudo ethtool -g eno1
comando? - E questo?
sudo ethtool -s eno1 speed 1000 duplex full autoneg on
L’utilità ethtool è un altro strumento che dovresti avere nel tuo set di strumenti.
Automatizza iperf3 con Python 3
Potresti notare che iperf3 ha una libreria che ti permette di integrare lo strumento con altri linguaggi, incluso Python:
[client ~]$ rpm -qil iperf3|rg libiperf
/usr/lib64/libiperf.so.0
/usr/lib64/libiperf.so.0.0.0
/usr/share/man/man3/libiperf.3.gz
Sono disponibili diversi collegamenti per Python:
- iperf3-python ha un’API per integrare iperf3 con Python, usando quei collegamenti.
- Il modulo Python ethtool è disponibile ma contrassegnato come deprecato, ma lo userò per ciò di cui ha bisogno questa dimostrazione.
Non tratterò qui l’API, ma piuttosto ti indirizzerò al codice sorgente di uno script Python che utilizza iperf3 ed ethtool per rilevare errori di rete (come ho fatto manualmente sopra). Puoi vederlo in esecuzione di seguito. Controlla il repository ed esegui lo script. Rimarrai stupito di quanto sia facile automatizzare alcune attività con Python.
Cosa puoi fare dopo?
L’apprendimento non si ferma mai, quindi ecco alcuni suggerimenti e osservazioni per farti andare avanti:
- Fasterdata ha più esempi di utilizzo di iperf con parametri diversi.
- Tieni presente che isolcpus è considerato deprecato e cpuset è consigliato. Fare riferimento a questa discussione sull’overflow dello stack per vedere come giocare con cpuset.
- Ora sai come scrivere i tuoi script di risoluzione dei problemi con l’API Python iperf3. Probabilmente dovresti scrivere un server iperf3 che possa mostrare i risultati usando un browser web (magari combinarlo con FastAPI ?).