A versão TL;DR
Assista a este elenco ASCII ou a este vídeo - e depois pense em algum motivo para isso estar 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
é gerado, conectando-se a um SBC executando Armbian (um Orange PI Zero). - O próprio SBC está conectado via Ethernet a um roteador DSL e possui um IP de 192.168.1.150
- O laptop está conectado ao roteador por WiFi - usando um dongle Raspberry PI WiFi oficial.
- Há também outro laptop (Máquina 2) conectado via Ethernet ao roteador DSL.
Fazendo benchmarking do link com iperf3
Quando comparado com iperf3
o , o link entre o laptop e o SBC é menor que os 56 MBits/s teóricos - como esperado, já que se trata de uma conexão Wi-Fi dentro de um "2,4 GHz" muito "lotado" (prédio de apartamentos) .
Mais especificamente: após a execução 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/s e o download dele ( -R
) atinge 32 MBits/s.
Benchmarking com SSH
Dado isso, vamos ver como o SSH se sai. Eu experimentei pela primeira vez os problemas que levaram a esta postagem ao usar rsync
e 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, isso é uma velocidade abismal! Muito mais lento que a velocidade de link esperada...
(Caso você não saiba pv -ptevar
: ele 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 atinge, em média, 400 KB/s - ou seja, 3,2 MBits/s, um valor bem menor do que os 24 MBits/s esperados.)
Por que nosso link está operando a 13% de sua capacidade?
É talvez nossa /dev/urandom
culpa?
# 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) lidar com 11 MBytes/seg (ou seja, 100 MBits/seg) que seu link Ethernet fornece.
E a CPU do SBC é carregada ao fazer isso?
Não.
Então...
- em termos de rede (conforme
iperf3
), devemos ser capazes de fazer 10x a velocidade - nossa CPU pode facilmente acomodar a carga
- ... e não envolvemos nenhum outro tipo de E/S (por exemplo, drives).
O que diabos está acontecendo?
Netcat e ProxyCommand para o resgate
Vamos tentar netcat
conexões antigas simples - elas rodam tão rápido quanto esperávamos?
No SBC:
# nc -l -p 9988 | pv -ptebar > /dev/null
No portátil:
# 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 o 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]
Funciona! velocidade 10x.
Agora estou um pouco confuso - ao usar um "naked" nc
como um Proxycommand
, você não está basicamente fazendo exatamente a mesma coisa que o SSH faz? ou seja, criar um soquete, conectar-se à porta 22 do SBC e, em seguida, transferir o protocolo SSH sobre ele?
Por que existe essa enorme diferença na velocidade resultante?
PS Este não foi um exercício acadêmico - meu borg
backup é executado 10 vezes mais rápido por causa disso. Só não sei porque :-)
EDIT : 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 - só que bem mais lento quando não estamos usando ProxyCommand
.
Muito obrigado às pessoas que enviaram ideias nos comentários. Eu passei por todos eles:
Gravando pacotes com tcpdump e comparando o conteúdo no WireShark
Não houve diferença de importância nos pacotes registrados.
Verificando a modelagem de tráfego
Não tinha ideia disso - mas depois de olhar a página de manual "tc", pude verificar que
tc filter show
não retorna nadatc class show
não retorna nadatc qdisc show
...retorna estes:
...que não parecem diferenciar entre "ssh" e "nc" - na verdade, nem tenho certeza se o modelagem de tráfego pode operar no nível do processo (eu esperaria que funcionasse em endereços/portas/diferenciados Campo de serviços no cabeçalho IP).
Debian Chroot, para evitar potencial "inteligência" no cliente Arch Linux SSH
Não, mesmos resultados.
Finalmente - Nagle
Fazendo um strace no sender...
... e observando o que exatamente acontece no soquete que transmite os dados, notei esta "configuração" antes do início da transmissão real:
Isso configura o soquete SSH para desabilitar o algoritmo de Nagle. Você pode pesquisar no Google e ler tudo sobre isso - mas o que isso significa é que o SSH está dando prioridade à capacidade de resposta sobre a largura de banda - ele instrui o kernel a transmitir qualquer coisa escrita neste soquete imediatamente e não "atrasar" a espera de confirmações 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 é 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 este era realmente o culpado, usei LD_PRELOAD para "descartar" esta syscall específica:
Lá - velocidade perfeita (bem, tão rápido quanto iperf3).
moral da história
Nunca desista :-)
E se você usar ferramentas como
rsync
ouborgbackup
que transportam seus dados por SSH, e seu link for lento, tente impedir que o SSH desative o Nagle (como mostrado acima) - ou useProxyCommand
para alternar o SSH para conectar vianc
. Isso pode ser automatizado em seu $HOME/.ssh/config:...para que todos os usos futuros de "orangepi" como um host de destino em ssh/rsync/borgbackup sejam usados doravante
nc
para conectar (e, portanto, deixar Nagle em paz).