基于Proxmox VE的KVM虚拟化实战

Proxmox VE是一个既可以运行虚拟机也可以运行容器的虚拟化平台。Proxmox VE基于Debian Linux 开发,而且完全开源。处于灵活性的考虑,Proxmox VE同时支持两种虚拟化技术:KVM虚拟机和LXC容器。以下操作基于KVM虚拟机。

任务背景:实验室有一台闲置的服务器,配置是64GB内存,10GB NVIDIA RTX 2080 Ti 显卡,i9-10900K 20核CPU。组内有师姐需要跑深度学习代码,而师兄的项目需要在服务器上运行数据库。为了实现数据隔离(生产环境不影响开发环境),考虑了一下两种方案:

1.Docker方案。服务器装ubuntu系统,每个任务运行一个docker 容器。优点是运行任务简单,直接docker pull 相应的容器即可。缺点对于不熟悉docker运维的人来说,维护相对复杂,有些任务需要持久化,需要将不同容器的不同目录映射到ubuntu下的目录。这样无法实现数据隔离,违背了初衷,而且无法实现ip直接访问,需要设置端口映射。遂放弃。

2.PVE-KVM方案。宿主机安装基于debianProxmox VE操作系统,不同的任务创建不同的客户机,根据VM IDip区分客户机。缺点是配置复杂。不过生命在于折腾,生命不止,折腾不息。

准备工作

检查CPU是否支持VT-d : https://ark.intel.com/content/www/cn/zh/ark.html

设置国内源

修改PVE Debian 源

1
2
sed -i 's|^deb http://ftp.debian.org|deb https://mirrors.ustc.edu.cn|g' /etc/apt/sources.list
sed -i 's|^deb http://security.debian.org|deb https://mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list

添加 Proxmox 社区源

1
2
source /etc/os-release
echo "deb https://mirrors.ustc.edu.cn/proxmox/debian/pve $VERSION_CODENAME pve-no-subscription" > /etc/apt/sources.list.d/pve-no-subscription.list

删除 Proxmox 企业源

1
2
source /etc/os-release
echo "#deb https://enterprise.proxmox.com/debian/pve $VERSION_CODENAME pve-enterprise" > /etc/apt/sources.list.d/pve-enterprise.list

删除订阅弹窗

1
2
# 执行完成后,浏览器Ctrl+F5强制刷新缓存
sed -Ezi.bak "s/(Ext.Msg.show\(\{\s+title: gettext\('No valid sub)/void\(\{ \/\/\1/g" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && systemctl restart pveproxy.service

修改节点名称

  1. 修改 /etc/hostname/etc/hosts/etc/postfix/mail.cf里的主机名称,保存

  2. 复制 /etc/pve/nodes/旧节点名称/ 所有文件 至 /etc/pve/nodes/新节点名称/ 目录。

    直接cp或者mv的话,这一步可能会报错

    cannot create regular file ‘/etc/pve/nodes/新节点名称/qemu-server/***.conf’: File exists

  3. 如果报错:

    1. 将旧节点 /etc/pve/nodes/旧节点名称/qemu-server/ 目录下的所有 .conf 文件转移到其他地方,比如下载到本地
    2. 删除旧节点目录 /etc/pve/nodes/旧节点名称/
    3. 将备份的 .conf 文件转移回 /etc/pve/nodes/新节点名称/qemu-server/ 目录
  4. 重启PVE,在WEB端可以看到节点名称已更改,且旧节点已删除

  5. 修改节点名称后,可能需要修改一下附加的存储、NFS目录等

添加多块硬盘

方法一

  1. 选择对应节点---->磁盘----->需要添加的磁盘,选择擦除磁盘

    勾选存储目录选项,等待擦除完毕

  2. 检查是否添加成功:依次点击数据中心---->存储

方法二
  1. 分区
1
2
3
fdisk /dev/sde1
> n
> w
  1. 格式化
1
mkfs -t ext4 /dev/sde1
  1. 创建目录
1
mkdir -p /mnt/sde1
  1. 挂载
1
mount -t ext4 /dev/sde1 /mnt/sde1
  1. 写入启动项
1
echo /dev/sde1 /mnt/sde1 ext4 defaults 0 2 >> /etc/fstab

启用内存ballooning

简单理解就是宿主机会收集和客户机未使用的内存,以免内存会浪费。

但启用内存ballooning也有缺点,如:

  1. Ballooning需要客户机操作系统加载virtio_balloon驱动,然而并非每个客户机系统都有该驱动(如windows需要自己安装该驱动)
  2. 如果有大量内存从客户机系统中回收,Ballooning可能会降低客户机操作系统运行的性能。一方面,内存的减少,可能会让客户机中作为磁盘数据缓存的内存被放到气球中,从而客户机中的磁盘I/O访问会增加;另一方面,如果处理机制不够好,也可能让客户机中正在运行的进程由于内存不足而执行失败。
  3. 目前没有比较方便的、自动化的机制来管理ballooning,一般都是采用在QEMU monitor中执行balloon命令来实现ballooning的。没有对客户机的有效监控,没有自动化的ballooning机制,这可能会让生产环境中实现大规模自动化部署并不很方便。
  4. 内存的动态增加或减少,可能会使内存被过度碎片化,从而降低内存使用时的性能。

在PVE中可以简单的开启内存ballooning

  1. 创建虚拟机时,在内存选项勾选Ballooning设备

  2. 查看是否启用成功

    进入客户机,输入以下命令

    1
    lspci | grep "Red Hat"

    如看到 Red Hat, Inc. Virtio memory balloon 说明启用成功

  3. 查看宿主机回收了多少内存

    登录宿主机,如这里要查看编号为102客户机的内存回收情况,输入一下命令:

    1
    2
    qm monitor 102 # 进入qm shell
    qm>info balloon

​ 这里的free_mem就是宿主机从客户机回收的内存大小,可以看到,总共分配了8G,这里回收了大概7G多。在客户机负载比较低的时候还是很有用的。

创建虚拟机

千万注意不要设置自启动!!

选择自己上传的系统镜像,这里以Ubuntu 2004为例。

设置系统选项,注意使用EFI模式

选择CPU时,请根据你的CPU核心数选择。类别选择host。此种模式下客户机下可以看到当前CPU的型号,同时尽量保证虚拟机内的CPU指令集和宿主机内一致。

网络选择:一般情况下选择默认即可。默认是网桥接入。网桥相当于一个软件实现的物理交换机。所有虚拟机共享一个网桥。网桥接入模式下,可以在上一级网关设备查看当前客户机的IP地址。

确认设置,先不要开启虚拟机

配置显卡直通

首先编辑GRUB配置文件:

1
nano /etc/default/grub

开启开启IOMMU支持:

1
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on video=efifb:off"

如果是AMD的CPU:

1
GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on video=efifb:off"

我是Intel的CPU,因此修改为:

更新GRUB:

1
update-grub

添加所需的系统模块(驱动):

1
2
3
4
echo "vfio" >> /etc/modules
echo "vfio_iommu_type1" >> /etc/modules
echo "vfio_pci" >> /etc/modules
echo "vfio_virqfd" >> /etc/modules

接着添加模块(驱动)黑名单,即让GPU设备在下次系统启动之后不使用这些驱动,把设备腾出来给vfio驱动用:

N卡/A卡:

1
2
3
echo "blacklist nouveau" >> /etc/modprobe.d/pve-blacklist.conf
echo "blacklist nvidiafb" >> /etc/modprobe.d/pve-blacklist.conf
echo "blacklist radeon" >> /etc/modprobe.d/pve-blacklist.conf

如果是N卡还需要加入下面的配置到kvm.conf(据老外说是避免一些莫名其妙的错误):

1
2
echo "options vfio_iommu_type1 allow_unsafe_interrupts=1" > /etc/modprobe.d/iommu_unsafe_interrupts.conf
echo "options kvm ignore_msrs=1" > /etc/modprobe.d/kvm.conf

更新内核:

1
update-initramfs -u

重启机器:

1
reboot

重启上来之后检查模块是否正常加载:

1
lsmod | grep vfio

成功的话有类似回显:

查看GPU设备ID:

1
lspci -nn | grep VGA

会有类似的回显:

再执行下面的命令(01:00.0请替换成你的显卡ID)

1
lspci -vvv -s 01:00.0

会有类似的回显:

现在把需要直通给虚拟机的设备ID写到vfio.conf内,注意这次这里的ID不是01:00.0而是自己查看自己设备的ID,ID查看在上面回显里:

1
echo "options vfio-pci ids=10de:1e07" >> /etc/modprobe.d/vfio.conf

然后应用更改:

1
2
update-grub
update-initramfs
更改虚拟机主机类型

其中100 是VM ID

1
qm set 100 -machine pc-q35-3.1
添加显卡

添加PCI设备,注意ID要选择你的电脑对应的显卡ID。注意不要添加成其他设备,如Audio device等。

安装操作系统

此过程略

伪装CPU

由于NVIDIA官方限制在虚拟机中使用显卡,通过修改CPU类型,可以达到欺骗NVIDIA显卡驱动的目的。

1
nano /etc/pve/qemu-server/<你的虚拟机的ID>.conf

找到CPU那一行,删掉,然后改为:

1
2
cpu: host,hidden=1,flags=+pcid
args: -cpu 'host,+kvm_pv_unhalt,+kvm_pv_eoi,hv_vendor_id=NV43FIX,kvm=off'

如下图:

安装显卡驱动

以下操作是在客户机上完成的,根据显卡选择合适的驱动安装。

安装完毕,重启客户机。执行一下命令查看驱动是否安装成功:

1
nvidia-smi

有下图所示回显说明安装成功:

安装qemu-guest-agent

客户机安装qemu-guest-agent,可以在宿主机直接关闭客户机而不需要客户机确认

1
sudo apt install qemu-guest-agent -y

PVE配置WiFi

如果你的pve宿主机有无线网卡,可以按照如下步骤配置无线连接。

安装必要的软件
1
2
sudo apt update
sudo apt install connman -y
设置网络接口
1
nano /etc/network/interfaces
1
2
3
4
5
6
7
8
9
10
11
12
13
14
auto lo
iface lo inet loopback

auto wlp3s0
iface wlp3s0 inet dhcp
wpa-ssid "SSID"
wpa-psk "PASSWORD"
auto vmbr0
iface vmbr0 inet static
address 192.168.0.1
netmask 255.255.255.0
bridge_ports none
bridge_stp off
bridge_fd 0

PVE的四种网络模式

基于网桥的默认配置

网桥相当于一个软件实现的物理交换机。所有虚拟机共享一个网桥,在多个域的网络环境中, 也可以创建多个网桥以分别对应不同网络域。理论上,每个Proxmox VE 最多可以支持4094个网桥。

Proxmox VE 安装程序会创建一个名为vmbr0 的网桥,并和检测到的服务器第一块网卡桥接。

配置文件/etc/network/interfaces 中的对应配置信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
auto lo
iface lo inet loopback

iface enp2s0 inet manual

auto vmbr0
iface vmbr0 inet static
address 192.168.10.2/24
gateway 192.168.10.1
bridge_ports enp2s0
bridge_stp off
bridge_fd 0

iface enp4s0 inet manual

iface wlp3s0 inet manual

在基于网桥的默认配置下,虚拟机看起来就和直接接入物理网络一样。尽管所有虚拟机共享 一根网线接入网络,但每台虚拟机都使用自己独立的MAC 地址访问网络。

注意:这种模式在当前是通过无线网卡连接外网的状态下可能无法正常工作!

路由配置

处于网络安全的考虑,大部分网络服务托管商一旦发现网络接口上有多个mac地址出现,托管商有可能立刻禁用相关网络接口。

也许有些网络服务托管商也允许你注册多个mac地址,这样就可以避免上面提到的问题,但这样就需要注册每一个虚拟机的mac地址,实际操作会很麻烦。

可以采用路由的方式让多个虚拟机共享一个网络端口,这样就可以避免上面提到的问题。这种方式可以确保所有的对外网络通信都使用同一个MAC地址。

常见的应用场景是吗,你有一个可以和外部网络通信的IP地址,假定为(192.51.100.5),还有一个供虚拟机使用的IP地址段(203.0.113.16/29)。针对该场景,推荐使用以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
auto lo
iface lo inet loopback

auto eno0
iface eno0 inet static
address 198.51.100.5/29
gateway 198.51.100.1
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
post-up echo 1 > /proc/sys/net/ipv4/conf/eno1/proxy_arp


auto vmbr0
iface vmbr0 inet static
address 203.0.113.17/28
bridge-ports none
bridge-stp off
bridge-fd 0

路由模式下,需要在PVE内部搭建DHCP服务器,以便在启动虚拟机时,动态获取网络配置(IP,网关等),该模式下无法直接访问PVE内部虚拟机。如果像直接访问,可以通过代理或者端口转发方式。

基于iptables的网络地址转换配置(NAT)

利用地址转换技术,所有虚拟机可以使用内部私有IP 地址,并通过Proxmox VE 服务器的IP来访问外部网络。Iptables 将改写虚拟机和外部网络通信数据包,对于虚拟机向外部网络发出的数据包,将源IP 地址替换成服务器IP 地址,对于外部网络返回数据包,将目的地址替换为对应虚拟机IP 地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
auto lo
iface lo inet loopback

auto eno1
#real IP address
iface eno1 inet static
address 198.51.100.5/24
gateway 198.51.100.1

auto vmbr0
#private sub network
iface vmbr0 inet static
address 10.10.10.1/24
bridge-ports none
bridge-stp off
bridge-fd 0

post-up echo 1 > /proc/sys/net/ipv4/ip_forward
post-up iptables -t nat -A POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE
post-down iptables -t nat -D POSTROUTING -s '10.10.10.0/24' -o eno1 -j MASQUERADE

LVC容器网络配置

在宿主机的网络配置是通过iptables进行NAT的基础下,设置容器的网络地址为静态配置,从而容器可以正常上网。

安装必要软件
1
apt install proxmox-ve postfix open-iscsi -y
下载、导入模板

点此下载

创建LVC容器

注意勾选无特权的容器

配置网络

桥接vmbr0,设置网关和服务器的IP

设置嵌套虚拟化

如果需要在LVC容器中安装docker需要设置此项