TL; DR 버전
이 ASCII 캐스트 또는 이 비디오를 시청 한 다음 이러한 상황이 발생하는 이유를 생각해보십시오. 다음의 텍스트 설명은 더 많은 컨텍스트를 제공합니다.
설정 세부 사항
- Machine 1은 Arch Linux 랩톱으로,
ssh
Armbian 실행 SBC (오렌지 PI Zero)에 연결됩니다. - SBC 자체는 이더넷을 통해 DSL 라우터에 연결되며 IP는 192.168.1.150입니다.
- 랩탑은 공식 Raspberry PI WiFi 동글을 사용하여 WiFi를 통해 라우터에 연결됩니다.
- 이더넷을 통해 DSL 라우터에 연결된 다른 랩탑 (기계 2)도 있습니다.
iperf3와의 링크 벤치마킹
로 벤치마킹 할 때 iperf3
랩탑과 SBC 간의 링크는 이론적으로 56MBits / sec 미만입니다. 이는 매우 “붐비는 2.4GHz” (아파트 빌딩) 내의 WiFi 연결이기 때문 입니다.
보다 구체적으로 : 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 / sec에 도달하고 다운로드 ( -R
)는 32MBits / sec에 도달합니다.
SSH로 벤치마킹
그렇다면 SSH가 어떻게 작동하는지 봅시다. 내가 처음 사용하는 경우이 게시물을 주도하는 문제를 경험 한 rsync
과 borgbackup
: 그럼 같은 링크에 어떻게 SSH가 수행을 보자 … 둘 전송 계층으로 ssh를 사용하여 –
# cat /dev/urandom | \
pv -ptebar | \
ssh root@192.168.1.150 'cat >/dev/null'
20.3MiB 0:00:52 [ 315KiB/s] [ 394KiB/s]
글쎄요, 그건 끔찍한 속도입니다! 많은 느린 예상 연결 속도보다 …
(혹시 인식하지 pv -ptevar
:. 그것을 통과하는 데이터의 현재 및 평균 속도를 표시이 경우, 우리가 보는 그에서 읽기 /dev/urandom
와 SBC에 SSH를 통해 데이터를 전송 평균 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 root@192.168.1.150 'cat >/dev/null'
240MiB 0:00:31 [10.7MiB/s] [7.69MiB/s]
SBC의 SSH 데몬은 이더넷 링크가 제공하는 11MBytes / sec (즉, 100MBits / sec)를 (쉽게) 처리 할 수 있습니다.
그리고이 작업을 수행하는 동안 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 배 더 빠릅니다.
nc를 사용하기 위해 ProxyCommand를 사용하여 SSH를 실행하면 어떻게됩니까?
# 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]
공장! 10 배속.
이제는 약간 혼란스러워합니다. “naked”를 nc
로 Proxycommand
사용할 때 기본적으로 SSH와 동일한 작업을 수행하지 않습니까? 즉, 소켓을 만들고 SBC의 포트 22에 연결 한 다음 SSH 프로토콜을 삽니까?
결과 속도에 왜 이렇게 큰 차이가 있습니까?
PS 이것은 아니었다 학술 운동 – 내 borg
백업이 10 배 빠른 이것 때문에 실행됩니다. 난 그냥 왜 🙂 몰라
편집 : 여기 에 프로세스의 “비디오”를 추가했습니다 . ifconfig의 출력에서 전송 된 패킷을 계산할 때 두 테스트에서 40MB의 데이터를 전송하여 약 30K 패킷으로 전송한다는 것은 분명합니다 ProxyCommand
.
답변
의견에 아이디어를 제출 한 사람들에게 감사드립니다. 나는 그들 모두를 겪었다.
tcpdump로 패킷 기록 및 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
기록 된 패킷에서 중요성의 차이는 없었다.
트래픽 조절 확인
이것에 대해 전혀 몰랐습니다 – “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 헤더의 서비스 필드).
아치 리눅스 SSH 클라이언트에서 잠재적 인 “영리함”을 피하기위한 데비안 Chroot
아뇨, 같은 결과입니다.
마지막으로-Nagle
발신자에서 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를 사용하여이 특정 syscall을 “삭제”했습니다.
$ 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%
완벽한 속도 (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을 그대로 둡니다.