Tốc độ SSH được cải thiện rất nhiều thông qua ProxyCommand - nhưng tại sao?


14

Phiên bản TL; DR

Xem diễn viên ASCII này hoặc video này - sau đó đưa ra bất kỳ lý do tại sao điều này xảy ra. Mô tả văn bản sau đây cung cấp nhiều bối cảnh hơn.

Chi tiết thiết lập

  • Máy 1 là máy tính xách tay Arch Linux, trên đó sshđược sinh ra, kết nối với SBC chạy bằng tiếng Armenia (Orange PI Zero).
  • Bản thân SBC được kết nối qua Ethernet với bộ định tuyến DSL và có IP là 192.168.1.150
  • Máy tính xách tay được kết nối với bộ định tuyến qua WiFi - sử dụng khóa Raspberry PI WiFi chính thức.
  • Ngoài ra còn có một máy tính xách tay khác (Máy 2) được kết nối qua Ethernet với bộ định tuyến DSL.

Cấu trúc liên kết

Điểm chuẩn liên kết với iperf3

Khi được điểm chuẩn iperf3, liên kết giữa máy tính xách tay và SBC nhỏ hơn 56 MBits / giây trên lý thuyết - như mong đợi, vì đây là kết nối WiFi trong một " tòa nhà chung cư " rất đông đúc .

Cụ thể hơn: sau khi chạy iperf3 -strên SBC, các lệnh sau được thực thi trên máy tính xách tay:

# 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

Vì vậy, về cơ bản, tải lên SBC đạt khoảng 24MBits / giây và tải xuống từ nó ( -R) đạt 32MBits / giây.

Điểm chuẩn với SSH

Do đó, chúng ta hãy xem giá vé SSH. Lần đầu tiên tôi gặp phải sự cố dẫn đến bài đăng này khi sử dụng rsyncborgbackup- cả hai đều sử dụng SSH làm lớp vận chuyển ... Vì vậy, hãy xem cách SSH thực hiện trên cùng một liên kết:

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

Chà, đó là một tốc độ kinh khủng! Chậm hơn nhiều so với tốc độ liên kết dự kiến ​​... (Trong trường hợp bạn không biết pv -ptevar: nó hiển thị tốc độ dữ liệu trung bình và hiện tại đi qua nó. Trong trường hợp này, chúng tôi thấy rằng đọc /dev/urandomvà gửi dữ liệu qua SSH đến SBC trung bình đạt 400KB / giây - tức là 3,2 MB / giây, một con số thấp hơn nhiều so với 24 MB / giây dự kiến.)

Tại sao liên kết của chúng tôi chạy ở mức 13% công suất?

Có lẽ đó /dev/urandomlà lỗi của chúng tôi ?

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

Không, chắc chắn là không.

Có lẽ chính SBC? Có lẽ nó quá chậm để xử lý? Hãy thử chạy cùng một lệnh SSH (nghĩa là gửi dữ liệu tới SBC) nhưng lần này là từ một máy khác (Máy 2) được kết nối qua 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] 

Không, điều này hoạt động tốt - trình nền SSH trên SBC có thể (dễ dàng) xử lý 11 MB / giây (tức là 100 MB / giây) mà liên kết Ethernet cung cấp.

Và CPU của SBC có được tải trong khi làm việc này không?

CPU dễ dàng xử lý nó

Không.

Vì thế...

  • mạng khôn ngoan (theo iperf3) chúng ta sẽ có thể tăng tốc độ gấp 10 lần
  • CPU của chúng tôi có thể dễ dàng đáp ứng tải
  • ... và chúng tôi không liên quan đến bất kỳ loại I / O nào khác (ví dụ: ổ đĩa).

Cái quái gì đang xảy ra vậy?

Netcat và ProxyCommand để giải cứu

Hãy thử các netcatkết nối cũ đơn giản - chúng có chạy nhanh như chúng ta mong đợi không?

Trong SBC:

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

Trong máy tính xách tay:

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

Nó hoạt động! Và chạy ở tốc độ mong đợi - tốt hơn nhiều, tốt hơn gấp 10 lần - tốc độ.

Vậy chuyện gì xảy ra nếu tôi chạy SSH bằng ProxyCommand để sử dụng 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]

Làm! Tốc độ gấp 10 lần.

Bây giờ tôi có một chút bối rối - khi sử dụng "trần trụi" ncnhư một Proxycommand, về cơ bản, bạn không làm chính xác những gì mà SSH làm? tức là tạo một ổ cắm, kết nối với cổng 22 của SBC và sau đó di chuyển giao thức SSH qua nó?

Tại sao có sự khác biệt lớn về tốc độ này?

Tái bút: Đây không phải là một bài tập học thuật - borgbản sao lưu của tôi chạy nhanh hơn 10 lần vì điều này. Tôi chỉ không biết tại sao :-)

EDIT : Đã thêm một "video" của quá trình ở đây . Đếm các gói được gửi từ đầu ra của ifconfig, rõ ràng trong cả hai thử nghiệm, chúng tôi đang gửi 40 MB dữ liệu, truyền chúng trong khoảng 30K gói - chỉ chậm hơn nhiều khi không sử dụng ProxyCommand.


đệm? Tôi nghĩ rằng ncsử dụng bộ đệm dòng, trong khi sshkhông có bộ đệm. Vì vậy, (hoặc nếu vậy) lưu lượng ssh liên quan đến nhiều gói hơn.
Ralph Rönnquist

Tôi không phải là chuyên gia nhưng tôi nghĩ màu cam 0 chỉ có một bus USB được điều khiển bởi cpu, mạng đi qua bus USB đó, cpu phải tạo số ngẫu nhiên thông qua phần mềm (không có chip trên loại kiến ​​trúc đó thực hiện thông qua đó phần cứng) và đồng thời có ssh cypher đang diễn ra và có lẽ quá trình nén ssh cũng vậy. Tôi đã không kiểm tra tất cả những điều này vì vậy có thể tôi đang nói điều gì đó sai.
D'Arcy Nader

2
@ D'ArcyNader: Không, tôi sợ bạn hiểu sai. Tbe / dev / urandom xảy ra trong máy tính xách tay (x86) - và tôi đã thực hiện thử nghiệm tương tự từ Máy 2 nói với SBC, đạt tốc độ tối đa (100MBits / giây) và do đó chứng minh rằng SBC không gặp vấn đề gì khi xử lý lưu lượng. Vấn đề chỉ thể hiện khi SSH được sử dụng từ máy tính xách tay - và khi tôi thay đổi lệnh gọi SSH (một lần nữa, ở phía máy tính xách tay) để sử dụng netcat - vì vậy vẫn thực hiện dev / urandom và vẫn dẫn tất cả dữ liệu - vấn đề biến mất. Và BTW, bus USB đơn là vấn đề của Raspberry PI - không phải Orange PI.
ttsiodras

Tôi xin lỗi nếu tôi không giúp bạn. và cảm ơn bạn đã làm rõ.
D'Arcy Nader

@ RalphRönnquist: Trường hợp sử dụng ban đầu dẫn tôi xuống hố thỏ này là sao lưu mọi thứ qua rsync và borgbackup. Nhiều công cụ sử dụng SSH như một cơ chế vận chuyển - và trong trường hợp của tôi, đã phải chịu đựng vì điều này. Thực sự, nếu những gì tôi đang trải nghiệm là hành vi SSH "chuẩn", thì tôi sẽ mong đợi rằng việc gửi yêu cầu kéo tới tất cả các công cụ sao lưu để sinh ra SSH thông qua một ProxyCommand netcat sẽ ngay lập tức tăng tốc độ sao lưu trên khắp hành tinh! Tôi không thể tin rằng tôi đã thực hiện một khám phá "khổng lồ" như vậy :-) một cái gì đó khác phải xảy ra ở đây.
ttsiodras

Câu trả lời:


14

Rất cám ơn những người đã gửi ý tưởng trong các ý kiến. Tôi đã trải qua tất cả:

Ghi lại các gói với tcpdump và so sánh nội dung trong 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

Không có sự khác biệt về tầm quan trọng trong các gói được ghi lại.

Kiểm tra định hình giao thông

Không biết gì về điều này - nhưng sau khi xem trang chủ "tc", tôi đã có thể xác minh rằng

  • tc filter show trả lại không có gì
  • tc class show trả lại không có gì
  • tc qdisc show

... trả về những điều này:

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 

... dường như không phân biệt giữa "ssh" và "nc" - thực tế, tôi thậm chí không chắc liệu hình dạng lưu lượng truy cập có thể hoạt động ở cấp quy trình hay không (tôi hy vọng nó hoạt động trên địa chỉ / cổng / Phân biệt Trường dịch vụ trong IP Header).

Debian Chroot, để tránh "sự thông minh" tiềm năng trong máy khách Arch Linux SSH

Không, kết quả tương tự.

Cuối cùng - Nagle

Thực hiện một bước tiến trong người gửi ...

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

... và xem xét chính xác những gì xảy ra trên ổ cắm truyền dữ liệu, tôi nhận thấy "thiết lập" này trước khi quá trình truyền thực sự bắt đầu:

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>

Điều này thiết lập ổ cắm SSH để vô hiệu hóa thuật toán của Nagle. Bạn có thể Google và đọc tất cả về nó - nhưng ý nghĩa của nó là SSH đang ưu tiên đáp ứng băng thông - nó hướng dẫn kernel truyền bất cứ thứ gì được ghi trên ổ cắm này ngay lập tức và không "trì hoãn" chờ xác nhận từ xa.

Nói một cách đơn giản, điều này có nghĩa là trong cấu hình mặc định của nó, SSH KHÔNG phải là một cách tốt để truyền dữ liệu qua - không phải khi liên kết được sử dụng là chậm (đó là trường hợp của nhiều liên kết WiFi). Nếu chúng tôi đang gửi các gói qua mạng "chủ yếu là các tiêu đề", băng thông sẽ bị lãng phí!

Để chứng minh rằng đây thực sự là thủ phạm, tôi đã sử dụng LD_PRELOAD để "thả" tòa nhà cụ thể này:

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

Ở đó - tốc độ hoàn hảo (tốt, nhanh như iperf3).

Tinh thần của câu chuyện

Không bao giờ bỏ cuộc :-)

Và nếu bạn sử dụng các công cụ như rsynchoặc borgbackupvận chuyển dữ liệu của họ qua SSH và liên kết của bạn chậm, hãy thử ngăn SSH vô hiệu hóa Nagle (như được hiển thị ở trên) - hoặc sử dụng ProxyCommandđể chuyển SSH để kết nối qua nc. Điều này có thể được tự động hóa trong $ HOME / .ssh / config của bạn:

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

... Vì vậy, tất cả các sử dụng "orangepi" trong tương lai làm máy chủ đích trong ssh / rsync / borgbackup từ đó sẽ sử dụng ncđể kết nối (và do đó để Nagle yên).


Cảm ơn, bạn đã cứu cuộc đời tôi! Bạn đã thử liên hệ với những người ssh để hiểu tại sao không có cài đặt để kiểm soát điều này chưa?
static_rtti

1
Tôi rất vui vì những phát hiện của tôi đã giúp bạn là tốt! Đối với việc liên hệ với những người SSH, tôi đã cố gắng, vâng - nhưng cuối cùng không có gì xảy ra: bugzilla.mindrot.org/show_orms.cgi?id=2848
ttsiodras

Đã thêm bản thân vào lỗi. Ai biết được, một cái gì đó có thể xảy ra cuối cùng! Điều tra tuyệt vời, trong mọi trường hợp.
static_rtti
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.