TL; DRバージョン
これを見るASCIIキャスト または このビデオ -次に、これが発生している理由を考え出します。次のテキストの説明は、より多くのコンテキストを提供します。
セットアップの詳細
ssh
が生成され、Armbian実行SBC(オレンジPIゼロ)に接続します。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の料金を見てみましょう。 rsync
とborgbackup
を使用するときにこの投稿に至った問題を最初に経験しました-どちらもトランスポート層として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がロードされますか?
いいえ。
そう...
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倍良い-速度。
では、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倍速。
ここで少し混乱しています。「裸の」nc
をProxycommand
として使用する場合、基本的にSSHとまったく同じことをしていませんか?つまり、ソケットを作成し、SBCのポート22に接続してから、SSHプロトコルをそのソケットに押し込みますか?
結果の速度にこのような大きな違いがあるのはなぜですか?
追伸これはアカデミックエクササイズではありませんでした-このため、borg
バックアップは10倍速く実行されました。理由がわかりません:-)
[〜#〜] edit [〜#〜]:プロセスの "ビデオ" here を追加しました。 ifconfigの出力から送信されたパケットをカウントすると、両方のテストで40MBのデータが送信され、約30Kパケットで送信されていることがわかります。ProxyCommand
を使用しない場合は、はるかに遅くなります。
コメントでアイデアを提出してくれた人々に感謝します。私はそれらすべてを通過しました:
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経由でデータを転送するrsync
やborgbackup
などのツールを使用していて、リンクが遅い場合は、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はそのままにします)。