Скорость SSH значительно улучшилась благодаря ProxyCommand - но почему?

14

Версия TL; DR

Посмотрите этот актерский состав ASCII или это видео, а затем найдите причины, по которым это происходит. Текстовое описание, которое следует, обеспечивает больше контекста.

Подробности настройки

  • Machine 1 - это портативный компьютер Arch Linux, на котором sshон создается, подключаясь к SBC, работающему на Armbian (Orange PI Zero).
  • Сам SBC подключен через Ethernet к маршрутизатору DSL и имеет IP-адрес 192.168.1.150.
  • Ноутбук подключен к маршрутизатору через WiFi - с помощью официального ключа Raspberry PI WiFi.
  • Есть также еще один ноутбук (машина 2), подключенный через Ethernet к маршрутизатору DSL.

Топология

Сравнительный анализ связи с iperf3

При сравнительном тестировании iperf3связь между ноутбуком и SBC меньше теоретической скорости 56 Мбит / с, как и ожидалось, поскольку это соединение WiFi в очень «переполненном 2,4 ГГц» (жилой дом) .

Более конкретно: после запуска iperf3 -sна SBC на ноутбуке выполняются следующие команды:

# 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

Таким образом, в основном загрузка на SBC достигает около 24 Мбит / с, а загрузка с него ( -R) достигает 32 Мбит / с.

Бенчмаркинг с SSH

Учитывая это, давайте посмотрим, как поживает SSH. Сначала я столкнулся с проблемами, которые привели к этому сообщению при использовании rsyncи borgbackup- оба они используют SSH в качестве транспортного уровня ... Итак, давайте посмотрим, как SSH работает по той же ссылке:

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

Ну, это ужасная скорость! Намного медленнее, чем ожидаемая скорость соединения ... (В случае, если вы не в курсе pv -ptevar: он отображает текущую и среднюю скорость проходящих через него данных. В этом случае мы видим, что чтение /dev/urandomи отправка данных через SSH на SBC в среднем достигает 400 КБ / с - то есть 3,2 Мбит / с, что намного меньше, чем ожидаемые 24 Мбит / с.)

Почему наша ссылка работает на 13% своей емкости?

Возможно, это наша /dev/urandomвина?

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

Нет, определенно нет.

Возможно, это сам SBC? Возможно, это слишком медленно для обработки? Давайте попробуем запустить ту же команду SSH (т.е. отправить данные в SBC), но на этот раз с другой машины (машина 2), которая подключена через Ethernet:

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

Нет, это работает нормально - демон SSH на SBC может (легко) обрабатывать 11 МБ / с (то есть 100 Мбит / с), которые обеспечивает его канал Ethernet.

И загружается ли процессор SBC при этом?

Процессор легко справляется с этим

Нет.

Так...

  • по сети (в соответствии с iperf3) мы должны быть в состоянии сделать в 10 раз быстрее
  • наш процессор может легко справиться с нагрузкой
  • ... и мы не задействуем какой-либо другой тип ввода / вывода (например, накопители).

Какого черта происходит?

Netcat и ProxyCommand на помощь

Давайте попробуем простые старые netcatсоединения - они работают так быстро, как мы ожидали?

В SBC:

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

В ноутбуке:

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

Оно работает! И работает на ожидаемой - намного лучше, в 10 раз лучше - скорости.

Так что же произойдет, если я запускаю SSH с помощью ProxyCommand для использования nc?

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

Работает! 10-кратная скорость.

Теперь я немного запутался - когда вы используете «голый» ncкак Proxycommandразве вы не делаете в точности то же самое, что делает SSH? т.е. создать сокет, подключиться к порту 22 SBC, а затем переложить поверх него протокол SSH?

Почему такая огромная разница в скорости?

PS Это не было академическим упражнением - borgиз-за этого мое резервное копирование выполняется в 10 раз быстрее. Я просто не знаю почему :-)

РЕДАКТИРОВАТЬ : Добавлено "видео" процесса здесь . Подсчитав пакеты, отправленные с выхода ifconfig, становится ясно, что в обоих тестах мы отправляем 40 МБ данных, передавая их примерно в пакетах по 30 КБ - просто намного медленнее, когда они не используются ProxyCommand.

ttsiodras
источник
буферные? Я думаю, что ncиспользует буферизацию строки, а sshне имеет буферизации. Так (или если так) трафик ssh включает больше пакетов.
Ральф Реннквист
Я не эксперт, но я думаю, что Orange 0 имеет только одну шину USB, контролируемую процессором, сеть проходит через эту шину USB, процессор должен создавать случайные числа с помощью программного обеспечения (в такой архитектуре нет чипа, который делает это через аппаратное обеспечение), и в то же время идет шифрование ssh и, возможно, сжатие ssh. Я не проверял все это, так что возможно я говорю что-то не так.
Д'Арси Надер
2
@ D'ArcyNader: Нет, боюсь, вы ошиблись. Tbe / dev / urandom происходит в ноутбуке (x86) - и я провел тот же тест на машине 2, обращаясь к SBC, достигая максимальной скорости (100 Мбит / с) и, таким образом, доказывая, что у SBC нет проблем с трафиком. Проблема проявляется только тогда, когда SSH используется с ноутбука - и когда я меняю вызов SSH (опять же на стороне ноутбука) на использование netcat - так что все еще выполняя dev / urandom и все еще передавая все данные - проблема исчезает. И между прочим, одиночная шина USB - это проблема Raspberry PI, а не Orange PI.
ttsiodras
извините, если я вам не помог. и спасибо за разъяснения.
Д'Арси Надер
@ RalphRönnquist: Первоначальный вариант использования, который привел меня к этой кроличьей норе, заключался в резервном копировании через rsync и borgbackup. Многие инструменты используют SSH в качестве транспортного механизма - и в моем случае пострадали из-за этого. Если то, что я испытываю, это действительно «стандартное» поведение SSH, то я ожидаю, что отправка запросов извлечения всем инструментам резервного копирования для запуска SSH через Netcat ProxyCommand мгновенно ускорит резервное копирование по всей планете! Я не могу поверить, что сделал такое «огромное» открытие :-) здесь должно происходить что-то еще.
Циодрас

Ответы:

14

Большое спасибо людям, которые представили идеи в комментариях. Я прошел их всех:

Запись пакетов с помощью tcpdump и сравнение содержимого в WireShark

# tcpdump -i wlan0 -w good.ssh & \
     cat signature | ssh -o "ProxyCommand nc %h %p" \
        root@192.168.1.150 'cat | md5sum' ; \
     killall tcpdump
# tcpdump -i wlan0 -w bad.ssh & \
     cat signature | ssh root@192.168.1.150 'cat | md5sum' ; \
     killall tcpdump

Там не было никакой разницы какой-либо важности в записанных пакетах.

Проверка на формирование трафика

Я понятия не имел об этом - но после просмотра справочной страницы "tc" я смог убедиться, что

  • tc filter show ничего не возвращает
  • tc class show ничего не возвращает
  • tc qdisc show

... возвращает это:

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 

... которые, кажется, не делают различий между "ssh" и "nc" - на самом деле, я даже не уверен, может ли формирование трафика работать на уровне процесса (я ожидаю, что оно будет работать на адресах / портах / дифференцированных Поле служб в заголовке IP).

Debian Chroot, чтобы избежать потенциальной «хитрости» в SSH-клиенте Arch Linux

Нет, те же результаты.

Наконец - нагл

Выполняя пересылку в отправителе ...

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

... и, глядя на то, что именно происходит в сокете, через который передаются данные, я заметил эту «настройку» перед началом фактической передачи:

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>

Это устанавливает сокет SSH для отключения алгоритма Nagle. Вы можете Google и прочитать все об этом - но это означает, что SSH отдает приоритет реагированию над пропускной способностью - он инструктирует ядро ​​немедленно передавать все, что написано на этом сокете, а не «задерживать» ожидание подтверждений от удаленного устройства.

Говоря простым языком, это означает, что в конфигурации по умолчанию SSH НЕ является хорошим способом передачи данных между собой - не тогда, когда используемая связь является медленной (что имеет место для многих WiFi-соединений). Если мы отправляем по беспроводной сети пакеты, которые являются «главным образом заголовками», пропускная способность теряется!

Чтобы доказать, что это действительно было преступником, я использовал LD_PRELOAD, чтобы «удалить» этот конкретный системный вызов:

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

Там - идеальная скорость (ну так же быстро, как и iperf3).

Мораль истории

Никогда не сдавайся :-)

И если вы используете такие инструменты rsyncили такие, borgbackupкоторые передают их данные через SSH, и ваша ссылка медленная, попробуйте не использовать SSH для отключения Nagle (как показано выше) - или использовать ProxyCommandдля переключения SSH для подключения через nc. Это можно автоматизировать в вашем $ 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
...

... так что все будущие использования "orangepi" в качестве целевого хоста в ssh / rsync / borgbackup впредь будут использоваться ncдля подключения (и, следовательно, оставлять Nagle в покое).

ttsiodras
источник
Спасибо, ты спас мне жизнь! Вы пытались связаться с людьми ssh, чтобы понять, почему нет настройки для управления этим?
static_rtti
1
Я рад, что мои выводы также помогли вам! Что касается связи с людьми из SSH, я попытался, да - но в итоге ничего не произошло: bugzilla.mindrot.org/show_bug.cgi?id=2848
ttsiodras
Добавил себя в баг. Кто знает, что-то может случиться в конце концов! Отличное расследование, в любом случае.
static_rtti