0%

OpenVZ无法开启BBR时如何加快scp速度

关于BBR的介绍参见 BBR (很久以前写过详细文档,后来丢了,只找到了一小部分……)

在一些比较老的OpenVZ的vps(宿主机内核低于4.9版本)上,是无法开启bbr的。tcp的拥塞控制,在网络链路存在严重丢包时,会将拥塞窗口乘性减少,所以进行文件传输时,速度会越来越慢。以下介绍一下我面临scp速度慢的问题的解决办法

haproxy-lkl代理ssh端口

市面上有很多代替BBR的方案,大多都大差不差,都是使用lkl(linux kernal library)内核库绕过内核协议处理网络数据,再使用端口代理,实现在某几个端口的传输层能达到bbr的效果。

参考了ovz架构安装bbr内核一文,很方便,一建安装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
恭喜!BBR 安装完成并成功启动

已加速的端口: 22

你可以通过修改文件:
/usr/local/haproxy-lkl/etc/port-rules

来配置需要加速的端口或端口范围。

请使用 systemctl {start|stop|restart} haproxy-lkl
来 {开启|关闭|重启} 服务

服务已自动加入开机启动,请放心使用。

如果这个脚本帮到了你,你可以请作者喝瓶可乐:
https://blog.kuoruan.com/donate

享受加速的快感吧!

但也有缺点,这个端口代理只能影响外部访问该端口的,不能影响由内向外的。我尝试过打开某个本地端口,再通过以下代码将文件scp到外面,但是并没有用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 指定本地端口
# 没有用,haproxy-lkl并监听由本地端口发起的连接
def transfer_and_delete_file_using_key_and_local_port(local_file_path, remote_host, remote_user, private_key_path,
remote_file_path, local_port):
private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
local_host = "0.0.0.0" # 表示从任意本地网络接口连接

# 创建 SSH 客户端对象
ssh = paramiko.SSHClient()
# 自动添加主机密钥
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((local_host, local_port))
sock.connect((remote_host, 22))
transport = paramiko.Transport(sock)
try:
transport.start_client()
transport.auth_publickey(remote_user, private_key)
sftp = paramiko.SFTPClient.from_transport(transport)
# 检查远程文件路径的目录是否存在,如果不存在则创建
remote_dir = os.path.dirname(remote_file_path)
try:
sftp.listdir(remote_dir)
except IOError:
sftp.mkdir(remote_dir)
sftp.put(local_file_path, remote_file_path)
sftp.close()
logging.info(f"local file scp finish, {local_file_path}")
# 删除本地文件
os.remove(local_file_path)
logging.info(f"local file deleted: {local_file_path}")
except Exception as e:
traceback.print_exc()
logging.error(f"exception on {local_file_path} with error {e}")
finally:
# 关闭 Transport 连接
transport.close()

http请求呼叫远端pull

我干脆就不push了,让远端pull。这里面只有一个问题,就是网络链路不稳定容易导致('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))错误

解决方案是pull接口幂等+重试

本地:

1
2
3
4
5
6
7
8
9
10
11
def ask_remote_scp_local_file_and_remote():
while True:
try:
resp = session.post("http://xxx", json=req,timeout=3600)
if resp:
break
except Exception as e:
logging.error("ask remote scp fail," + e.__str__())
if resp.status_code == 200 and resp.text == 'success':
os.remove(file_path)
return

远端:
使用python-redis-lock包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def scp_from_remote_using_key(local_file_path, remote_host, remote_user, private_key_path, remote_file_path, port):
# 创建 SSH 客户端
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
# 加载私钥
private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
# 连接到远程服务器
ssh.connect(remote_host, port=port, username=remote_user, pkey=private_key)
# 使用 sftp 传输文件
sftp = ssh.open_sftp()
# 检查远程文件路径的目录是否存在,如果不存在则创建
local_dir = os.path.dirname(remote_file_path)
try:
os.listdir(local_dir)
except IOError:
os.mkdir(local_dir)

sftp.get(remote_file_path, local_file_path)
sftp.close()
logging.info(f"remote file scp finish, {remote_file_path}")
except Exception as e:
logging.error(f"exception on {remote_file_path} with error {e}")
finally:
# 关闭 SSH 连接
ssh.close()
def lock_and_scp(local_file_path, remote_host, remote_user, private_key_path, remote_file_path, port):
m2 = hashlib.md5()
m2.update(remote_file_path.encode('utf-8'))
md5 = m2.hexdigest()
lock = redis_lock.Lock(redis_connect, "lock_" + md5, expire=600)

if lock.acquire(blocking=True):
if redis_connect.get("finished_" + md5) is None:
scp_from_remote_using_key(local_file_path, remote_host, remote_user, private_key_path, remote_file_path,
port)
redis_connect.set("finished_" + md5, "1")
redis_connect.expire("finished_" + md5, 600)
lock.release()