Velocidade SSH melhorada através do ProxyCommand - mas por quê?

12

A versão TL; DR

Veja este elenco ASCII ou this video - então, indique as razões pelas quais isso está acontecendo. A descrição do texto a seguir fornece mais contexto.

Detalhes da configuração

  • A Máquina 1 é um laptop Arch Linux, no qual ssh é gerada, conectando-se a um SBC com execução no Armbian (um Orange PI Zero).
  • O próprio SBC está conectado via Ethernet a um roteador DSL e tem um IP de 192.168.1.150
  • O laptop está conectado ao roteador por WiFi - usando um dongle oficial do Raspberry PI WiFi.
  • Há também outro laptop (Máquina 2) conectado via Ethernet ao roteador DSL.

Façaobenchmarkingdolinkcomiperf3

Quandocomparadacomiperf3,olinkentreolaptopeoSBCémenordoqueos56MBitsteóricos/segundo-comoesperado,jáqueestaéumaconexãoWiFidentrodeum"2.4GHz" muito cheio edifício) .

Mais especificamente: depois de executar iperf3 -s no SBC, os seguintes comandos são executados no 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

Então, basicamente, o upload para o SBC atinge cerca de 24 MBits / segundo, e o download dele ( -R ) alcança 32MBits / seg.

Comparativo com o SSH

Dado isso, vamos ver como o SSH é tarifado. Eu experimentei pela primeira vez os problemas que levaram a este post ao usar rsync e borgbackup - ambos usando o SSH como uma camada de transporte ... Então, vamos ver como o SSH funciona no mesmo link:

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

Bem, essa é uma velocidade abismal! Muito mais lento que a velocidade de link esperada ... (Caso você não saiba de pv -ptevar : exibe a taxa atual e média de dados passando por ele. Nesse caso, vemos que ler /dev/urandom e enviar os dados por SSH para o SBC é em média, chegando a 400KB / s - isto é, 3,2MBits / seg, um número muito menor que os esperados 24MBits / seg.)

Por que nosso link está funcionando com 13% de sua capacidade?

É talvez nossa culpa do /dev/urandom ?

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

Não, definitivamente não.

É talvez o próprio SBC? Talvez seja muito lento para processar? Vamos tentar executar o mesmo comando SSH (ou seja, enviar dados para o SBC), mas desta vez de outra máquina (Máquina 2) conectada pela Ethernet:

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

Não, isso funciona bem - o daemon SSH no SBC pode (facilmente) manipular os 11MBytes / seg (isto é, os 100MBits / seg) que o link Ethernet fornece.

E a CPU do SBC é carregada ao fazer isso?

Não.

Então...

  • emtermosderede(deacordocomiperf3),poderemosfazer10xavelocidade
  • nossaCPUpodeacomodarfacilmenteacarga
  • ...enãoenvolvemosnenhumoutrotipodeE/S(porexemplo,drives).

Oquediabosestáacontecendo?

NetcateProxyCommandparaoresgate

Vamostentarasantigasconexõesnetcat-elasfuncionamtãorápidoquantoesperávamos?

NoSBC:

#nc-l-p9988|pv-ptebar>/dev/null

Nolaptop:

#cat/dev/urandom|pv-ptebar|nc192.168.1.1509988117MiB0:00:33[3.82MiB/s][3.57MiB/s]

Funciona!Ecorrenavelocidadeesperada-muitomelhor,10xmelhor-.

Então,oqueaconteceseeuexecutaroSSHusandoumProxyCommandparausaronc?

#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]

Funciona! Velocidade 10x.

Agora estou um pouco confuso - ao usar um "naked" nc como Proxycommand , você não está basicamente fazendo exatamente a mesma coisa que o SSH faz? isto é, criando um soquete, conectando-se à porta 22 do SBC e, em seguida, removendo o protocolo SSH sobre ele?

Por que existe essa enorme diferença na velocidade resultante?

P.S. Este não foi um exercício acadêmico - meu borg backup é executado 10 vezes mais rápido por causa disso. Eu só não sei porque: -)

EDITAR : Adicionado um "vídeo" do processo aqui . Contando os pacotes enviados da saída do ifconfig, fica claro que em ambos os testes estamos enviando 40MB de dados, transmitindo-os em aproximadamente 30K pacotes - apenas muito mais lento quando não usamos ProxyCommand .

    
por ttsiodras 01.04.2018 / 14:03

1 resposta

13

Muito obrigado às pessoas que enviaram ideias nos comentários. Eu passei por todos eles:

Gravando pacotes com o tcpdump e comparando o conteúdo no 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

Não houve diferença de importância nos pacotes gravados.

Verificando a modelagem de tráfego

Não tinha ideia sobre isso, mas depois de analisar a página "tc", consegui verificar que

  • tc filter show não retorna nada
  • tc class show não retorna nada
  • tc qdisc show

... retorna estes:

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 não parecem diferenciar entre "ssh" e "nc" - na verdade, eu nem tenho certeza se o traffic shaping pode operar no nível do processo (eu esperaria que funcionasse em endereços / Portas / Serviços diferenciados no cabeçalho IP).

Debian Chroot, para evitar potencial "esperteza" no cliente SSH Arch Linux

Não, os mesmos resultados.

Finalmente - Nagle

Executando uma strace no remetente ...

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

... e observando o que exatamente acontece no soquete que transmite os dados, notei essa "configuração" antes do início da transmissão:

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>

Isso configura o soquete SSH para desabilitar o algoritmo de Nagle. Você pode pesquisar no Google e ler tudo sobre ele - mas o que significa é que o SSH está dando prioridade à capacidade de resposta sobre a largura de banda - instrui o kernel a transmitir qualquer coisa escrita neste soquete imediatamente e não "atrasar" aguardando confirmações do controle remoto. / p>

O que isto significa, em termos simples, é que na sua configuração padrão, o SSH NÃO é uma boa maneira de transportar dados - não quando o link usado é lento (o que é o caso de muitos links WiFi). Se estamos enviando pacotes pelo ar que são "principalmente cabeçalhos", a largura de banda é desperdiçada!

Para provar que esse foi realmente o culpado, usei LD_PRELOAD para "descartar" este syscall específico:

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

Há - velocidade perfeita (bem, tão rápido quanto iperf3).

Moral da história

Nunca desista: -)

E se você usar ferramentas como rsync ou borgbackup que transportam seus dados por SSH, e seu link for lento, tente parar o SSH de desativar o Nagle (como mostrado acima) - ou usar ProxyCommand para mude o SSH para conectar via nc . Isso pode ser automatizado em seu $ 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 os usos futuros de "orangepi" como host de destino em ssh / rsync / borgbackup usem a partir de agora nc para se conectar (e, portanto, deixar Nagle em paz).

    
por 01.04.2018 / 23:09