Швидкість SSH значно покращилася через ProxyCommand - але чому?


14

Версія TL; DR

Перегляньте цей акторський фільм ASCII або це відео - тоді придумайте будь-які причини, чому це відбувається. Наступний опис тексту надає більше контексту.

Деталі налаштування

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

Топологія

Бенчмаркінг посилання на iperf3

Якщо орієнтуватися на iperf3, зв'язок між ноутбуком та SBC менший, ніж теоретичні 56 Мбіт / с - як очікувалося, оскільки це підключення до Wi-Fi у дуже "переповненому 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), але цього разу з іншої машини (Machine 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 може (легко) обробляти 11MBytes / sec (тобто 100MBits / sec), які надає 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]

Працює! 10x швидкість.

Тепер я трохи розгублений - коли ви використовуєте "голий" ncяк a Proxycommand, чи не в основному ви робите саме те, що робить SSH? тобто створити сокет, підключитися до порту 22 SBC, а потім перенести через нього протокол SSH?

Чому існує така величезна різниця в отриманій швидкості?

PS Це була не академічна вправа - borgзавдяки цьому моя резервна копія працює в 10 разів швидше. Я просто не знаю, чому :-)

EDIT : долучення «відео» процесу тут . Підраховуючи пакети, надіслані з виводу ifconfig, зрозуміло, що в обох тестах ми надсилаємо 40 Мб даних, передаючи їх приблизно в 30 К пакетів - просто набагато повільніше, коли не використовуємо ProxyCommand.


буферизація? Я думаю, ncвикористовує буферизацію ліній, тоді як sshбуферизація не має. Так (або якщо так), ssh трафік включає більше пакетів.
Ральф Ронквіст

Я не експерт, але я думаю, що в помаранчевому 0 є лише одна шина usb, керована процесором, мережа проходить через цю шину usb, процесор повинен створити випадкове число за допомогою програмного забезпечення (у такої архітектури немає чіпа, який це робить через апаратне забезпечення) і в той же час триває ssh cypher і, можливо, також стиснення ssh. я не перевірив усе це, так що можливо, я кажу щось не так.
Д'Арсі Надер

2
@ D'ArcyNader: Ні, я боюся, що ви помилилися. Tbe / dev / urandom трапляється в ноутбуці (x86) - і я зробив той самий тест з машини 2, що розмовляв з SBC, досягаючи максимальної швидкості (100 Мбіт / с), і таким чином доводив, що SBC не має жодних проблем з трафіком. Проблема виявляється лише тоді, коли SSH використовується з ноутбука - і коли я змінюю виклик SSH (знову ж таки, на стороні ноутбука), щоб використовувати netcat - так як все ще роблять dev / urandom і все ще переносять усі дані - проблема зникає. І BTW, єдина USB-шина є проблемою Raspberry PI, а не Orange PI.
ttsiodras

Вибачте, якщо я вам не допомогла. і дякую за роз’яснення.
Д'Арсі Надер

@ RalphRönnquist: Оригінальний випадок використання, який призвів мене до цієї кролячої нори, підкріплював речі над rsync та borgbackup. Багато інструментів використовують SSH як транспортний механізм - і в моєму випадку через це постраждали. Якщо те, що я переживаю, справді є "стандартною" поведінкою SSH, то я би сподівався, що подання запитів на тягу до всіх резервних інструментів для нерестування SSH через netcat ProxyCommand миттєво прискорить створення резервних копій по всій планеті! Я не можу повірити, що я зробив таке "величезне" відкриття :-) тут має відбуватися щось інше.
ttsiodras

Відповіді:


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, щоб уникнути потенційної "кмітливості" в клієнті Arch Linux SSH

Ні, такі самі результати.

Нарешті - Nagle

Виконання напруги у відправника ...

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 від відключення Негл (як показано вище) - або з використанням 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 у спокої).


Спасибі, ти врятував мені життя! Ви намагалися зв’язатися з ssh folks, щоб зрозуміти, чому немає налаштування для керування цим?
static_rtti

1
Я радий, що мої висновки допомогли і вам! Щодо контактів із людьми з SSH, я намагався, так - але нічого не сталося, врешті-решт: bugzilla.mindrot.org/show_bug.cgi?id=2848
ttsiodras

Додав себе до помилки. Хто знає, з часом може статися щось! Велике розслідування, в будь-якому випадку.
static_rtti
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.