La vitesse SSH s'est grandement améliorée via ProxyCommand - mais pourquoi?

14

La version TL; DR

Regardez cette distribution ASCII ou cette vidéo - puis trouvez les raisons pour lesquelles cela se produit. La description textuelle qui suit fournit plus de contexte.

Détails de l'installation

  • La machine 1 est un ordinateur portable Arch Linux, sur lequel sshest généré, se connectant à un SBC fonctionnant sous Armbian (un Orange PI Zero).
  • Le SBC lui-même est connecté via Ethernet à un routeur DSL et a une IP de 192.168.1.150
  • L'ordinateur portable est connecté au routeur via WiFi - à l'aide d'un dongle WiFi Raspberry PI officiel.
  • Il y a aussi un autre ordinateur portable (Machine 2) connecté via Ethernet au routeur DSL.

Topologie

Analyse comparative du lien avec iperf3

Lorsque comparé avec iperf3, le lien entre l'ordinateur portable et le SBC est inférieur aux 56 Mbits / s théoriques - comme prévu, car il s'agit d'une connexion WiFi dans un 2,4 GHz très fréquenté (immeuble à appartements) .

Plus précisément: après exécution iperf3 -ssur le SBC, les commandes suivantes sont exécutées sur l'ordinateur portable:

# 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

Donc, fondamentalement, le téléchargement vers le SBC atteint environ 24 Mo / s, et le téléchargement depuis ( -R) atteint 32 Mo / s.

Analyse comparative avec SSH

Cela étant, voyons comment s'en sort SSH. J'ai d'abord rencontré les problèmes qui ont conduit à ce message lors de l'utilisation rsyncet borgbackup- les deux utilisant SSH comme couche de transport ... Voyons donc comment SSH fonctionne sur le même lien:

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

Eh bien, c'est une vitesse abyssale! Beaucoup plus lent que la vitesse de liaison attendue ... (Dans le cas où vous n'êtes pas au courant pv -ptevar: il affiche le débit actuel et moyen des données qui le traversent. Dans ce cas, nous voyons que la lecture /dev/urandomet l'envoi des données via SSH au SBC atteint en moyenne 400 Ko / s, soit 3,2 Mo / s, un chiffre bien inférieur aux 24 Mo / s attendus.)

Pourquoi notre lien fonctionne-t-il à 13% de sa capacité?

Est-ce peut-être de notre /dev/urandomfaute?

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

Non, certainement pas.

Est-ce peut-être le SBC lui-même? Peut-être que c'est trop lent à traiter? Essayons d'exécuter la même commande SSH (c'est-à-dire l'envoi de données au SBC) mais cette fois à partir d'une autre machine (Machine 2) connectée via Ethernet:

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

Non, cela fonctionne bien - le démon SSH sur le SBC peut (facilement) gérer les 11 Mo / s (c'est-à-dire les 100 Mo / s) fournis par sa liaison Ethernet.

Et le CPU du SBC est-il chargé en faisant cela?

Le processeur le gère facilement

Nan.

Donc...

  • en termes de réseau (selon iperf3), nous devrions être en mesure de faire 10 fois la vitesse
  • notre CPU peut facilement supporter la charge
  • ... et nous n'impliquons aucun autre type d'E / S (par exemple des disques).

Qu'est-ce qui se passe?

Netcat et ProxyCommand à la rescousse

Essayons de simples anciennes netcatconnexions - fonctionnent-elles aussi vite que prévu?

Dans le SBC:

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

Dans l'ordinateur portable:

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

Ça marche! Et fonctionne à la vitesse attendue - bien meilleure, 10 fois meilleure -.

Alors, que se passe-t-il si j'exécute SSH à l'aide d'un ProxyCommand pour utiliser nc?

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

Travaux! Vitesse 10x.

Maintenant, je suis un peu confus - lorsque vous utilisez un "nu" nccomme Proxycommand, ne faites-vous pas exactement la même chose que SSH? c'est-à-dire créer un socket, se connecter au port 22 du SBC, puis pelleter le protocole SSH dessus?

Pourquoi y a-t-il cette énorme différence dans la vitesse résultante?

PS Ce n'était pas un exercice académique - ma borgsauvegarde s'exécute 10 fois plus vite à cause de cela. Je ne sais juste pas pourquoi :-)

EDIT : Ajout d'une "vidéo" du processus ici . En comptant les paquets envoyés à partir de la sortie d'ifconfig, il est clair que dans les deux tests, nous envoyons 40 Mo de données, les transmettant dans environ 30 000 paquets - juste beaucoup plus lentement lorsque vous ne les utilisez pas ProxyCommand.

ttsiodras
la source
tampon? Je pense qu'il ncutilise la mise en mémoire tampon de ligne, alors qu'il sshn'a pas de mise en mémoire tampon. Donc (ou si c'est le cas) le trafic ssh implique plus de paquets.
Ralph Rönnquist
Je ne suis pas un expert mais je pense que l'orange 0 n'a qu'un seul bus USB contrôlé par le processeur, le réseau passe par ce bus USB, le processeur doit créer un nombre aléatoire via un logiciel (il n'y a pas de puce sur ce type d'architecture qui le fait via matériel) et en même temps il y a le chiffrement ssh en cours et peut-être aussi la compression ssh. je n'ai pas vérifié tout cela, il est donc possible que je dis quelque chose de mal.
D'Arcy Nader
2
@ D'ArcyNader: Non, j'ai bien peur que tu te trompes. Tbe / dev / urandom se produit dans l'ordinateur portable (x86) - et j'ai fait le même test de la machine 2 parlant au SBC, atteignant des vitesses maximales (100 Mo / s), et prouvant ainsi que le SBC n'a aucun problème à gérer le trafic. Le problème ne se manifeste que lorsque SSH est utilisé à partir de l'ordinateur portable - et lorsque je modifie l'invocation SSH (encore une fois, côté ordinateur portable) pour utiliser netcat - donc toujours en dev / urandom et en canalisant toutes les données - le problème disparaît. Et BTW, le bus USB unique est un problème des Raspberry PI - pas des Orange PI.
ttsiodras
je suis désolé si je ne t'ai pas aidé. et merci pour la clarification.
D'Arcy Nader
@ RalphRönnquist: Le cas d'utilisation d'origine qui m'a conduit dans ce trou de lapin sauvegardait les choses via rsync et borgbackup. De nombreux outils utilisent SSH comme mécanisme de transport - et dans mon cas, ils en ont souffert. Si ce que je ressens est, en effet, le comportement SSH "standard", alors je m'attendrais à ce que la soumission de demandes d'extraction à tous les outils de sauvegarde pour générer SSH via un netcat ProxyCommand accélérerait instantanément les sauvegardes partout sur la planète! Je ne peux pas croire que j'ai fait une découverte aussi "énorme" :-) autre chose doit se passer ici.
ttsiodras

Réponses:

14

Un grand merci aux personnes qui ont soumis des idées dans les commentaires. Je les ai tous passés en revue:

Enregistrement de paquets avec tcpdump et comparaison du contenu dans WireShark

# tcpdump -i wlan0 -w good.ssh & \
     cat signature | ssh -o "ProxyCommand nc %h %p" \
        [email protected] 'cat | md5sum' ; \
     killall tcpdump
# tcpdump -i wlan0 -w bad.ssh & \
     cat signature | ssh [email protected] 'cat | md5sum' ; \
     killall tcpdump

Il n'y avait aucune différence d'importance dans les paquets enregistrés.

Vérification de la mise en forme du trafic

Je n'en avais aucune idée - mais après avoir regardé la page de manuel "tc", j'ai pu vérifier que

  • tc filter show ne renvoie rien
  • tc class show ne renvoie rien
  • tc qdisc show

... les renvoie:

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 

... qui ne semblent pas faire la différence entre "ssh" et "nc" - en fait, je ne suis même pas sûr que la mise en forme du trafic puisse fonctionner au niveau du processus (je m'attendrais à ce qu'il fonctionne sur les adresses / ports / différenciés Champ Services dans l'en-tête IP).

Debian Chroot, pour éviter une potentielle "intelligence" dans le client Arch Linux SSH

Non, mêmes résultats.

Enfin - Nagle

Exécution d'une séquence dans l'expéditeur ...

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

... et en regardant ce qui se passe exactement sur le socket qui transmet les données, j'ai remarqué cette "configuration" avant le début de la transmission:

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>

Cela configure le socket SSH pour désactiver l'algorithme de Nagle. Vous pouvez Google et tout lire à ce sujet - mais ce que cela signifie, c'est que SSH donne la priorité à la réactivité sur la bande passante - il demande au noyau de transmettre immédiatement tout ce qui est écrit sur cette socket et de ne pas "retarder" l'attente des accusés de réception de la télécommande.

Cela signifie, en termes simples, que dans sa configuration par défaut, SSH n'est PAS un bon moyen de transporter des données à travers - pas lorsque le lien utilisé est lent (ce qui est le cas pour de nombreux liens WiFi). Si nous envoyons des paquets en direct qui sont "principalement des en-têtes", la bande passante est gaspillée!

Pour prouver que c'était bien le coupable, j'ai utilisé LD_PRELOAD pour "supprimer" cet appel système spécifique:

$ 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 [email protected] 'cat >/dev/null'
No, Mr Nagle stays.
No, Mr Nagle stays.
 100MiB 0:00:29 [3.38MiB/s] [3.38MiB/s] [================================>] 100%   

Là - vitesse parfaite (enfin, aussi rapide que iperf3).

Moral de l'histoire

N'abandonnez jamais :-)

Et si vous utilisez des outils comme rsyncou borgbackupqui transportent leurs données sur SSH, et que votre lien est lent, essayez d'empêcher SSH de désactiver Nagle (comme illustré ci-dessus) - ou utilisez ProxyCommandpour basculer SSH pour vous connecter via nc. Cela peut être automatisé dans votre $ 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
...

... afin que toutes les utilisations futures de "orangepi" comme hôte cible dans ssh / rsync / borgbackup soient désormais utilisées ncpour se connecter (et donc laisser Nagle tranquille).

ttsiodras
la source
Merci, vous m'avez sauvé la vie! Avez-vous essayé de contacter les gens de ssh pour comprendre pourquoi il n'y a pas de paramètre pour contrôler cela?
static_rtti
1
Je suis heureux que mes découvertes vous aient également aidé! Quant à contacter les gens SSH, j'ai essayé, oui - mais rien ne s'est finalement produit: bugzilla.mindrot.org/show_bug.cgi?id=2848
ttsiodras
Je me suis ajouté au bogue. Qui sait, quelque chose pourrait éventuellement arriver! Grande enquête, en tout cas.
static_rtti