web-dev-qa-db-ja.com

SSH速度はProxyCommandによって大幅に改善されました-しかし、なぜですか?

TL; DRバージョン

これを見るASCIIキャスト または このビデオ -次に、これが発生している理由を考え出します。次のテキストの説明は、より多くのコンテキストを提供します。

セットアップの詳細

  • マシン1はArch Linuxラップトップで、その上にsshが生成され、Armbian実行SBC(オレンジPIゼロ)に接続します。
  • SBC自体はイーサネット経由でDSLルーターに接続され、IPは192.168.1.150です。
  • ラップトップは、公式のRaspberry Pi WiFiドングルを使用してWiFi経由でルーターに接続されています。
  • イーサネット経由でDSLルーターに接続された別のラップトップ(マシン2)もあります。

Topology

iperf3によるリンクのベンチマーク

iperf3でベンチマークすると、ラップトップとSBCの間のリンクは理論上の56 MBits /秒未満になります。これは、非常に「混雑した2.4 GHz」内のWiFi接続であるためです(アパートの建物)

より具体的には、SBCでiperf3 -sを実行した後、次のコマンドがラップトップで実行されます。

# 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へのアップロードは約24MBits /秒に達し、SBCからのダウンロード(-R)は32MBits /秒に達します。

SSHによるベンチマーク

それでは、SSHの料金を見てみましょう。 rsyncborgbackupを使用するときにこの投稿に至った問題を最初に経験しました-どちらもトランスポート層としてSSHを使用しています...それでは、SSHが同じリンクでどのように実行されるかを見てみましょう:

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

まあ、それはひどいスピードです!予想されるリンク速度よりもはるかに遅い...pv -ptevarがわからない場合:通過するデータの現在および平均の速度が表示されます。この場合、 /dev/urandomから読み取り、SSH経由でSBCにデータを送信すると、平均で400KB/sに達します。つまり、3.2MBits/secであり、予想される24MBits/secよりもはるかに少ない数値です。)

リンクが容量の13%で実行されているのはなぜですか?

それはおそらく私たちの/dev/urandomのせいですか?

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

いいえ、違います。

それはおそらくSBC自体ですか?おそらく処理が遅すぎますか?同じSSHコマンド(つまり、SBCにデータを送信)を実行してみますが、今回はイーサネット経由で接続されている別のマシン(マシン2)から実行します。

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

いいえ、これは正常に機能します。SBCのSSHデーモンは、イーサネットリンクが提供する11Mバイト/秒(つまり、100MBビット/秒)を(簡単に)処理できます。

これを実行している間にSBCのCPUがロードされますか?

CPU is easily handling it

いいえ。

そう...

  • ネットワーク的に(iperf3に従って)、10倍の速度で実行できるはずです。
  • cPUは負荷に簡単に対応できます
  • ...そして、他の種類のI/O(ドライブなど)は関与しません。

一体何が起こっているのですか?

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倍良い-速度。

では、ncを使用するためにProxyCommandを使用してSSHを実行するとどうなりますか?

# 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]

動作します! 10倍速。

ここで少し混乱しています。「裸の」ncProxycommandとして使用する場合、基本的にSSHとまったく同じことをしていませんか?つまり、ソケットを作成し、SBCのポート22に接続してから、SSHプロトコルをそのソケットに押し込みますか?

結果の速度にこのような大きな違いがあるのはなぜですか?

追伸これはアカデミックエクササイズではありませんでした-このため、borgバックアップは10倍速く実行されました。理由がわかりません:-)

[〜#〜] edit [〜#〜]:プロセスの "ビデオ" here を追加しました。 ifconfigの出力から送信されたパケットをカウントすると、両方のテストで40MBのデータが送信され、約30Kパケットで送信されていることがわかります。ProxyCommandを使用しない場合は、はるかに遅くなります。

14
ttsiodras

コメントでアイデアを提出してくれた人々に感謝します。私はそれらすべてを通過しました:

tcpdumpを使用してパケットを記録し、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

記録されたパケットに重要性の違いはありませんでした。

トラフィックシェーピングの確認

これについては何も知りませんでした-"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クライアントの潜在的な「賢さ」を回避するため

いいえ、同じ結果です。

ついに-ネーグル

送信者でstraceを実行しています...

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>

これにより、Nagleのアルゴリズムを無効にするようにSSHソケットが設定されます。あなたはグーグルしてそれについてすべて読むことができます-つまり、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 [email protected] 'cat >/dev/null'
No, Mr Nagle stays.
No, Mr Nagle stays.
 100MiB 0:00:29 [3.38MiB/s] [3.38MiB/s] [================================>] 100%   

そこに-完璧な速度(まあ、iperf3と同じくらい速い)。

物語の士気

あきらめないで :-)

また、SSH経由でデータを転送するrsyncborgbackupなどのツールを使用していて、リンクが遅い場合は、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
...

...将来的にssh/rsync/borgbackupでターゲットホストとして「orangepi」を使用する場合は、今後はncを使用して接続します(したがってNagleはそのままにします)。

14
ttsiodras