Kecepatan SSH meningkat pesat melalui ProxyCommand - tapi mengapa?

14

Versi TL; DR

Tonton pemeran ASCII ini atau video ini - lalu buat alasan mengapa ini terjadi. Deskripsi teks berikut ini memberikan lebih banyak konteks.

Detail pengaturan

  • Mesin 1 adalah laptop Arch Linux, yang mana sshmelahirkan, menghubungkan ke SBC yang menjalankan Armbian (sebuah Orange PI Zero).
  • SBC itu sendiri terhubung melalui Ethernet ke router DSL, dan memiliki IP 192.168.1.150
  • Laptop terhubung ke router melalui WiFi - menggunakan dongle WiFi Raspberry PI resmi.
  • Ada juga laptop lain (Mesin 2) yang terhubung melalui Ethernet ke router DSL.

Topologi

Benchmarking tautan dengan iperf3

Ketika diperbandingkan dengan iperf3, tautan antara laptop dan SBC kurang dari yang teoritis 56 MBits / detik - seperti yang diharapkan, karena ini adalah koneksi WiFi dalam 2,4GHz yang sangat ramai (gedung apartemen) .

Lebih khusus: setelah berjalan iperf3 -sdi SBC, perintah berikut dijalankan di 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

Jadi pada dasarnya, mengunggah ke SBC mencapai sekitar 24MBits / detik, dan pengunduhan darinya ( -R) mencapai 32MBits / detik.

Pembandingan dengan SSH

Mengingat itu, mari kita lihat bagaimana tarif SSH. Saya pertama kali mengalami masalah yang mengarah ke pos ini saat menggunakan rsyncdan borgbackup- keduanya menggunakan SSH sebagai lapisan transpor ... Jadi, mari kita lihat kinerja SSH pada tautan yang sama:

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

Nah, itu kecepatan yang luar biasa! Jauh lebih lambat daripada kecepatan tautan yang diharapkan ... (Jika Anda tidak menyadarinya pv -ptevar: ini akan menampilkan laju saat ini dan rata-rata data yang melewatinya. Dalam hal ini, kami melihat bahwa pembacaan dari /dev/urandomdan mengirim data melalui SSH ke SBC rata-rata mencapai 400KB / dtk - yaitu 3,2 MB / dtk, angka yang jauh lebih rendah dari yang diharapkan 24Mt / dtk.)

Mengapa tautan kami berjalan pada 13% dari kapasitasnya?

Apakah ini mungkin /dev/urandomkesalahan kita?

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

Tidak, pasti tidak.

Apakah mungkin SBC itu sendiri? Mungkin terlalu lambat untuk diproses? Mari kita coba menjalankan perintah SSH yang sama (yaitu mengirim data ke SBC) tetapi kali ini dari komputer lain (Mesin 2) yang terhubung melalui Ethernet:

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

Tidak, ini berfungsi dengan baik - daemon SSH pada SBC dapat (dengan mudah) menangani 11MBytes / detik (yaitu 100MBits / detik) yang disediakan oleh tautan Ethernet.

Dan apakah CPU dari SBC dimuat saat melakukan ini?

CPU dengan mudah menanganinya

Nggak.

Begitu...

  • Dari segi jaringan (per iperf3) kita harus dapat melakukan 10x kecepatan
  • CPU kami dapat dengan mudah mengakomodasi beban
  • ... dan kami tidak melibatkan jenis I / O lainnya (mis. drive).

Apa yang terjadi?

Netcat dan ProxyCommand untuk menyelamatkan

Mari kita coba netcatkoneksi lama biasa - apakah mereka berjalan secepat yang kita harapkan?

Dalam SBC:

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

Di laptop:

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

Berhasil! Dan berjalan pada kecepatan yang diharapkan - jauh lebih baik, 10x lebih baik -.

Jadi apa yang terjadi jika saya menjalankan SSH menggunakan ProxyCommand untuk menggunakan 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]

Bekerja! Kecepatan 10x.

Sekarang saya agak bingung - ketika menggunakan "telanjang" ncsebagai Proxycommand, bukankah pada dasarnya Anda melakukan hal yang sama persis seperti SSH? yaitu membuat soket, menghubungkan ke port SBC 22, dan kemudian menyekop protokol SSH di atasnya?

Mengapa ada perbedaan besar dalam kecepatan yang dihasilkan?

PS Ini bukan latihan akademis - borgcadangan saya berjalan 10 kali lebih cepat karena ini. Aku hanya tidak tahu mengapa :-)

EDIT : Menambahkan "video" dari proses di sini . Menghitung paket yang dikirim dari output ifconfig, jelas bahwa dalam kedua pengujian kami mengirim data 40MB, mentransmisikannya dalam paket sekitar 30K - hanya lebih lambat jika tidak digunakan ProxyCommand.

ttsiodras
sumber
buffering? Saya akan berpikir ncmenggunakan buffering garis, sedangkan sshtidak memiliki buffering. Jadi (atau jika demikian) lalu lintas ssh melibatkan lebih banyak paket.
Ralph Rönnquist
Saya bukan ahli tetapi saya pikir oranye 0 hanya memiliki satu bus usb yang dikendalikan oleh CPU, jaringan melewati bus usb itu, CPU harus membuat nomor acak melalui perangkat lunak (tidak ada chip pada jenis arsitektur yang melakukan itu melalui perangkat keras) dan pada saat yang sama ada kode ssh sedang berlangsung dan mungkin juga kompresi ssh. saya tidak memeriksa semua ini sehingga mungkin saya mengatakan sesuatu yang salah.
D'Arcy Nader
2
@ D'ArcyNader: Tidak, saya khawatir Anda salah. Tbe / dev / urandom terjadi di laptop (x86) - dan saya melakukan tes yang sama dari Mesin 2 berbicara kepada SBC, mencapai kecepatan tertinggi (100MBits / detik), dan dengan demikian membuktikan bahwa SBC tidak memiliki masalah dalam berurusan dengan lalu lintas. Masalahnya hanya memanifestasikan ketika SSH digunakan dari laptop - dan ketika saya mengubah doa SSH (sekali lagi, di sisi laptop) untuk menggunakan netcat - jadi masih melakukan dev / urandom dan masih mem-pip semua data - masalahnya hilang. Dan BTW, bus USB tunggal adalah masalah Raspberry PI - bukan PI Orange.
ttsiodras
Maaf jika saya tidak membantu Anda. dan terima kasih atas klarifikasi.
D'Arcy Nader
@ RalphRönnquist: Kasus penggunaan asli yang membawa saya ke lubang kelinci ini mendukung semuanya di rsync dan borgbackup. Banyak alat menggunakan SSH sebagai mekanisme transportasi - dan dalam kasus saya, menderita karena ini. Jika apa yang saya alami adalah perilaku SSH "standar", maka saya akan berharap bahwa mengirimkan permintaan tarik ke semua alat cadangan untuk menelurkan SSH melalui netcat ProxyCommand akan segera mempercepat backup di seluruh planet ini! Aku tidak percaya aku membuat penemuan "besar" :-) sesuatu yang lain pasti terjadi di sini.
ttsiodras

Jawaban:

14

Banyak terima kasih kepada orang-orang yang mengirimkan ide dalam komentar. Saya melewati semuanya:

Merekam paket dengan tcpdump dan membandingkan konten di 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

Tidak ada perbedaan yang penting dalam paket yang direkam.

Memeriksa pembentukan lalu lintas

Tidak tahu tentang ini - tetapi setelah melihat halaman "tc", saya bisa memverifikasi itu

  • tc filter show tidak mengembalikan apa pun
  • tc class show tidak mengembalikan apa pun
  • tc qdisc show

... mengembalikan ini:

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 

... yang sepertinya tidak membedakan antara "ssh" dan "nc" - pada kenyataannya, saya bahkan tidak yakin apakah traffic shaping dapat beroperasi pada level proses (saya berharap ini bekerja pada alamat / port / Diferensial Bidang Layanan dalam IP Header).

Debian Chroot, untuk menghindari potensi "kepintaran" di klien Arch Linux SSH

Tidak, hasil yang sama.

Akhirnya - Nagle

Melakukan strace di pengirim ...

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

... dan melihat apa yang sebenarnya terjadi pada soket yang mentransmisikan data, saya perhatikan "pengaturan" ini sebelum transmisi yang sebenarnya dimulai:

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>

Ini mengatur soket SSH untuk menonaktifkan algoritma Nagle. Anda dapat Google dan membaca semua tentang hal itu - tetapi apa artinya, adalah bahwa SSH memberikan prioritas untuk responsif terhadap bandwidth - ia memerintahkan kernel untuk mengirimkan apa pun yang tertulis pada soket ini segera dan tidak "menunda" menunggu pengakuan dari jarak jauh.

Apa artinya ini, secara sederhana, adalah bahwa dalam konfigurasi standarnya, SSH BUKAN cara yang baik untuk memindahkan data - bukan ketika tautan yang digunakan lambat (yang merupakan kasus bagi banyak tautan WiFi). Jika kami mengirim paket melalui udara yang "kebanyakan header", bandwidthnya terbuang sia-sia!

Untuk membuktikan bahwa ini memang penyebabnya, saya menggunakan LD_PRELOAD untuk "menjatuhkan" syscall khusus ini:

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

Di sana - kecepatan sempurna (well, secepat iperf3).

Moral cerita

Jangan menyerah :-)

Dan jika Anda memang menggunakan alat seperti rsyncatau borgbackupyang mengangkut data mereka melalui SSH, dan tautan Anda lambat, coba hentikan SSH dari menonaktifkan Nagle (seperti yang ditunjukkan di atas) - atau gunakan ProxyCommanduntuk beralih SSH untuk terhubung melalui nc. Ini dapat diotomatisasi dalam $ HOME / .ssh / config Anda:

$ cat .ssh/config
...
Host orangepi
    Hostname 192.168.1.150
    User root
    Port 22
    # Compression no
    # Cipher None
    ProxyCommand nc %h %p
...

... sehingga semua penggunaan "orangepi" di masa depan sebagai host target di ssh / rsync / borgbackup untuk selanjutnya akan digunakan ncuntuk terhubung (dan karenanya meninggalkan Nagle sendiri).

ttsiodras
sumber
Terima kasih, kamu menyelamatkan hidupku! Sudahkah Anda mencoba menghubungi orang-orang ssh untuk memahami mengapa tidak ada pengaturan untuk mengendalikan ini?
static_rtti
1
Saya senang temuan saya membantu Anda juga! Adapun untuk menghubungi orang-orang SSH, saya mencoba, ya - tetapi tidak ada yang terjadi, pada akhirnya: bugzilla.mindrot.org/show_bug.cgi?id=2848
ttsiodras
Menambahkan diri saya ke bug. Siapa tahu, sesuatu akan terjadi pada akhirnya! Investigasi hebat, dalam hal apa pun.
static_rtti