La velocidad SSH mejoró enormemente a través de ProxyCommand, pero ¿por qué?

14

La versión TL; DR

Mire este elenco de ASCII o este video , luego descubra cualquier razón por la que esto está sucediendo. La descripción del texto que sigue proporciona más contexto.

Detalles de la configuración

  • Machine 1 es una computadora portátil Arch Linux, en la cual ssh se genera, conectándose a un SBC que funciona con Armbian (un Orange PI Zero).
  • El SBC está conectado a través de Ethernet a un enrutador DSL y tiene una IP de 192.168.1.150
  • La computadora portátil está conectada al enrutador a través de WiFi, utilizando un dongle oficial Raspberry PI WiFi.
  • También hay otra computadora portátil (Máquina 2) conectada a través de Ethernet al enrutador DSL.

Topología

Evaluación comparativa del enlace con iperf3

Cuando se compara con iperf3, el enlace entre la computadora portátil y el SBC es inferior a los 56 MBits / seg teóricos, como se esperaba, ya que se trata de una conexión WiFi dentro de un " edificio de apartamentos " muy "abarrotado de 2,4 GHz" .

Más específicamente: después de ejecutar iperf3 -sen el SBC, los siguientes comandos se ejecutan en la computadora portátil:

# 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

Básicamente, la carga en el SBC alcanza aproximadamente 24 MBits / seg, y la descarga desde este ( -R) alcanza los 32 MBits / seg.

Benchmarking con SSH

Dado eso, veamos cómo le va a SSH. La primera vez que experimenté los problemas que llevaron a esta publicación fue cuando usé rsyncy borgbackup, ambos usaron SSH como capa de transporte ... Entonces, veamos cómo SSH se desempeña en el mismo enlace:

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

Bueno, eso es una velocidad abismal! Mucho más lento que la velocidad de enlace esperada ... (En caso de que no lo sepa pv -ptevar: muestra la velocidad actual y promedio de datos que lo atraviesan. En este caso, vemos que leer /dev/urandomy enviar los datos a través de SSH al SBC en promedio alcanza los 400 KB / s, es decir, 3.2 MBits / seg, una cifra mucho menor que los 24 MBits / seg esperados).

¿Por qué nuestro enlace funciona al 13% de su capacidad?

¿Acaso /dev/urandomes culpa nuestra ?

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

No, definitivamente no.

¿Es quizás el SBC mismo? ¿Quizás es demasiado lento para procesar? Intentemos ejecutar el mismo comando SSH (es decir, enviar datos al SBC) pero esta vez desde otra máquina (Máquina 2) que está conectada a través de Ethernet:

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

No, esto funciona bien: el demonio SSH en el SBC puede (fácilmente) manejar los 11 MB / s (es decir, los 100 MB / s) que proporciona su enlace Ethernet.

¿Y se carga la CPU del SBC al hacer esto?

CPU lo maneja fácilmente

No

Entonces...

  • En cuanto a la red (según iperf3 ), deberíamos poder hacer 10 veces la velocidad
  • nuestra CPU puede acomodar fácilmente la carga
  • ... y no involucramos ningún otro tipo de E / S (por ejemplo, unidades).

¿Qué diablos está pasando?

Netcat y ProxyCommand al rescate

Probemos simplemente viejo netcat conexiones : ¿funcionan tan rápido como podríamos esperar?

En el SBC:

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

En la laptop:

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

¡Funciona! Y funciona a la velocidad esperada, mucho mejor, 10 veces mejor.

Entonces, ¿qué sucede si ejecuto SSH usando un ProxyCommand para usar 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]

¡Trabajos! 10x velocidad.

Ahora estoy un poco confundido, cuando uso un "desnudo" nccomoProxycommand , ¿no estás haciendo exactamente lo mismo que hace SSH? es decir, ¿crear un socket, conectarse al puerto 22 del SBC y luego traspasar el protocolo SSH?

¿Por qué hay esta gran diferencia en la velocidad resultante?

PD: Este no fue un ejercicio académico : mi borgcopia de seguridad se ejecuta 10 veces más rápido debido a esto. Simplemente no sé por qué :-)

EDITAR : Se agregó un "video" del proceso aquí . Contando los paquetes enviados desde la salida de ifconfig, está claro que en ambas pruebas estamos enviando 40MB de datos, transmitiéndolos en aproximadamente 30K paquetes, solo que mucho más lento cuando no se usa ProxyCommand.

ttsiodras
fuente
amortiguamiento? Creo que ncusa buffering de línea, mientras que sshno tiene buffering. Entonces (o si es así) el tráfico ssh involucra más paquetes.
Ralph Rönnquist
No soy un experto, pero creo que el naranja 0 solo tiene un bus USB controlado por CPU, la red pasa por ese bus USB, la CPU tiene que crear un número aleatorio a través del software (no hay chip en ese tipo de arquitectura que lo hace a través de hardware) y al mismo tiempo existe el cifrado ssh en curso y tal vez la compresión ssh también. No verifiqué todo esto, así que es posible que esté diciendo algo mal.
D'Arcy Nader
2
@ D'ArcyNader: No, me temo que te equivocaste. Tbe / dev / urandom ocurre en la computadora portátil (x86), e hice la misma prueba desde la máquina 2 hablando con el SBC, alcanzando velocidades máximas (100 MBits / seg), y demostrando que el SBC no tiene problemas para lidiar con el tráfico. El problema solo se manifiesta cuando se usa SSH desde la computadora portátil, y cuando cambio la invocación de SSH (de nuevo, en el lado de la computadora portátil) para usar netcat, de modo que sigo haciendo dev / urandom y aún almacenando todos los datos, el problema desaparece. Y, por cierto, el bus USB único es un problema de los Raspberry PI, no de los Orange PI.
ttsiodras
lo siento si no te ayude. y gracias por la aclaración
D'Arcy Nader
@ RalphRönnquist: El caso de uso original que me llevó por este agujero de conejo fue respaldar las cosas a través de rsync y borgbackup. Muchas herramientas usan SSH como mecanismo de transporte, y en mi caso, sufrí por esto. Si lo que estoy experimentando es, de hecho, el comportamiento SSH "estándar", ¡entonces esperaría que enviar solicitudes de extracción a todas las herramientas de respaldo para generar SSH a través de un Netcat ProxyCommand aceleraría instantáneamente los respaldos en todo el planeta! No puedo creer que haya hecho un descubrimiento tan "enorme" :-) algo más debe estar sucediendo aquí.
ttsiodras

Respuestas:

14

Muchas gracias a las personas que enviaron ideas en los comentarios. Los revisé todos:

Grabar paquetes con tcpdump y comparar los contenidos en 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

No hubo diferencias de importancia en los paquetes grabados.

Comprobación de la conformación del tráfico

No tenía idea de esto, pero después de mirar la página de manual "tc", pude verificar que

  • tc filter show no devuelve nada
  • tc class show no devuelve nada
  • tc qdisc show

... devuelve estos:

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 

... que no parecen diferenciar entre "ssh" y "nc"; de hecho, ni siquiera estoy seguro de si la conformación del tráfico puede funcionar en el nivel del proceso (espero que funcione en direcciones / puertos / diferenciados Campo de servicios en encabezado IP).

Debian Chroot, para evitar la posible "inteligencia" en el cliente SSH de Arch Linux

No, los mismos resultados.

Finalmente - Nagle

Realizando un strace en el remitente ...

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

... y mirando exactamente qué sucede en el socket que transmite los datos, noté esta "configuración" antes de que comience la transmisión real:

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>

Esto configura el socket SSH para deshabilitar el algoritmo de Nagle. Puede buscar en Google y leer todo al respecto, pero lo que significa es que SSH está dando prioridad a la capacidad de respuesta sobre el ancho de banda: le indica al núcleo que transmita todo lo escrito en este socket de inmediato y no "demore" la espera de acuses de recibo desde el control remoto.

Lo que esto significa, en términos simples, es que en su configuración predeterminada, SSH NO es una buena forma de transportar datos, no cuando el enlace utilizado es lento (como es el caso de muchos enlaces WiFi). Si estamos enviando paquetes por el aire que son "en su mayoría encabezados", ¡se desperdicia el ancho de banda!

Para demostrar que este fue realmente el culpable, usé LD_PRELOAD para "soltar" esta llamada al sistema específica:

$ 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%   

Ahí: velocidad perfecta (bueno, tan rápido como iperf3).

Moral de la historia

Nunca te rindas :-)

Y si utiliza herramientas como rsynco borgbackupque transportan sus datos a través de SSH, y su enlace es lento, intente evitar que SSH desactive Nagle (como se muestra arriba), o use ProxyCommandpara cambiar SSH para conectarse nc. Esto se puede automatizar en su $ 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
...

... para que todos los usos futuros de "orangepi" como host de destino en ssh / rsync / borgbackup de ahora en adelante se usen ncpara conectarse (y, por lo tanto, dejar a Nagle solo).

ttsiodras
fuente
Gracias, me salvaste la vida! ¿Has intentado contactar a la gente de ssh para entender por qué no hay una configuración para controlar esto?
static_rtti
1
¡Me alegra que mis hallazgos también te hayan ayudado! En cuanto a contacto con la gente de SSH, he intentado, sí - pero no pasó nada, al final: bugzilla.mindrot.org/show_bug.cgi?id=2848
ttsiodras
Me agregué al error. Quién sabe, ¡algo podría pasar eventualmente! Gran investigación, en cualquier caso.
static_rtti