La velocità SSH è notevolmente migliorata tramite ProxyCommand - ma perché?


14

La versione TL; DR

Guarda questo cast ASCII o questo video , quindi trova tutti i motivi per cui questo sta accadendo. La descrizione del testo che segue fornisce più contesto.

Dettagli della configurazione

  • Machine 1 è un laptop Arch Linux, su cui sshviene generato, che si collega a un SBC con esecuzione Armbian (un Orange PI Zero).
  • Lo stesso SBC è collegato via Ethernet a un router DSL e ha un IP di 192.168.1.150
  • Il laptop è collegato al router tramite WiFi, utilizzando un dongle WiFi ufficiale Raspberry PI.
  • C'è anche un altro laptop (Machine 2) collegato via Ethernet al router DSL.

Topologia

Benchmarking del collegamento con iperf3

Se confrontato con iperf3, il collegamento tra laptop e SBC è inferiore ai teorici 56 MBits / sec - come previsto, poiché si tratta di una connessione WiFi all'interno di un "affollato 2.4GHz" (condominio) .

Più precisamente: dopo l'esecuzione iperf3 -ssull'SBC, i seguenti comandi vengono eseguiti sul laptop:

# iperf3 -c 192.168.1.150
Connecting to host 192.168.1.150, port 5201
[  5] local 192.168.1.89 port 57954 connected to 192.168.1.150 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  2.99 MBytes  25.1 Mbits/sec    0    112 KBytes       
...
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  28.0 MBytes  23.5 Mbits/sec    5             sender
[  5]   0.00-10.00  sec  27.8 MBytes  23.4 Mbits/sec                  receiver

iperf Done.

# iperf3 -c 192.168.1.150 -R
Connecting to host 192.168.1.150, port 5201
Reverse mode, remote host 192.168.1.150 is sending
[  5] local 192.168.1.89 port 57960 connected to 192.168.1.150 port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  3.43 MBytes  28.7 Mbits/sec                  
...                
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  39.2 MBytes  32.9 Mbits/sec  375             sender
[  5]   0.00-10.00  sec  37.7 MBytes  31.6 Mbits/sec                  receiver

Quindi, fondamentalmente, il caricamento su SBC raggiunge circa 24 MB / sec e il download da esso ( -R) raggiunge 32 MB / sec.

Benchmarking con SSH

Detto questo, vediamo come tariffe SSH. Ho riscontrato per la prima volta i problemi che hanno portato a questo post durante l'utilizzo rsynce borgbackup- entrambi utilizzano SSH come layer di trasporto ... Vediamo quindi come SSH si comporta sullo stesso link:

# cat /dev/urandom | \
    pv -ptebar | \
    ssh  root@192.168.1.150 'cat >/dev/null'
20.3MiB 0:00:52 [ 315KiB/s] [ 394KiB/s]

Bene, questa è una velocità spaventosa! Molto più lento della velocità di collegamento prevista ... (Nel caso in cui non ne sia a conoscenza pv -ptevar: visualizza la velocità corrente e media dei dati che la attraversano. In questo caso, vediamo che leggere /dev/urandome inviare i dati tramite SSH all'SBC raggiunge in media 400 KB / s, ovvero 3,2 MB / sec, una cifra di gran lunga inferiore rispetto ai 24 MB / sec previsti)

Perché il nostro link funziona al 13% della sua capacità?

Forse /dev/urandomè colpa nostra ?

# cat /dev/urandom | pv -ptebar > /dev/null
834MiB 0:00:04 [ 216MiB/s] [ 208MiB/s]

No, assolutamente no.

È forse lo stesso SBC? Forse è troppo lento per l'elaborazione? Proviamo a eseguire lo stesso comando SSH (ovvero l'invio di dati all'SBC) ma questa volta da un'altra macchina (Macchina 2) connessa tramite Ethernet:

# cat /dev/urandom | \
    pv -ptebar | \
    ssh  root@192.168.1.150 'cat >/dev/null'
240MiB 0:00:31 [10.7MiB/s] [7.69MiB/s] 

No, funziona bene - il demone SSH su SBC può (facilmente) gestire gli 11 MByte / sec (cioè i 100 MBit / sec) forniti dal suo collegamento Ethernet.

E la CPU dell'SBC viene caricata mentre lo fa?

La CPU lo gestisce facilmente

No.

Così...

  • per quanto riguarda la rete (come da iperf3) dovremmo essere in grado di fare 10 volte la velocità
  • la nostra CPU può sopportare facilmente il carico
  • ... e non coinvolgiamo nessun altro tipo di I / O (ad es. unità).

Cosa diavolo sta succedendo?

Netcat e ProxyCommand in soccorso

Proviamo semplici vecchie netcatconnessioni: funzionano alla velocità che ci aspetteremmo?

Nell'SBC:

# nc -l -p 9988 | pv -ptebar > /dev/null

Nel laptop:

# cat /dev/urandom | pv -ptebar | nc 192.168.1.150 9988
117MiB 0:00:33 [3.82MiB/s] [3.57MiB/s] 

Funziona! E funziona alla velocità prevista - molto meglio, 10 volte migliore -.

Quindi cosa succede se eseguo SSH usando un ProxyCommand per usare nc?

# cat /dev/urandom | \
    pv -ptebar | \
    ssh -o "Proxycommand nc %h %p" root@192.168.1.150 'cat >/dev/null'
101MiB 0:00:30 [3.38MiB/s] [3.33MiB/s]

Lavori! 10x velocità.

Ora sono un po 'confuso - quando usi un "nudo" nccome Proxycommand, non stai fondamentalmente facendo esattamente la stessa cosa che fa SSH? cioè creare un socket, connettersi alla porta 22 dell'SBC e spalare il protocollo SSH su di esso?

Perché c'è questa enorme differenza nella velocità risultante?

PS Questo non è stato un esercizio accademico - il mio borgbackup viene eseguito 10 volte più veloce per questo motivo. Solo non so perché :-)

EDIT : Aggiunto un "video" del processo qui . Contando i pacchetti inviati dall'output di ifconfig, è chiaro che in entrambi i test stiamo inviando 40 MB di dati, trasmettendoli in pacchetti di circa 30 KB - solo molto più lenti quando non vengono utilizzati ProxyCommand.


il buffering? Penso che ncusa il buffering di linea, mentre sshnon ha buffering. Quindi (o in tal caso) il traffico ssh coinvolge più pacchetti.
Ralph Rönnquist,

non sono un esperto ma penso che orange 0 abbia un solo bus usb controllato da cpu, la rete passa attraverso quel bus usb, la cpu deve creare un numero casuale via software (non c'è chip su quel tipo di architettura che lo fa tramite hardware) e allo stesso tempo è in corso il cifrario ssh e forse anche la compressione ssh. non ho controllato tutto questo, quindi è possibile che sto dicendo qualcosa di sbagliato.
D'Arcy Nader,

2
@ D'ArcyNader: No, temo che tu abbia sbagliato. Tbe / dev / urandom si verifica nel laptop (x86) - e ho fatto lo stesso test da Machine 2 parlando all'SBC, raggiungendo le velocità massime (100 MB / sec) e dimostrando così che l'SBC non ha problemi a gestire il traffico. Il problema si manifesta solo quando SSH viene utilizzato dal laptop - e quando cambio l'invocazione di SSH (di nuovo, sul lato laptop) per utilizzare netcat - quindi ancora facendo dev / urandom e ancora piping tutti i dati - il problema scompare. E a proposito, il singolo bus USB è un problema dei Raspberry PI, non degli Orange PI.
ttsiodras,

mi dispiace se non ti ho aiutato. e grazie per il chiarimento.
D'Arcy Nader,

@ RalphRönnquist: il caso d'uso originale che mi ha portato in questa tana del coniglio è stato il backup delle cose su rsync e borgbackup. Molti strumenti usano SSH come meccanismo di trasporto e, nel mio caso, ne sono stati vittime. Se quello che sto sperimentando è, in effetti, il comportamento "standard" di SSH, allora mi aspetto che l'invio di richieste pull a tutti gli strumenti di backup per generare SSH tramite un ProxyCommand netcat acceleri immediatamente i backup in tutto il pianeta! Non riesco a credere di aver fatto una scoperta così "enorme" :-) qualcosa deve succedere qui.
ttsiodras,

Risposte:


14

Mille grazie alle persone che hanno inviato idee nei commenti. Li ho esaminati tutti:

Registrazione di pacchetti con tcpdump e confronto dei contenuti in WireShark

# tcpdump -i wlan0 -w good.ssh & \
     cat signature | ssh -o "ProxyCommand nc %h %p" \
        root@192.168.1.150 'cat | md5sum' ; \
     killall tcpdump
# tcpdump -i wlan0 -w bad.ssh & \
     cat signature | ssh root@192.168.1.150 'cat | md5sum' ; \
     killall tcpdump

Non vi era alcuna differenza di importanza nei pacchetti registrati.

Verifica della modellatura del traffico

Non ne avevo idea - ma dopo aver visto la manpage "tc", sono stato in grado di verificarlo

  • tc filter show non restituisce nulla
  • tc class show non restituisce nulla
  • tc qdisc show

... restituisce questi:

qdisc noqueue 0: dev lo root refcnt 2
qdisc noqueue 0: dev docker0 root refcnt 2
qdisc fq_codel 0: dev wlan0 root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn 

... che non sembrano distinguere tra "ssh" e "nc" - in effetti, non sono nemmeno sicuro che il traffic shaping possa operare a livello di processo (mi aspetto che funzioni su indirizzi / porte / Differenziato Campo servizi nell'intestazione IP).

Debian Chroot, per evitare la potenziale "intelligenza" nel client SSH Arch Linux

No, stessi risultati.

Finalmente - Nagle

Esecuzione di una sequenza nel mittente ...

pv data | strace -T -ttt -f ssh 192.168.1.150 'cat | md5sum' 2>bad.log

... e guardando cosa succede esattamente sul socket che trasmette i dati, ho notato questa "configurazione" prima che inizi la trasmissione effettiva:

1522665534.007805 getsockopt(3, SOL_TCP, TCP_NODELAY, [0], [4]) = 0 <0.000025>
1522665534.007899 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000021>

Questo imposta il socket SSH per disabilitare l'algoritmo di Nagle. Puoi Google e leggere tutto a riguardo - ma ciò significa che SSH sta dando priorità alla reattività rispetto alla larghezza di banda - indica al kernel di trasmettere immediatamente qualsiasi cosa scritta su questo socket e non "ritardare" in attesa di conferme dal telecomando.

Ciò significa, in parole povere, che nella sua configurazione predefinita SSH NON è un buon modo per trasportare i dati attraverso - non quando il collegamento utilizzato è lento (come nel caso di molti collegamenti WiFi). Se inviamo pacchetti via etere che sono "principalmente intestazioni", la larghezza di banda viene sprecata!

Per dimostrare che questo era davvero il colpevole, ho usato LD_PRELOAD per "eliminare" questo syscall specifico:

$ cat force_nagle.c

#include <stdio.h>
#include <dlfcn.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>

int (*osetsockopt) (int socket, int level, int option_name,
           const void *option_value, socklen_t option_len) = NULL;

int setsockopt(int socket, int level, int option_name,
           const void *option_value, socklen_t option_len)
{
    int ret;
    if (!osetsockopt) {
        osetsockopt = dlsym(RTLD_NEXT, "setsockopt");
    }

    if (option_name == TCP_NODELAY) {
        puts("No, Mr Nagle stays.");
        return 0;
    }
    ret = osetsockopt(socket, level, option_name, option_value, option_len);
    return ret;
}

$ gcc -fPIC -D_GNU_SOURCE -shared -o force_nagle.so force_nagle.c -ldl

$ pv /dev/shm/data | LD_PRELOAD=./force_nagle.so ssh root@192.168.1.150 'cat >/dev/null'
No, Mr Nagle stays.
No, Mr Nagle stays.
 100MiB 0:00:29 [3.38MiB/s] [3.38MiB/s] [================================>] 100%   

Ecco - velocità perfetta (beh, veloce come iperf3).

Morale della storia

Non mollare mai :-)

E se usi strumenti come rsynco borgbackupche trasportano i loro dati su SSH e il tuo collegamento è lento, prova a impedire a SSH di disabilitare Nagle (come mostrato sopra) - o usare ProxyCommandper cambiare SSH per connetterti tramite nc. Questo può essere automatizzato nel tuo $ HOME / .ssh / config:

$ cat .ssh/config
...
Host orangepi
    Hostname 192.168.1.150
    User root
    Port 22
    # Compression no
    # Cipher None
    ProxyCommand nc %h %p
...

... in modo che tutti gli usi futuri di "orangepi" come host di destinazione in ssh / rsync / borgbackup verranno d'ora in poi utilizzati ncper connettersi (e quindi lasciare solo Nagle).


Grazie, mi hai salvato la vita! Hai provato a contattare la gente ssh per capire perché non esiste un'impostazione per controllare questo?
static_rtti

1
Sono contento che anche le mie scoperte ti abbiano aiutato! Per quanto riguarda il contatto con la gente SSH, ho provato, sì - ma alla fine non è successo nulla: bugzilla.mindrot.org/show_bug.cgi?id=2848
ttsiodras

Mi sono aggiunto al bug. Chissà, alla fine potrebbe succedere qualcosa! Grande indagine, in ogni caso.
static_rtti
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.