PVE运维记录
PBS (Proxmox Backup Server) 部署
Proxmox Backup Server 是一个企业级的虚拟机/容器/物理机备份解决方案,专门针对 PVE 平台备份进行优化,支持备份数据的数据去重、增量备份、压缩、数据完整性校验以及加密。
虽然官方更建议使用物理机来跑 PBS (It is recommended that the server runs on dedicated hardware rather than being run as a virtualized instance),同时也不建议使用 NFS 等网络存储作为存放备份数据的位置,但由于现实条件所迫,本文暂且使用 CT 容器 + NFS 挂载的方式部署 PBS。
首先创建 CT 容器,去掉 Unprivileged container(非特权容器)的勾选,Template 模板选择 Debian 13(可能需要先参考后面修改 pm 文件,否则可能会在创建的时候提示 unsupported Debian version '13.x'),硬盘官方建议至少 8 GB,推荐至少 32GB,CPU 至少 2 核推荐至少 4 核,内存至少 2 GB 推荐至少 4GB,并需要为每 TB 的备份存储相应增加 1GB 内存。创建成功后,为了方便后续挂载 NFS 存储,还需要去 Options 的 Feature 项中勾选 NFS 与 SMB/CIFS。
进入系统首先需要换源和增加 PBS 源:
# 换中科大源
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
# 添加 PBS 源
cat > /etc/apt/sources.list.d/proxmox.sources << EOF
Types: deb
URIs: https://mirrors.ustc.edu.cn/proxmox/debian/pbs
Suites: trixie
Components: pbs-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
# 下载 GPG 公钥
wget https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -O /usr/share/keyrings/proxmox-archive-keyring.gpg
然后便可以开始安装:
apt update
apt install proxmox-backup
这将安装所有的包以及带 ZFS 支持的 Proxmox 内核(虽然照理说 CT 容器不该擅自更新内核,但这么做之后并没有出什么问题)。
接下来开始挂载 NFS 存储,首先需要在 NAS 处配置好,后续采用 <NAS IP>:/volume1/PBS 作为远程挂载目录。接着安装 nfs-common 包并创建挂载文件夹:
apt install nfs-common
mkdir -p /mnt/nas
chown backup:backup /mnt/nas
chmod 775 /mnt/nas
然后将挂载信息写入 /etc/fstab 并应用,接着再次设置权限:
echo "<NAS IP>:/Volume1/PBS /mnt/nas nfs vers=4,nouser,atime,auto,retrans=2,rw,dev,exec 0 0" >> /etc/fstab
mount -a
chmod 775 /mnt/nas
现在可以配置 PBS 了,登录 PBS WebUI,在左侧点击“添加数据存储”,在弹出的窗口中配置“备份路径”为 /mnt/nas,并按需配置其他选项,记录 ID 以备后续使用。
接着在 PBS WebUI 左侧选择“访问控制”按钮,接着在“用户管理”中点击“添加”按钮,设置一个用于备份的用户名如 backup 以及密码。接着点击 “权限”->“添加”->“用户权限”,在弹出的窗口中“路径”填 /mnt/nas,“用户”选择先前创建的用户,“角色”选择 DatastoreAdmin。配置完成即可前往 PVE 中添加 PBS了,在此之前,现在“仪表盘”的右上角点击“显示指纹”按钮复制 PBS 的指纹以备后续使用。
登录 PVE WebUI,选择 Datacenter -> Storage -> Add -> Proxmox Backup Server,Server 填写 PBS 的 IP 地址,Username 填写刚刚创建的 backup@pbs,Datastore 填写刚刚“添加数据存储”时填写的 ID。
接着便可以在备份或创建备份任务时选择 PBS 作为备份存储了。
换源(PVE 8.x)
首先更新证书:
sudo apt install apt-transport-https ca-certificates
接着就可以修改 Debian 源和 PVE 软件源了:
curl -fsSL https://mirrors.ustc.edu.cn/repogen/conf/debian-https-4-bookworm -o /etc/apt/sources.list
echo "deb https://mirrors.ustc.edu.cn/proxmox/debian bookworm pve-no-subscription" > /etc/apt/sources.list.d/pve-enterprise.list
echo "deb https://mirrors.ustc.edu.cn/proxmox/debian/ceph-quincy bookworm no-subscription" > /etc/apt/sources.list.d/ceph.list
wget https://mirrors.ustc.edu.cn/proxmox/debian/proxmox-release-bookworm.gpg -O /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg
apt update
最后修改 CT Templates 源(需要重启服务):
sed -i 's|http://download.proxmox.com|https://mirrors.ustc.edu.cn/proxmox|g' /usr/share/perl5/PVE/APLInfo.pm
systemctl restart pvedaemon.service
时间同步(NTP)
cat >> /etc/chrony/chrony.conf <<'EOF_INNER'
# Aliyun NTP
server ntp1.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp2.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp3.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp4.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp5.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp6.aliyun.com minpoll 4 maxpoll 10 iburst
server ntp7.aliyun.com minpoll 4 maxpoll 10 iburst
EOF_INNER
systemctl restart chrony
chronyc sources -v
解决 unsupported Ubuntu version ‘24.04’ / ‘24.10’ / ‘25.04’ 或 unsupported Debian version ‘13.x’
对于 Ubuntu,首先修改 /usr/share/perl5/PVE/LXC/Setup/Ubuntu.pm:
my $known_versions = {
+ '25.04' => 1, # plucky
+ '24.10' => 1, # oracular
+ '24.04' => 1, # noble
'23.10' => 1, # mantic
'23.04' => 1, # lunar
然后在母鸡的 Shell 中执行:
pveam available
pveam update
接着在母鸡的磁盘 -> CT 模板 -> 模板中选择下载 Ubuntu_24.04,接着再次执行 pveam available 即可。
合并命令:
grep -q "25.04" /usr/share/perl5/PVE/LXC/Setup/Ubuntu.pm || sed -i "/'23.10'/i\ '25.04' => 1, # plucky\n '24.10' => 1, # oracular\n '24.04' => 1, # noble" /usr/share/perl5/PVE/LXC/Setup/Ubuntu.pm
pveam available
pveam update
对于 Debian,直接修改 /usr/share/perl5/PVE/LXC/Setup/Debian.pm 即可:
$version = $1;
- die "unsupported debian version '$version'\n" if !($version >= 4 && $version <= 13);
+ die "unsupported debian version '$version'\n" if !($version >= 4 && $version <= 14);
解决 CT 容器内无法使用 tun 网卡
编辑 /etc/pve/lxc/<CT_ID>.conf,在最后加入:
lxc.cgroup.devices.allow: a
lxc.cap.drop:
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
批量添加:
for f in /etc/pve/lxc/*.conf; do
grep -q "lxc.mount.entry: /dev/net/tun" "$f" || cat <<'EOF' >> "$f"
lxc.cgroup.devices.allow: a
lxc.cap.drop:
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
EOF
done
删除克隆任务提示 TASK ERROR: VM is locked (clone)
在 PVE 中,Full Clone 克隆 VM 模板或者克隆普通 VM 一般要比 Linked Clone 模板要慢,也更容易在克隆硬盘时卡住,因此优先使用 Linked Clone
假如克隆已经卡住,停止后尝试删除 VM 则会提示“TASK ERROR: VM is locked (clone)”,此时需要进入母鸡 Shell 使用 qm unlock <CT_ID> 来解锁容器,再在 UI 控制台中操作删除
缩小 CT 容器磁盘空间
首先关闭 CT 容器,接着定位到磁盘存储位置,其中 PVE 母鸡中的在 /dev/pve/ 下,NFS 挂载存储中的在 /mnt/pve/<storage_name>/images/<CT_ID>/ 下
接着执行下面的命令检查并调整磁盘空间:
e2fsck -fy ./vm-<CT_ID>-disk-0*
resize2fs ./vm-<CT_ID>-disk-0* <size>G
接着缩小文件系统占用空间:
如果磁盘是 PVE 母鸡中的,那么执行:
lvreduce -L <size>G ./vm-<CT_ID>-disk-0*
如果是 NFS 挂载存储中的,那么执行:
qemu-img resize --shrink ./vm-<CT_ID>-disk-0* <size>G
最后修改配置文件 /etc/pve/lxc/<CT_ID>.conf:
- rootfs: <storage_name>:vm-<CT_ID>-disk-0*,size=<old_size>G
+ rootfs: <storage_name>:vm-<CT_ID>-disk-0*,size=<size>G
最后重启 CT 容器,修改完成~
使用模板批量克隆创建 CT 容器
#!/bin/bash
# ========================
# 配置
# ========================
TEMPLATE=1252 # LXC 模板ID
nodes=(pve1 pve2 pve3 pve4) # 节点列表,一空格分隔
ct_per_node=5 # 每个节点创建的容器数量
hostname_prefix="CT" # hostname 前缀,例如 CT.xxx
hostname_start=201 # hostname 起始尾号
ip_prefix="172.16.1" # IP 前缀,例如 172.16.1.xxx
ip_start=201 # IP 起始尾号
ip_netmask="/16" # 子网掩码
gateway="172.16.1.1" # 网关
vmid=$((hostname_start)) # VMID起点默认和hostname_start一致,可单独调整
# ========================
# 执行克隆与配置
# ========================
for node in "${nodes[@]}"; do
for i in $(seq 0 $((ct_per_node - 1))); do
cur_vmid=$((vmid + i))
cur_ip="${ip_prefix}.$((ip_start + i))${ip_netmask}"
cur_hostname="${hostname_prefix}.$((hostname_start + i))"
echo "=== Creating CT $cur_vmid on $node ==="
echo "Hostname: $cur_hostname"
echo "IP: $cur_ip"
# full clone 到目标节点
pct clone $TEMPLATE $cur_vmid \
--full 1 \
--target $node \
--hostname $cur_hostname
# 使用 pvesh 修改网络和hostname(支持跨节点)
pvesh set /nodes/$node/lxc/$cur_vmid/config \
--net0 name=eth0,bridge=vmbr0,firewall=1,gw=$gateway,ip=$cur_ip,type=veth \
--hostname $cur_hostname
# 启动容器
pvesh create /nodes/$node/lxc/$cur_vmid/status/start
done
# 每个节点结束后更新 VMID / IP / hostname 起点
vmid=$((vmid + ct_per_node))
hostname_start=$((hostname_start + ct_per_node))
ip_start=$((ip_start + ct_per_node))
done
母鸡挂载 VM 磁盘
有些时候(比如忘记了虚拟机密码)需要将虚拟机磁盘挂载到母鸡,以便查看和修改其中的文件。首先需要查看虚拟机的存储类型:
qm config <VMID>
若看到类似 scsi0: <storage>:vm-<VMID>-disk-0,size=<SIZE>G 则说明该虚拟机的磁盘类型为 LVM,<storage> 位于 local-lvm 中的逻辑卷路径一般为 /dev/pve/vm-<VMID>-disk-0(可以使用 lvs 确认,后续将以此路径作为示例),<storage> 位于 NFS 挂载的远程磁盘中的逻辑卷路径一般为 /mnt/pve/<storage>/images/<VMID>/vm-<VMID>-disk-0,然后扫描分区:
fdisk -l /dev/pve/vm-<VMID>-disk-0
对于 Linux,输出一般为:
Disk /dev/pve/vm--disk-0: GiB, bytes, sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 65536 bytes / 65536 bytes
Disklabel type: gpt
Disk identifier:
Device Start End Sectors Size Type
/dev/pve/vm--disk-0p1 2048 4095 2048 1M BIOS boot
/dev/pve/vm--disk-0p2 4096 G Linux filesystem
我们需要挂载的是较大的 /dev/pve/vm-<VMID>-disk-0p2 分区,但直接使用 mount 挂载会发现找不到,这是因为对于PVE 的 VM 磁盘,类型通常是 LVM thin volume,因此内核不会自动生成子设备。
此时可以使用 kpartx 来映射分区:
apt update
apt install kpartx -y
kpartx -av /dev/pve/vm-<VMID>-disk-0
然后会输出:
add map pve-vm----disk--0p1 (253:32): 0 2048 linear 253:12 2048
add map pve-vm----disk--0p2 (253:35): 0 linear 253:12 4096
现在子设备成功生成,现在即可挂载数据分区:
mkdir -p /mnt/<VMID>
mount /dev/mapper/pve-vm--<VMID>--disk--0p2 /mnt/<VMID>
然后就可以在 /mnt/<VMID>/ 下查看虚拟机磁盘中的文件了。
卸载与清理:
umount /mnt/<VMID>
kpartx -d /dev/pve/vm-<VMID>-disk-0
rm -r /mnt/<VMID>
若看到类似 scsi0: <storage>:<VMID>/vm-<VMID>-disk-0.qcow2,size=<SIZE>G 则说明该虚拟机的磁盘类型为 QCOW,<storage> 位于 local-lvm 中的逻辑卷路径一般为 /dev/pve/vm-<VMID>-disk-0.qcow2,<storage> 位于 NFS 挂载的远程磁盘中的逻辑卷路径一般为 /mnt/pve/<storage>/images/<VMID>/vm-<VMID>-disk-0.qcow2(后续将以此路径作为示例)。
然后我们使用 qemu-nbd 来把 qcow2 磁盘映射为块设备:
modprobe nbd max_part=16 # 首先激活 nbd 模块
qemu-nbd --connect=/dev/nbd0 /mnt/pve/<storage>/images/<VMID>/vm-<VMID>-disk-0.qcow2
然后就可以使用 lsblk 查看分区了,便可以挂载存在的数据分区(假设为 /dev/nbd0p1):
mkdir -p /mnt/<VMID>
mount /dev/nbd0p1 /mnt/<VMID>
卸载与清理:
umount /mnt/<VMID>
qemu-nbd --disconnect /dev/nbd0
rm -r /mnt/<VMID>
若挂载的磁盘中存在 LVM 分区,则可以尝试激活:
vgscan # 扫描
pvs -o+pv_used # 查看 PV 结构
vgs -o+vg_attr,vg_size,vg_free # 查看 VG 结构
vgchange -ay --partial <VG> # 激活特定的 VG 分区,对于不完整的 VG 分区可以通过加上 --partial 来允许
lvs -a -o+devices,segtype,lv_attr # 接着查看 LV
mount /dev/<VG>/<LV> /mnt/<VMID> # 挂载分区
查看完成,需要卸载分区:
umount /mnt/<VMID>
vgchange -an <VG>
rm -r /mnt/<VMID>
批量迁移容器硬盘
在 CT 容器/ VM 一段时间内不需要运行时,可以考虑将其硬盘迁移至低速归档介质(如NAS)中。对于少量迁移,可以直接在 PVE UI 页面上操作迁移。当需要大量迁移时,可以使用批处理脚本来迁移:
# 因为迁移时间较久,因此使用 screen 来防止终端意外断连导致迁移一半
apt update
apt install screen -y
screen -S move
TARGET_STORAGE="<目标存储在 PVE 中的挂载名>"
for id in <以空格隔开的 CTID / VMID>; do
if qm status $id &>/dev/null; then
if qm status $id | grep -q running; then
echo "VM $id running, skip"
continue
fi
qm config $id | grep -E '^(scsi|virtio|sata|ide)[0-9]+' | while read line; do
disk=$(echo $line | cut -d':' -f1)
store=$(echo $line | cut -d':' -f2 | cut -d',' -f1)
if [[ "$store" == "$TARGET_STORAGE" ]]; then
echo "VM $id $disk already in $TARGET_STORAGE"
continue
fi
echo "Moving VM $id $disk"
qm move_disk $id $disk $TARGET_STORAGE --format qcow2 --delete 1
done
elif pct status $id &>/dev/null; then
if pct status $id | grep -q running; then
echo "CT $id running, skip"
continue
fi
pct config $id | grep -E '^(rootfs|mp[0-9]+)' | while read line; do
vol=$(echo $line | cut -d':' -f1)
store=$(echo $line | cut -d':' -f2 | cut -d',' -f1)
if [[ "$store" == "$TARGET_STORAGE" ]]; then
echo "CT $id $vol already in $TARGET_STORAGE"
continue
fi
echo "Moving CT $id $vol"
pct move-volume $id $vol $TARGET_STORAGE --delete 1
done
fi
done
参考链接: