TL, DR 버전
이 ASCII 변환을 시청하세요또는이 비디오- 그런 다음 이런 일이 발생하는 이유를 알아보세요. 아래 텍스트 설명은 추가 컨텍스트를 제공합니다.
설정 세부정보
ssh
Machine 1은 Armbian을 실행하는 SBC(Orange PI Zero)에 연결된 빌드가 빌드된 Arch Linux 노트북입니다 .- SBC 자체는 IP 192.168.1.150의 이더넷을 통해 DSL 라우터에 연결됩니다.
- 노트북은 공식 Raspberry PI WiFi 어댑터를 사용하여 WiFi를 통해 라우터에 연결됩니다.
- 이더넷을 통해 DSL 라우터에 연결된 다른 노트북(머신 2)이 있습니다.
iperf3를 사용한 벤치마킹 링크
노트북과 SBC 사이의 링크는 다음을 사용하여 벤치마킹했을 때 iperf3
이론적인 56MBits/초보다 낮았습니다.(아파트).
보다 구체적으로 말하자면, 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로의 업로드 속도는 약 24MBits/초이고, -R
SBC( )에서 다운로드하는 속도는 최대 32MBits/초입니다.
SSH를 사용한 벤치마킹
이를 염두에 두고 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/초입니다. 예상되는 24MBits/초 미만).
링크가 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 데몬은 이더넷 링크에서 제공되는 11MBytes/초(100MBits/초)를 (쉽게) 처리할 수 있습니다.
이 작업을 수행할 때 SBC의 CPU가 로드됩니까?
아니요.
그래서...
- 네트워크 측면에서(에 따르면
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배 더 좋습니다.
그렇다면 ProxyCommand를 사용하여 nc를 사용하여 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
에서와 같이 "naked"를 사용할 때 Proxycommand
기본적으로 SSH와 똑같은 작업을 수행하는 것이 아닌가요? 즉, 소켓을 만들고 SBC의 포트 22에 연결한 다음 여기에 SSH 프로토콜을 추가하시겠습니까?
최종 속도에 왜 그렇게 큰 차이가 있습니까?
추신 : 이것은 아닙니다학업 연습- borg
결과적으로 백업이 10배 더 빠르게 실행됩니다. 왜 그런지 모르겠어요 :-)
편집하다: 과정 "영상" 추가여기. ifconfig의 출력에서 전송된 패킷을 세어보면 두 테스트 모두에서 40MB의 데이터를 전송하여 약 30K 패킷으로 전송했다는 것이 분명합니다. 사용하지 않을 때는 훨씬 느립니다 ProxyCommand
.
답변1
댓글로 아이디어를 제공해 주신 분들께 진심으로 감사드립니다. 나는 이 모든 것을 겪었습니다:
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 헤더 서비스 필드.
Arch Linux SSH 클라이언트의 잠재적인 "영리함"을 피하기 위한 Debian Chroot
아니요, 결과는 동일합니다.
마지막으로 - Nagel
발신자에서 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 소켓을 설정합니다. 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 [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만큼 빠릅니다).
스토리 사기
절대 포기하지 마:-)
rsync
SSH를 통해 데이터를 전송하는 것과 같은 도구를 사용 borgbackup
하고 링크가 느린 경우 Nagle을 비활성화하여 SSH를 차단하거나(위 참조) Nagle을 사용 ProxyCommand
하여 SSH를 전환 해 보세요 nc
. 이 작업은 $HOME/에서 자동으로 실행될 수 있습니다. SSH/구성에서:
$ cat .ssh/config
...
Host orangepi
Hostname 192.168.1.150
User root
Port 22
# Compression no
# Cipher None
ProxyCommand nc %h %p
...
nc
...이후 ssh/rsync/borgbackup에서 대상 호스트로 "orangepi"를 사용하는 모든 작업이 연결 에 사용됩니다 (따라서 Nagle을 귀찮게 하지 마십시오).