目 录CONTENT

文章目录

在树莓派上使用百度网盘并限制下载带宽

acmookey
2024-02-06 / 0 评论 / 0 点赞 / 729 阅读 / 11043 字
温馨提示:
本文最后更新于 2024-02-08,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

本文介绍了如何在树莓派(Ubuntu Server 22.04 on Raspberry Pi 4B 8G)上部署百度网盘,并通过脚本(Linux tc命令)的方式对docker容器进行带宽限制。

你也可以从这篇博文中了解如何使用Devv AI编写shell脚本。

找到合适的镜像

目标是使用百度网盘在树莓派上进行离线下载,因此需要找到合适的 arm64镜像,在DockerHub上找寻了一番,发现 johngong/baidunetdisk这个镜像拉取次数也不少并且支持arm64。所以就采用这个镜像来部署容器。

DockerHub搜索记录

容器部署

参考文档

创建目录

  • 工作目录
# mkdir -p /opt/docker_container/baidunetdisk && cd /opt/docker_container/baidunetdisk
# touch .env docker-compose.yml 
  • 下载目录
# mkdir -p /var/download/baidunetdisk/download

docker-compose.yml

/opt/docker_container/baidunetdisk/docker-compose.yml

version: '3.9'
services:
  baidunetdisk:
    container_name: baidunetdisk
    image: johngong/baidunetdisk:v_4.14.6_arm64v8
    user: root # 配置了其他用户启动时报错,配置成root简单粗暴,只有需要离线下载时才启动这个容器
    restart: unless-stopped
    env_file: ./.env
    volumes:
      - /var/download/baidunetdisk/download:/config/baidunetdiskdownload
      - /var/download/baidunetdisk:/config
    network_mode: bridge # 如果对网络有隔离要求,可以单独配置网络
    ports:
      - 5800:5800
      - 5900:5900 #开启VNC端口,可以设置访问密码,增加安全性

环境变量配置

  • 随机生成VNC密码
< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c 32 | xargs -I{} echo {}
  • /opt/docker_container/baidunetdisk/.env
VNC_PASSWORD=your_vnc_password

启动网盘容器

# docker compose up -d

配置Nginx反向代理(可选)

如果有公网访问需要,建议配置反向代理并且开启SSL。

以下Nginx配置供参考(假设域名是 pan.test.com)

server {
  listen 80;
  server_name pan.test.com;

  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl;
  server_name pan.test.com;
  
  ssl_session_cache           shared:SSL:10m;
  ssl_session_timeout         10m;
  ssl_prefer_server_ciphers   on;
  ssl_protocols               TLSv1 TLSv1.1 TLSv1.2;
  # Maximum secure cipher list from https://cipherli.st/. Not support some clients: IF6/XP, IE8/XP, Java 6u45, Java 7u25, OpenSSL 0.9.8y
  ssl_ciphers                 "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
  add_header                  Strict-Transport-Security 'max-age=63072000; includeSubDomains; preload' always;

  ssl_certificate             /path/to/pan.test.com.pem; # 修改为实际证书文件路径
  ssl_certificate_key         /path/to/pan.test.com.key; # 修改为实际密钥文件路径

  location / {
    proxy_pass http://127.0.0.1:5800;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # 以下配置开启Websocket
    proxy_http_version 1.1;
    proxy_set_header   Upgrade $http_upgrade;
    proxy_set_header   Connection upgrade;

    proxy_connect_timeout 3m;
    proxy_send_timeout 3m;
    proxy_read_timeout 3m;

    client_max_body_size 0;

    proxy_buffering off;
    proxy_request_buffering off;
    proxy_redirect off;
  }
}

访问Web地址

  • Web地址:http://your_ip_address:5800
  • 打开以上地址后输入之前配置的VNC_PASSWORD就进入了百度网盘的界面了
    Web界面

docker容器限制带宽

背景说明(可忽略)

百度网盘部署完成后,登录了百度网盘的账号后就可以开始让它静静的在后台下载了。

但是因为所安装版本的设置内没有限制下载速度的选项,这样在进行下载时占满了带宽就很糟糕了。
网盘传输设置选项截图

首先想到的是在路由器上限制树莓派的下行带宽
家庭路由器限制带宽

然后家里的其它设备就有充足的带宽正常上网了,但是树莓派上还部署了其他的服务,百度网盘不限速的话就把树莓派下行带宽全部占满影响了其他服务,因此就需要想办法把 docker 容器进行限速。

经过一番研究,找到几个方法来解决此问题:
1、使用docker-tc镜像作为sidecar来限制容器的带宽,但是该镜像没有arm64的版本,因此未尝试此方案。
2、将百度网盘容器的网络类型修改为 macvlan, 单独分配一个与宿主机同网段的IP,然后在路由器的界面中限制带宽。(理论上这个方法是可行的,但是我的macvlan网络配置未成功,遂放弃...)
3、在宿主机上使用tc命令限制docker容器对应的veth虚拟网络设备的带宽,参考 stackoverflow: https://stackoverflow.com/a/71677515

最终在 Devv AI 的帮助下,我采用方案3实现了对 docker容器的带宽限制,具体见以下shell脚本。

使用tc.sh脚本限制docker容器带宽

/opt/docker_container/baidunetdisk/tc.sh

#!/bin/bash

# 解析参数
while [[ $# -gt 0 ]]; do
    key="$1"
    case $key in
        -c|--container)
        CONTAINER_NAME="$2"
        shift
        shift
        ;;
        -r|--rate)
        RATE="$2"
        shift
        shift
        ;;
        *)
        shift
        ;;
    esac
done

# 检查参数是否为空
if [ -z "$CONTAINER_NAME" ]; then
    echo "缺少参数:容器名称,usage: -c <container_name> or --container <container_name>"
    exit 1
fi
if [ -z "$RATE" ]; then
    echo "缺少参数:宽带速率,usage: -r 24 or --rate 24,其中24代表 24 mbit,即3MB/s"
    exit 1
fi

# 计算burst值
LATENCY=10
RATE_BIT_S=$(echo "scale=10; $RATE * 10^6" | bc -l)
LATENCY_S=$(echo "scale=10; $LATENCY * 10^-3" | bc -l)
BURST=$(echo "scale=0; ($RATE_BIT_S * $LATENCY_S) / (8 * 10^3)" | bc -l)

# 获取容器PID
PID=$(docker inspect -f '{{.State.Pid}}' $CONTAINER_NAME)

# 创建网络命名空间
mkdir -p /var/run/netns
ln -sfT /proc/$PID/ns/net /var/run/netns/$CONTAINER_NAME
# 获取veth名称
VETH_NAME=$(ip link show | grep "link-netns $CONTAINER_NAME" -B 1 | grep -o 'veth[0-9a-f]*')

echo "veth_name ${VETH_NAME}"

# 检查网络设备的规则状态
RULES=$(tc -s qdisc show dev $VETH_NAME 2>&1)

# 设置网络设备的限速规则
if [[ $RULES == *"Cannot find device"* || $RULES == *"qdisc noqueue"* ]]; then
    sudo tc qdisc add dev $VETH_NAME root tbf rate ${RATE}mbit burst ${BURST%.*}kbit latency ${LATENCY}ms
else
    sudo tc qdisc change dev $VETH_NAME root tbf rate ${RATE}mbit burst ${BURST%.*}kbit latency ${LATENCY}ms
fi

# 查看网络设备的队列规则
tc -s qdisc show dev $VETH_NAME

脚本使用方法

以限制百度网盘的docker容器baidunetdisk的带宽为例子, 脚本需要在对应的docker容器启动后执行

  • 设置带宽为 8mbit,即 1MB/s
# sh ./tc.sh -c baidunetdisk -r 8
veth_name veth9a59fb4
qdisc tbf 800a: root refcnt 5 rate 8Mbit burst 1280b lat 10ms
 Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
 backlog 0b 0p requeues 0
  • 设置带宽为 16mbit,即 2MB/s
# sh ./tc.sh -c baidunetdisk -r 16
veth_name veth9a59fb4
qdisc tbf 800a: root refcnt 5 rate 16Mbit burst 2560b lat 10ms
 Sent 592 bytes 3 pkt (dropped 0, overlimits 0 requeues 0)
 backlog 0b 0p requeues 0

按照以上的配置,就可以安心的把它挂在后台下载了 :)。

等到不需要离线下载时,把容器baidunetdisk停掉,对应的虚拟网络设备 veth9a59fb4就自动删除了。

使用Devv AI生成tc.sh

附上通过AI来生成shell脚本的基本步骤:

1、做好背景知识调查,例如:

  • 通过搜索引擎找到可行的方案 stackoverflow问题
  • 学习 tc命令的基本参数使用方法

2、提供需求描述
3、调试AI反馈的脚本,修改需求描述,迭代完成脚本的编写

背景知识调查

解释命令
寻找优化空间
列举qdisc类型
burst参数说明
burst参数计算

需求描述

复制以下需求描述内容Devv AI 去试试提问吧~

        现有以下脚本:
        `脚本1`(通过容器名称获取容器`PID`):
        ```bash
        # docker inspect -f '{{.State.Pid}}' <container_name>
        ```

        `脚本2`(创建网络命名空间):
        ```bash
        # mkdir -p /var/run/netns

        # ln -sfT /proc/<PID>/ns/net /var/run/netns/<container_name>
        ```

        `脚本3`(查看docker容器的网卡名称):
        ```bash
        # ip link show | grep "link-netns <container_name>" -B 1
        130172: veth6749ab5@if130171: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc tbf master docker0 state UP mode DEFAULT group default qlen 1000
            link/ether 1e:e0:4d:31:30:fb brd ff:ff:ff:ff:ff:ff link-netns baidunetdisk
        ```
        从以上脚本中最后一个命令的输出获取网卡名称: veth6749ab5

        `脚本4`(查看网络设备队列规则): `tc -s qdisc show dev <veth_name>`
        此脚本有两种执行结果:
        1、未找到网络设备
        ```bash
        Cannot find device "<veth_name>"
        ```
        或者
        ```bash
        qdisc noqueue ...
        ```
        2、正常显示网络设备
        ```bash
        qdisc tbf 8003: root refcnt 5 rate 24Mbit burst 4Kb lat 10ms
         Sent 150219 bytes 1193 pkt (dropped 0, overlimits 0 requeues 0)
         backlog 0b 0p requeues 0
        ```

        `脚本5`(增加网卡设备的限速规则)
        ```bash
        # sudo tc qdisc add dev veth6749ab5 root tbf rate <rate> burst <burst> latency 10ms
        ```

        `脚本6`(修改网卡设备的限速规则)
        ```bash
        # sudo tc qdisc add dev veth6749ab5 root tbf rate <rate> burst <burst> latency 10ms
        ```


        `公式1`: `burst = (rate_bit_s * latency_s) / 8`

        举例说明:
        假设: rate  = 24 mbit, latency = 10 ms

        将带宽转换为 ‘bit/s’,即 rate_bit_s = 24 * 10^6

        延迟单位为 ‘秒’,即 latency_s = 10 * 10^-3
        然后计算得到的burst值将以字节为 bit。

        根据这个公式,计算出的burst值将是:burst = (24 * 10^6 * 10 * 10^-3) / (8 * 10^3) = 30 kbit

        因此,对于24mbit的带宽和10ms的延迟,一个合理的burst值将是 30kbit。

        --- 

        编写shell脚本,根据以上提供的功能脚本实现以下功能:
        1、输入参数: 
        -c 或 --container: docker容器名称
        -r 或 --rate: 限制带宽(单位mbit)
        2、计算参数 burst:
        固定 latency = 10ms
        通过`公式1` 计算burst参数值
        3、由以上 第1步 和 第2步得到以下三个参数:
        - rate: 单位 mbit,举例 24 mbit
        - burst: 单位 kbit,举例 30 kbit
        - latency: 单位 ms,值固定为 10 ms


        4、设置该docker容器对应的veth网卡的网速,有以下几个要求:
        4.1、基于 `脚本1` 获取容器PID
        4.2、基于 `脚本2` 创建容器的网络命名空间
        4.3、基于 `脚本3` 获取网卡名称 `veth_name`
        4.4、执行 `脚本4` 获取网络设备的规则状态,分为两种 “未找到网络设备” 和 “网络设备已配置限速规则”
        4.4.1、当4.4的执行结果为 “未找到网络设备” 时,执行`脚本5`
        4.4.1、当4.4的执行结果为 “网络设备已配置限速规则” 时,执行`脚本6`

        5、执行`脚本4` 查看网络设备的队列规则,并打印
0

评论区