跳转至

2025

小心使用 epoll 负载均衡

记录一个epoll使用问题。在一个新项目写了一个协议转换的小功能。把收到的RTP包封装客户的头再转发出去,用的epoll来收RTP。在公司环境测试正常。可是到了客户现场出现奇怪的现象,每次RTP流只能转发前面几秒的流,后面就不转发了。关闭/打开监听,又是只转发前面几秒。

live c program

程序运行时编译和执行c代码。公司有个基础库可以修改和打印进程的状态,这个库是基于GNU的bfd的,代码比较复杂。在新项目中,发现dlsym(libdl)可以实现类似功能,并且代码非常简洁。
在HN上看到一个很强大的示例也是用的libdl https://nullprogram.com/blog/2014/12/23/, 在运行时改代码,实际也是修改了状态。

Ethernet over USB

NCM 全称“Network Control Model”,是USB定义的用来交换以太网帧。也就是USB网卡,但是相比Windows的RNDS,速度快多了。实测在300MBps以上。而且听说是不安全导致Linux废弃了RNDS的支持。

树莓派

在网上买了一个USB供电板,免焊接的。用大模型生成了USB网卡配置脚本

#!/bin/bash
set -ex

CONFIGFS=/sys/kernel/config
GADGET=usb_gadget/g1
GADGET_DIR=$CONFIGFS/$GADGET
UDC=$(ls /sys/class/udc | head -n1)

# 1. Load libcomposite and mount configfs
modprobe libcomposite
mount -t configfs none $CONFIGFS || true

# 2. Create gadget directory
mkdir -p "$GADGET_DIR"
cd "$GADGET_DIR"

# 3. Set vendor/product IDs (example values—you can adjust)
echo 0x1d6b > idVendor     # Linux Foundation
echo 0x0104 > idProduct    # Multifunction Composite Gadget

# 4. Create English strings
mkdir -p strings/0x409
echo "serial1234" > strings/0x409/serialnumber
echo "MyManuf" > strings/0x409/manufacturer
echo "My NCM Gadget" > strings/0x409/product

# 5. Add NCM function
mkdir -p functions/ncm.usb0
# You may optionally set MACs, interface name, qmult, etc.
# echo "12:34:56:78:9a:bc" > functions/ncm.usb0/host_addr
# echo "12:34:56:78:9a:bd" > functions/ncm.usb0/dev_addr
# echo "usb0" > functions/ncm.usb0/ifname

#!/bin/bash
set -ex

CONFIGFS=/sys/kernel/config
GADGET=usb_gadget/g1
GADGET_DIR=$CONFIGFS/$GADGET
UDC=$(ls /sys/class/udc | head -n1)

# 1. Load libcomposite and mount configfs
modprobe libcomposite
mount -t configfs none $CONFIGFS || true

# 2. Create gadget directory
mkdir -p "$GADGET_DIR"
cd "$GADGET_DIR"

# 3. Set vendor/product IDs (example values—you can adjust)
echo 0x1d6b > idVendor     # Linux Foundation
echo 0x0104 > idProduct    # Multifunction Composite Gadget

# 4. Create English strings
mkdir -p strings/0x409
echo "serial1234" > strings/0x409/serialnumber
echo "MyManuf" > strings/0x409/manufacturer
echo "My NCM Gadget" > strings/0x409/product

# 5. Add NCM function
mkdir -p functions/ncm.usb0
# You may optionally set MACs, interface name, qmult, etc.
# echo "12:34:56:78:9a:bc" > functions/ncm.usb0/host_addr
# echo "12:34:56:78:9a:bd" > functions/ncm.usb0/dev_addr
# echo "usb0" > functions/ncm.usb0/ifname

# 6. Create config and assign function
mkdir -p configs/c.1
mkdir -p configs/c.1/strings/0x409
echo "Config NCM" > configs/c.1/strings/0x409/configuration
ln -s functions/ncm.usb0 configs/c.1/

# 7. Bind to UDC (activate gadget)
echo "$UDC" > UDC

echo "NCM gadget activated"

# Wait for interface to appear
for i in {1..10}; do
    if ip link show usb0 &>/dev/null; then
        break
    fi
    sleep 0.5
done

# move to usb_ncm_up.service
#nmcli connection up usb0-shared || true

NetworkManager 配置

nmcli connection add type ethernet ifname usb0 con-name usb0-shared ipv4.method shared
nmcli connection up usb0-shared

配置systemd自动运行和拉起网口

设备上的USB网卡配置脚本要在NetworkManager启动前执行,而拉起设备要在启动NetworkManager后执行,所以分拆成两个服务。

usb_ncm_gadget.service

[Unit]
Description=Setup USB NCM Gadget
Before=NetworkManager.service
After=local-fs.target sysfs.mount

[Service]
Type=oneshot
ExecStart=/usr/local/bin/usb_ncm_gadget.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

等NetworkManager启动后,拉起网口

usb_ncm_gadget_up.service

[Unit]
Description=Bring Up NM connection for usb0
After=NetworkManager.service
Requires=NetworkManager.service
Requires=usb_ncm_gadget.service
Wants=sys-subsystem-net-devices-usb0.device
After=sys-subsystem-net-devices-usb0.device

[Service]
Type=oneshot
ExecStart=/usr/bin/nmcli connection up usb0-shared

[Install]
WantedBy=multi-user.target

run vllm Qwen3 locally

  • torch==2.5.1+cu121 https://mirrors.aliyun.com/pytorch-wheels/cu121/torch-2.5.1+cu121-cp313-cp313-linux_x86_64.whl
  • vllm_pascal-0.7.2 https://github.com/sasha0552/pascal-pkgs-ci/releases/download/wheels/vllm_pascal-0.7.2-cp38-abi3-linux_x86_64.whl
  • python 3.9
  • uv 0.9.0
  • Tesla P40
  • Ubuntu 22

cuda版本

系统的cuda开发编译环境(/usr/local/cuda)可以不安装,除非需要本地编译。为了避免编译,就下载whl包。 whl包是别人用nvcc编译的二进制包,所以固定了cuda版本。例如torch-2.5.1+cu121表示用了cuda 12.1的编译工具编译的。 而且为了vllm能调用pytorch, vllm就必须和pytorch一样的cu版本,所以我两个都下载了cu121。

安装vllm_pascal后,还需要执行下面的命令,来生成一个新的vllm 包

npx 相当于npm -g, 来安装一个全局的包+cli。

npx install transient-package
transient-package create --source vllm --source-version 0.7.2 --target vllm-pascal --target-version 0.7.2 --output-directory .
uv pip install vllm-0.7.2-py3-none-any.whl
如果没有这一步, vllm命令提示找不到cuda
WARNING 03-14 23:02:38 init.py:26] The vLLM package was not found, so its version could not be inspected. This may cause platform detection to fail.
INFO 03-14 23:02:38 init.py:211] No platform detected, vLLM is running on UnspecifiedPlatform
0.7.2

安装其他依赖

uv pip install ./*.whl命令不会安装依赖,运行一个一个这样报缺包再安装包很烦,所以直接到vllm的代码里找requrement,一次安装。还有到pytorch官网看对应torch==2.5.1的torch-audio torch-vision包。

验证和验证和修改参数

  • 参数类型不匹配,模型配置文件里设定了"torch_dtype": "bfloat16", 而显卡只能支持float16, 所以修改了模型配置。
  • 输出token长度太大,导致vllm不支持, 修改模型配置"max_position_embeddings",从40960改成了10240。

Install CUDA 11.7 toolkit and driver

sudo apt install -y cuda-11-7

uv install 和 uv add 的区别

新项目应该用 uv add, 在用 uv add 之前,需要先执行 * uv init 初始化项目 * uv venv ./.venv 创造虚拟环境

nvidia-smi 输出

| NVIDIA-SMI 580.95.05 Driver Version: 580.95.05 CUDA Version: 13.0 |

  • Driver Version 是GPU硬件驱动
  • CUDA Version 是最大支持的cuda toolkits,也叫运行时。而实际的cuda toolkit version 通过nvcc --version来确认

nvcc

nvcc 是/usr/local/cuda/bin/下的,也就是cuda toolkits,cuda目录通常是软链接到实际的cuda toolkits

lrwxrwxrwx  1 root root   22 Nov  3 18:23 cuda -> /etc/alternatives/cuda
lrwxrwxrwx  1 root root   25 Nov  3 18:23 cuda-11 -> /etc/alternatives/cuda-11
drwxr-xr-x 15 root root 4096 Nov  3 18:23 cuda-11.7
上面的cuda和cuda-11 都链接到cuda-11.7 也就是实际的toolkit version, 并且通过nvcc --version确认版本 nvcc --version nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2022 NVIDIA Corporation Built on Wed_Jun__8_16:49:14_PDT_2022 Cuda compilation tools, release 11.7, V11.7.99 Build cuda_11.7.r11.7/compiler.31442593_0

11.7 小于 nvidia-smi打印的 13.0, 所以这个toolkit是支持的。

cuda capabilities

虽然GPU driver 可以向前兼容,所以 Driver 535可以替代Driver 515。但是硬件架构限定了cuda capabilities,无法向前兼容的。例如Tesla P40的硬件架构是Pascal sm_6.1,而pytorch 的2.7.0 版本不支持sm_6.1了。

Install pytorch

因为 cuda capability限制,知道最高版本支持sm 6.1的pytorch版本是2.6.0, 而pytroch有专门对cuda特定版本的二进制包,在官网上能找到 https://pytorch.org/get-started/previous-versions/
下面可见,2.6.0版本有 cuda-11.8 cuda-12.4 cuda-12.6版本的二进制安装包。

# ROCM 6.1 (Linux only)
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/rocm6.1
# ROCM 6.2.4 (Linux only)
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/rocm6.2.4
# CUDA 11.8
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu118
# CUDA 12.4
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu124
# CUDA 12.6
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu126
# CPU only
pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cpu
然后搜索得知 11.8 要求最低的cuda capability 版本是 5.0, 所以也支持6.1的Tesla P40, 于是安装安装命令是
uv pip install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url https://download.pytorch.org/whl/cu118

Install vllm

vllm 0.9 开始不支持pytorch 2.6了,所以只能用vllm 0.8.5。从githup release找到了二进制安装包 vllm-0.8.5.post1+cu118-cp38-abi3-manylinux1_x86_64.whl
下载到本地后运行 uv pip install ~/vllm-0.8.5.post1+cu118-cp38-abi3-manylinux1_x86_64.whl

vllm serve Qwen3-8B fail: RuntimeError: CUDA error: no kernel image is available for execution on the device

可惜,vllm 0.8.5 不支持 pytorch-cuda-6.1, 不知道这跟pytroch 支持 sm-6.1有什么区别。好在可以手动编译vllm来支持

git clone https://github.com/vllm-project/vllm.git
cd vllm
git checkout v0.8.5
export TORCH_CUDA_ARCH_LIST="6.1"
pip install -e . --no-build-isolation

计算VRAM(Video RAM)

dtype 是参数类型,所需的大小分别是 * FP32 4B 单精度 * BP16 2B 双精度 * FP16 2B 单精度 * INT8 1B * INT4 0.5B 量化 vllm 可以运行时指定量化

而大模型的参数常见有32B,7B。7B的大模型,使用FP32时,需要VRAM=47G。 使用FP16时,只需要27G。

注意,模型的config.torch_dtype是可以改的,最大输出token也是可以改的。

参考

https://github.com/vllm-project/vllm/pull/4290

pytroch 2.7 不支持 6.1: https://www.reddit.com/r/LocalLLaMA/comments/1lrerwe/pytorch_27x_no_longer_supports_pascal_architecture/ vllm 0.11.5 才能支持Qwen/Qwen3-VL-32B-Instruct,否则报错 "'Qwen3VLConfig' object has no attribute 'vocab_size'" https://huggingface.co/Qwen/Qwen3-VL-32B-Instruct/discussions/3

aria2c usage

aria2c支持很多类型的协议下载,http, bt等,支持任务列表,支持前台运行和后台运行, 还支持并发下载(单连接限速)和断点续传(前提是服务器支持)。因为现代浏览器普遍也支持断点续传和并发下载,所以aria2更适合用来批量下载。

前台运行

就类似wget fcurl * 单个文件下载 aria2c "下载链接" * 文件列表下载 aria2c -i list.txt

后台运行

aria2c 本身不支持类似-d/--daemon参数,所以要么在终端执行下面的命令,要么用systed来运行。 * 后台运行 aria2c -c -i list.txt -j4 --load-cookies=rapidshare > /tmp/aria2c.log 2>&1 & * 查看日志 tail -f /var/log/aria2c.log

systemd 和 web/rpc 控制

创建文件~/.config/systemd/user/aria2cd.service,内容如下。这时只能通过rpc协议和aria2c交互,可以用curl和web来发送rpc。

[Unit]
Description=Aria2 Daemon

[Service]
Type=simple
ExecStart=/usr/bin/aria2c --conf-path=/etc/aria2/aria2.conf

[Install]
WantedBy=default.target
我认为最好的纯静态前端YAAW for Chrome

配置代理

有时aria2c运行在没有网络的服务器上,需要代理运行,如下添加http代理

all-proxy=http://ip:port
http-proxy=http://ip:port
https-proxy=http://ip:port

参考

https://www.linuxquestions.org/questions/linux-software-2/putting-aria2c-in-background-769997/

change hostname of pve and vm

以前总不明白,创建虚拟机时填写的域名 example.com 的作用,直到自己用pve创建了多个虚拟机,

change domain of pve

一开始我直接在系统里用hostnamectl来修改,然后发现执行pct命令时,报错没有虚拟机的配置文件,原来pct用hostname来隔离配置

$ pct set 110 --hostname docker.pve                                                        
Configuration file 'nodes/NEW_HOSTNAME/lxc/110.conf' does not exist
看官方的文档, 正确的方法要改好几个文件,于是重新改回去了。

change domain of lxc

这个开始在虚拟机里改,后来知道正确方法是用pct工具

pct set ID --hostname NEW_HOSTNAME

参考

https://virtualizeeverything.com/2021/11/12/how-to-change-the-hostname-of-a-lxc-using-command-line-proxmox-7/

为pve配置dns

我用闲暇台式机安装了pve, 创了一些虚拟机和lxc容器来跑本地部署的应用,例如代码管理的gitea, web应用,nginx反向代理等。 反向代理容器要访问其他容器,也要从外部访问容器,例如我的笔记本。 正好最近工作遇到些dns的问题,于是想自己搭建一个dns服务器。用来实现从外面,从容器都能通过域名来访问。就如同docker compose 里能通过容器名称来访问其他容器一样。反代nginx可以用frp等技术实现公网访问。

实现效果

在笔记本上能访问 gitea.pve, 在nginx反代容器里也能访问 gitea.pve,但是前者得到10地址,后者得到192地址。

网络规划

pve在安装了自动创建了网桥,桥接物理网卡,网络10.8.10.0/24,外面可以直接访问。后我自己创建了虚拟网络 192.168.10.1/24。于是每个容器和虚拟机都有两个地址,我称之外部地址和内部地址。 pve 创建网络位置在 Datacenter-> pve -> system -> network

宿主机器上安装bind9和配置

在AI的帮助下,了解了bind9的view专门用来完成这样的事情。先修改文件 /etc/bind/named.conf.default-zones, 将原来的部分套上 'external view',然后加上自定义根域名'pve'

+view "external" {   
// prime the server with knowledge of the root servers
zone "." {
        type hint;
        file "/usr/share/dns/root.hints";
};

...

+zone "pve" {
+        type master;
+        file "/etc/bind/db.pve.external";
+};

+};

修改文件 /etc/bind/named.conf.local,原本这文件是空的,现在添加 'internal view'

//
// Do any local configuration here
//

// Consider adding the 1918 zones here, if they are not used in your
// organization
//include "/etc/bind/zones.rfc1918";

+acl "internal-nets" {
+    192.168.10.0/24;    // VM subnet
+    127.0.0.1;          // localhost
+};
+
+view "internal" {
+    match-clients { internal-nets; };
+
+    zone "pve" {
+        type master;
+        file "/etc/bind/db.pve.internal";
+    };
};

然后在/etc/bind/db.pve.external 中添加域名记录,ip是外部地址, 在/etc/bind/db.pve.internal添加域名记录,ip是内部地址, 内容如下

$TTL 86400
@   IN  SOA ns1.pve. admin.pve. (
        2025082701 3600 1800 1209600 86400 )
    IN  NS  ns1.pve.

ns1      IN A 192.168.10.1
pve      IN A 192.168.10.1
docker   IN A 192.168.10.101
frp      IN A 192.168.10.102
gitea    IN A 192.168.10.103

然后检查配置和更新配置

rndc reconfig pve
rndc reload

注意: AI说修改了域名记录,要增加SOA号,例如 2025082701 -> 2025082702

dig 验证

  • 在笔记本中dig @pve的ip 查询容器ip
  • 在pve里dig @192.168.10.1 查询容器的ip

外部配合修改dns服务器

上面dig命令指定了dns服务器的ip, 为了让系统自动选择pve上的dns服务器来解析 *.pve 域名,要修改笔记本配置,使用的NetworkManager

nmcli connection modify WIFI_CONNECTION ipv4.dns-search "~pve" 
nmcli connection modify MI-MINI_5G_D300 ipv4.dns 10.8.10.77
nmcli connection modify MI-MINI_5G_D300 ipv4.dns-priority -1 

而我用了有以太网和wifi两个网络,我只修改了wlp5s0。通过前后 resolvectl status 对比,区别如下

Link 3 (wlp5s0)
-    Current Scopes: LLMNR/IPv4 LLMNR/IPv6 mDNS/IPv4 mDNS/IPv6
+    Current Scopes: DNS LLMNR/IPv4 LLMNR/IPv6 mDNS/IPv4 mDNS/IPv6
         Protocols: -DefaultRoute +LLMNR +mDNS -DNSOverTLS DNSSEC=no/unsupported
+       DNS Servers: 10.8.10.77
+        DNS Domain: ~pve
     Default Route: no

这样是做了dns转发,把*.pve的域名转发给10.8.10.77来处理。 效果如下

# ping gitea.pve                  
PING gitea.pve (10.8.10.130) 56(84) bytes of data.
64 bytes from gitea.local (10.8.10.130): icmp_seq=1 ttl=64 time=0.588 ms
64 bytes from gitea.local (10.8.10.130): icmp_seq=2 ttl=64 time=0.767 ms
64 bytes from gitea.local (10.8.10.130): icmp_seq=3 ttl=64 time=2.95 ms

但是我配置 systemd-resolved 的 search 功能不生效,只能全写域名, 不能像在pve容器里那样省略'.pve'。

内部配合修改dns服务器

pve创建容器时,默认拷贝宿主机的dns配置即/etc/resolve.conf,先将其修改成如下,search 的作用是简化域名 gitea.pve 成 gitea

nameserver 192.168.10.1
nameserver 1.0.0.1
search pve local

这样新建的容器的dns自动用搭建好的。 在已创建的容器里找到dns选项,手动修改。效果

# ping gitea
PING gitea.pve (192.168.10.103) 56(84) bytes of data.
64 bytes from 192.168.10.103 (192.168.10.103): icmp_seq=1 ttl=64 time=0.022 ms
64 bytes from 192.168.10.103 (192.168.10.103): icmp_seq=2 ttl=64 time=0.046 ms

download music to ipod shuffle 4

install ipod-shuffle-4g.py

Python script for building the Track and Playlist database for the newer gen IPod Shuffle. Forked from the shuffle-db-ng project and improved my nims11 and NicoHood.

automatically or manually mount ipod

mount /dev/sdb /mnt

copy mp3 or m4a file into ipod

/mnt/iPod_Control/Music/F00/

command of update play list

sudo ./ipod-shuffle-4g.py -d 1 /mnt/

copy music to iPhone in Linux

sudo apt update
sudo apt install ifuse libimobiledevice-utils

pair iphone, unlock and trust pair in the iphone

idevicepair pair

list supported app installed in the iPhone

ifuse --list-apps

mount vlc's app directory to Linux

ifuse --documents org.videolan.vlc-ios ~/iphone_vlc

copy Music file to ~/iphone_vlc

finally fusermount -u ~/iphone_vlc

virtio nic configure multi-queue

virtio虚拟出来的网卡默认是单队列的,ip link 显示如下。 多核系统下,单队列要要阻塞收发, 影响性能, 多队列可以并发,内核也支持(TUN/TAP)。这里记录开启多队列的方式。 因为virtio的限制,队列最大256个。

2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:16:b5:50 brd ff:ff:ff:ff:ff:ff

libvirt 或者 virsh 修改网卡xml文件

<interface type='network'> 
      <source network='default'/> 
      <model type='virtio'/> 
+     <driver name='vhost' queues='N'/> 
</interface> 

qemu

qemu-kvm -netdev tap,id=hn0,queues=N -device virtio-net-pci,netdev=hn0,vectors=2M+2 ...

openstack

openstack image set <IMG_ID> --property hw_vif_multiqueue_enabled=true

验证

ip link 的输出多了mq

$ ip link
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000    

$ ethtool -l enp1s0
Channel parameters for enp1s0:
Pre-set maximums:
RX:             0
TX:             0
Other:          0
Combined:       256
Current hardware settings:
RX:             0
TX:             0
Other:          0
Combined:       40

参考

https://docs.paloaltonetworks.com/vm-series/10-1/vm-series-deployment/set-up-the-vm-series-firewall-on-kvm/performance-tuning-of-the-vm-series-for-kvm/enable-multi-queue-support-for-nics-on-kvm https://cloud.vk.com/docs/en/computing/iaas/how-to-guides/vm-multiqueue https://www.linux-kvm.org/page/Multiqueue

lxc enable tun device

在lxc中运行tailscale时,默认使用tun模式,就是创建一个虚拟网卡,然后用路由规则将出去的流量都路由到tun的虚拟网卡中。但是lxc中默认没有'/dev/net/tun',这时tailscaled 启动时会尝试执行'modprobe tun',结果lxc的内核版本和宿主的不一致,所以导致下面的报错。解决办法有2个,一是使用tailscale的userspace networking(也就是sock5代理),二是将宿主机的tun设备绑定到lxc容器中。

Aug 21 15:20:22 debian-dockers tailscaled[22673]:Linux kernel version: 6.1.10-1-pve
Aug 21 15:20:22 debian-dockers tailscaled[22673]: is CONFIG_TUN enabled in your kernel? `modprobe tun` failed with: modprobe: FATAL: Module tun not found in directory /lib/modules/6.1.10-1-pve
Aug 21 15:20:22 debian-dockers tailscaled[22673]: kernel/drivers/net/tun.ko found on disk, but not for current kernel; are you in middle of a system update and haven't rebooted? found: linux-image-5.10.0-32-rt-am>
Aug 21 15:20:22 debian-dockers tailscaled[22673]: wgengine.NewUserspaceEngine(tun "tailscale0") error: tstun.New("tailscale0"): CreateTUN("tailscale0") failed; /dev/net/tun does not exist

使用userspace networking

修改/etc/default/tailscaled文件,修改FLAG为'FLAGS="--tun=userspace-networking"',这样tailscaled就不会去创建tun设备,而只是暴露一个端口,供应用来使用。

使用tailscale的方法

ALL_PROXY=socks5://localhost:1055/ HTTP_PROXY=http://localhost:1055/ http_proxy=http://localhost:1055/ ./my-app

另外也可以手动启动tailscaled

tailscaled --tun=userspace-networking --socks5-server=localhost:1055 --outbound-http-proxy-listen=localhost:1055 &
tailscale up --auth-key=<your-auth-key>

给lxc容器添加tun设备

将下面两行添加到容器配置文件,例如容器id为100, 则配置文件的路径是/etc/pve/nodes/lxc/100.conf

lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net dev/net none bind,create=dir

lxc enable fuse

当lxc挂载 webdav 文件系统时,依赖fuse 驱动,可以在pve的lxc选项里添加feature。 参考

参考

https://tailscale.com/kb/1112/userspace-networking https://github.com/tailscale/tailscale/issues/6941