A velocidade do SSH melhorou bastante via ProxyCommand - mas por quê?

14

A versão TL; DR

Assista a este elenco ASCII ou a este vídeo - depois, encontre as razões pelas quais isso está acontecendo. A descrição de texto a seguir fornece mais contexto.

Detalhes da configuração

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

Topologia

Comparando o link com o iperf3

Quando comparado iperf3, o vínculo entre o laptop e o SBC é inferior aos 56 MBits / s teóricos - como esperado, pois essa é uma conexão Wi-Fi em um edifício de apartamentos de 2,4 GHz muito lotado .

Mais especificamente: após a execução iperf3 -sno 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 / s, e o download a partir dele ( -R) atinge 32 MBits / s.

Benchmarking com SSH

Dado isso, vamos ver como o SSH se sai. Eu experimentei os problemas que levaram a este post ao usar rsynce borgbackup- ambos usando 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 esperada do link ... (Caso você não esteja ciente pv -ptevar: ele exibe a taxa atual e média de dados passando por ele. Nesse caso, vemos que a leitura /dev/urandome o envio de dados via SSH para o SBC está atingindo, em média, 400 KB / s - ou seja, 3,2 MBits / s, um número bem menor que os 24 MBits / s esperados.)

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

Talvez seja /dev/urandomculpa nossa ?

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

Não, definitivamente não.

Talvez seja 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 a partir 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 11 MBytes / s (ou seja, os 100 MBits / s) fornecidos pelo link Ethernet.

E a CPU do SBC é carregada ao fazer isso?

CPU é facilmente manipulá-lo

Não.

Então...

  • Em termos de rede (conforme iperf3), devemos ser capazes de fazer 10x a velocidade
  • nossa CPU pode acomodar facilmente a carga
  • ... e não envolvemos nenhum outro tipo de E / S (por exemplo, unidades).

O que diabos está acontecendo?

Netcat e ProxyCommand para o resgate

Vamos tentar netcatconexões simples e antigas - elas funcionam tão rápido quanto o esperado?

No SBC:

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

No laptop:

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

Funciona! E roda na velocidade esperada - muito melhor, 10x melhor -.

Então, o que acontece se eu executar o SSH usando um 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]

Trabalho! 10x velocidade.

Agora estou um pouco confuso - ao usar um "nu" nccomo um Proxycommand, você não está basicamente fazendo exatamente a mesma coisa que o SSH? ou seja, criando um soquete, conectando-se à porta 22 da SBC e depois colocando o protocolo SSH sobre ela?

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

PS: Este não foi um exercício acadêmico - meu borgbackup é executado 10 vezes mais rápido por causa disso. Só não sei por que :-)

EDIT : Adicionado um "vídeo" do processo aqui . Contando os pacotes enviados a partir da saída do ifconfig, é claro que em ambos os testes estamos enviando 40 MB de dados, transmitindo-os em aproximadamente 30K pacotes - apenas muito mais devagar quando não estiver em uso ProxyCommand.

ttsiodras
fonte
carregando? Eu acho que ncusa buffer de linha, enquanto sshque não tem buffer. Portanto (ou se houver), o tráfego ssh envolve mais pacotes.
Ralph Rönnquist 01/04/19
Eu não sou especialista, mas acho que o orange 0 tem apenas um barramento USB controlado pela CPU, a rede passa por esse barramento USB, a CPU precisa criar um número aleatório via software (não há chip nesse tipo de arquitetura que faça isso via hardware) e ao mesmo tempo há o código ssh em andamento e talvez a compressão ssh também. Eu não verifiquei tudo isso, então é possível que eu esteja dizendo algo errado.
D'Arcy Nader
2
@ D'ArcyNader: Não, acho que você entendeu errado. O / dev / urandom acontece no laptop (x86) - e eu fiz o mesmo teste da Máquina 2 falando com a SBC, atingindo velocidades máximas (100 MBits / s) e, assim, provando que a SBC não tem problemas em lidar com o tráfego. O problema só se manifesta quando o SSH é usado no laptop - e quando altero a invocação do SSH (novamente, no lado do laptop) para usar o netcat -, ainda fazendo dev / urandom e ainda canalizando todos os dados - o problema desaparece. E, BTW, o único barramento USB é um problema dos PIs de framboesa - não dos PIs de laranja.
Ttsiodras 01/04/19
me desculpe se eu não te ajudei. e obrigado pelo esclarecimento.
D'Arcy Nader
@ RalphRönnquist: O caso de uso original que me levou a essa toca de coelho era fazer backup de coisas sobre rsync e borgbackup. Muitas ferramentas usam o SSH como mecanismo de transporte - e, no meu caso, sofreram por causa disso. Se o que estou experimentando é, de fato, o comportamento SSH "padrão", esperaria que o envio de solicitações pull a todas as ferramentas de backup para gerar o SSH por meio de um ProxyCommand da netcat aceleraria instantaneamente os backups em todo o planeta! Não acredito que fiz uma descoberta tão "grande" :-) outra coisa deve estar acontecendo aqui.
ttsiodras

Respostas:

14

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

Gravando pacotes com 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 fazia ideia disso - mas, depois de examinar a página de manual "tc", pude verificar se

  • 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, não tenho certeza se a modelagem de tráfego pode operar no nível do processo (eu esperaria que funcionasse em endereços / portas / Diferenciado Serviços no cabeçalho IP).

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

Não, mesmos resultados.

Finalmente - Nagle

Executando um traço no remetente ...

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

... e olhando 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 desativar o algoritmo do Nagle. Você pode pesquisar no Google e ler tudo sobre ele - mas o que isso significa é que o SSH está dando prioridade à capacidade de resposta em relação à largura de banda - instrui o kernel a transmitir qualquer coisa escrita neste soquete imediatamente e não "atrasa" a espera de agradecimentos do controle remoto.

O que isso significa, em termos simples, é que, em sua configuração padrão, o SSH NÃO é uma boa maneira de transportar dados - não quando o link usado é lento (o que ocorre com 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 era realmente o culpado, usei LD_PRELOAD para "eliminar" 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 o iperf3).

Moral da história

Nunca desista :-)

E se você usa ferramentas como rsyncou borgbackupque transportam seus dados pelo SSH, e seu link é lento, tente impedir que o SSH desative o Nagle (como mostrado acima) - ou use ProxyCommandpara alternar o SSH para se 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 no ssh / rsync / borgbackup passem a ser usados ncpara conectar-se (e, portanto, deixar Nagle em paz).

ttsiodras
fonte
Obrigado, você salvou minha vida! Você já tentou entrar em contato com o pessoal do ssh para entender por que não há uma configuração para controlar isso?
static_rtti 02/02
1
Fico feliz que minhas descobertas tenham ajudado você também! Quanto a entrar em contato com o pessoal do SSH, tentei sim - mas nada aconteceu no final: bugzilla.mindrot.org/show_bug.cgi?id=2848
ttsiodras 03/02
Me adicionei ao bug. Quem sabe, algo pode acontecer eventualmente! Grande investigação, em qualquer caso.
static_rtti 04/02