CNCF Cloud Native Survey 2021:第1部分现在开始了!
问卷链接(https://www.surveymonkey.com/r/GRMM6Y2)
作者:Mark DeNeve
图形用户界面(GUI)在过去几年中已经出现,大多数现代桌面环境都希望某种形式的 GPU 加速,以便给你一个无缝的用户体验。如果你试过在 KubeVirt 上运行像 Windows 10 这样的东西,你可能会注意到桌面体验有点慢。这是由于 Windows 10 对 GPU 加速的依赖。此外,许多应用程序也正在利用 GPU 加速,它甚至可以用于基于 web 的应用程序,如“FishGL”:
如果没有 GPU 硬件加速,将极大地影响虚拟机的用户体验。
从嵌入英特尔图形处理器的第五代 Intel Core 处理器开始,可以在多个虚拟机之间共享图形处理器。在 Linux 中,GPU 的这种共享通常是通过使用中介 GPU 设备(也称为 vGPU)来实现的。KubeVirt 从 2019 年 v0.22.0 版本开始就支持 GPU 的使用,包括 GPU 直通(passthrough)和 vGPU。这种支持是以一个特定的供应商为中心的,并且只适用于昂贵的企业级卡,并且需要额外的许可。KubeVirt 0.40[1]支持检测和分配基于 Intel 的 vGPU。从 4.19 版本开始,Linux 内核就支持创建这些虚拟化 Intel GPU。这对你来说意味着什么?你不再需要额外的驱动程序或许可证来测试 GPU 加速的虚拟机。
你可以创建的 Intel vGPU 的总数取决于你的特定硬件以及对更改显卡孔径大小和 BIOS 中共享显卡内存的支持。有关此的更多细节,请参阅英特尔 GVTg wiki 中的创建 vGPU(仅 KVMGT)[2]。最小配置的设备通常可以制造至少两个 vGPU 设备。
你可以在运行 KubeVirt v0.40.0 或更高版本的任何 Kubernetes 集群上复制这项工作,但是加载内核模块和启用虚拟设备所需的步骤将根据 Kubernetes 集群所运行的底层操作系统而有所不同。为了演示如何启用该特性,我们将使用一个使用 Fedora 32 和 minikube 构建的一体化 Kubernetes 集群。
请注意:这篇博文是一个更高级的主题,并且假设你对 Linux 和 Kubernetes 有一定的了解。
在我们开始之前,你需要一些东西来使用 Intel GPU:
为了在 Fedora 32 上使用 minikube,我们将在整个演示中安装多个应用程序。此外,我们将配置工作站以使用 cgroups v1,我们将更新防火墙,以允许与 Kubernetes 集群以及任何托管应用程序进行适当的通信。最后,我们将根据 minikube 裸金属安装说明禁用 SELinux:
请注意:这篇文章假设我们从 Fedora 32 的新安装开始。如果使用已配置的现有 Fedora 32 工作站,可能会有一些软件冲突。
sudo dnf update -y
sudo dnf install -y pciutils podman podman-docker conntrack tigervnc rdesktop
sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=0"
# Setup firewall rules to allow inbound and outbound connections from your minikube cluster
sudo firewall-cmd --add-port=30000-65535/tcp --permanent
sudo firewall-cmd --add-port=30000-65535/udp --permanent
sudo firewall-cmd --add-port=10250-10252/tcp --permanent
sudo firewall-cmd --add-port=10248/tcp --permanent
sudo firewall-cmd --add-port=2379-2380/tcp --permanent
sudo firewall-cmd --add-port=6443/tcp --permanent
sudo firewall-cmd --add-port=8443/tcp --permanent
sudo firewall-cmd --add-port=9153/tcp --permanent
sudo firewall-cmd --add-service=dns --permanent
sudo firewall-cmd --add-interface=cni-podman0 --permanent
sudo firewall-cmd --add-masquerade --permanent
sudo vi /etc/selinux/config
# change the "SELINUX=enforcing" to "SELINUX=permissive"
sudo setenforce 0
sudo systemctl enable sshd --now
我们现在将安装 CRIO 运行时:
sudo dnf module enable -y cri-o:1.18
sudo dnf install -y cri-o cri-tools
sudo systemctl enable --now crio
为了使用 Intel vGPU 驱动程序,我们需要对我们的一体化主机做一些更改。下面的命令假设你使用的是基于 Fedora 的主机。如果你使用的是不同的基本操作系统,请确保更新针对该特定发行版的命令。
以下命令将执行以下操作:
sudo sh -c "echo kvmgt > /etc/modules-load.d/gpu-kvmgt.conf"
sudo grubby --update-kernel=ALL --args="intel_iommu=on i915.enable_gvt=1"
sudo shutdown -r now
重启后检查,以确保已经加载了适当的内核模块:
$ sudo lsmod | grep kvmgt
kvmgt 32768 0
mdev 20480 2 kvmgt,vfio_mdev
vfio 32768 3 kvmgt,vfio_mdev,vfio_iommu_type1
kvm 798720 2 kvmgt,kvm_intel
i915 2494464 4 kvmgt
drm 557056 4 drm_kms_helper,kvmgt,i915
现在我们将创建我们的 vGPU 设备。这些虚拟设备是通过将 GUID 回送到由 Intel 驱动程序创建的系统设备来创建的。每次系统引导时都需要执行此操作。最简单的方法是使用在每次启动时都运行的 systemd 服务。在创建这个 systemd 服务之前,我们需要验证你的 Intel 显卡的 PCI ID。为此,我们将使用 lspci 命令:
$ sudo lspci
00:00.0 Host bridge: Intel Corporation Device 9b53 (rev 03)
00:02.0 VGA compatible controller: Intel Corporation Device 9bc8 (rev 03)
00:08.0 System peripheral: Intel Corporation Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th/8th Gen Core Processor Gaussian Mixture Model
请注意,在上面的输出中,英特尔图形处理器是在“00:02.0”上。现在创建/etc/systemd/system/gvtg-enable.service,但请确保更新适合你的机器的 PCI ID:
cat > ~/gvtg-enable.service << EOF
[Unit]
Description=Create Intel GVT-g vGPU
[Service]
Type=oneshot
ExecStart=/bin/sh -c "echo '56a4c4e2-c81f-4cba-82bf-af46c30ea32d' > /sys/devices/pci0000:00/0000:00:02.0/mdev_supported_types/i915-GVTg_V5_8/create"
ExecStart=/bin/sh -c "echo '973069b7-2025-406b-b3c9-301016af3150' > /sys/devices/pci0000:00/0000:00:02.0/mdev_supported_types/i915-GVTg_V5_8/create"
ExecStop=/bin/sh -c "echo '1' > /sys/devices/pci0000:00/0000:00:02.0/56a4c4e2-c81f-4cba-82bf-af46c30ea32d/remove"
ExecStop=/bin/sh -c "echo '1' > /sys/devices/pci0000:00/0000:00:02.0/973069b7-2025-406b-b3c9-301016af3150/remove"
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
sudo mv ~/gvtg-enable.service /etc/systemd/system/gvtg-enable.service
sudo systemctl enable gvtg-enable --now
请注意:上面的 systemd 服务将创建两个 vGPU 设备,你可以使用额外的唯一 guid 重复命令,最多 8 个 vGPU,如果你的硬件支持它。
我们可以通过查看/sys/devices/pci0000:00/0000:00:02.0/目录来验证 vGPU 设备是否已经创建。
$ ls -lsa /sys/devices/pci0000:00/0000:00:02.0/56a4c4e2-c81f-4cba-82bf-af46c30ea32d
total 0
lrwxrwxrwx. 1 root root 0 Apr 20 13:56 driver -> ../../../../bus/mdev/drivers/vfio_mdev
drwxr-xr-x. 2 root root 0 Apr 20 14:41 intel_vgpu
lrwxrwxrwx. 1 root root 0 Apr 20 14:41 iommu_group -> ../../../../kernel/iommu_groups/8
lrwxrwxrwx. 1 root root 0 Apr 20 14:41 mdev_type -> ../mdev_supported_types/i915-GVTg_V5_8
drwxr-xr-x. 2 root root 0 Apr 20 14:41 power
--w-------. 1 root root 4096 Apr 20 14:41 remove
lrwxrwxrwx. 1 root root 0 Apr 20 13:56 subsystem -> ../../../../bus/mdev
-rw-r--r--. 1 root root 4096 Apr 20 13:56 uevent
注意“mdev_type”指向“i915-GVTg_V5_8”,这将在稍后我们配置 KubeVirt 以检测 vGPU 时发挥作用。
现在我们将把 Kubernetes 安装到我们的 Fedora 工作站。Minikube[7]将帮助我们快速建立 Kubernetes 集群环境。我们将从 minikube 和 kubectl 的最新版本开始。
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
VERSION=$(minikube kubectl version | head -1 | awk -F', ' {'print $3'} | awk -F':' {'print $2'} | sed s/\"//g)
sudo install ${HOME}/.minikube/cache/linux/${VERSION}/kubectl /usr/local/bin
我们将使用 minikube 驱动程序“none”,它将直接安装 Kubernetes 到这台机器上。这将允许你维护通过重新引导构建的虚拟机的一个副本。在这篇文章的后面,我们将在“/data”中为虚拟机存储创建持久卷。如前所述,确保在“/data”中至少有 50Gb 的空闲空间来完成此设置。安装 minikube 需要几分钟时间。
$ sudo mkdir -p /data/winhd1-pv
$ sudo minikube start --driver=none --container-runtime=crio
? minikube v1.19.0 on Fedora 32
✨ Using the none driver based on user configuration
? Starting control plane node minikube in cluster minikube
? Running on localhost (CPUs=12, Memory=31703MB, Disk=71645MB) ...
ℹ️ OS release is Fedora 32 (Workstation Edition)
? Preparing Kubernetes v1.20.2 on Docker 20.10.6 ...
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
? Configuring local host environment ...
? Verifying Kubernetes components...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
? Enabled addons: storage-provisioner, default-storageclass
? Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
为了使我们与 Kubernetes 的交互更容易一些,我们需要复制一些文件并更新我们的.kube/config:
mkdir -p ~/.minikube/profiles/minikube
sudo cp -r /root/.kube /home/$USER
sudo cp /root/.minikube/ca.crt /home/$USER/.minikube/ca.crt
sudo cp /root/.minikube/profiles/minikube/client.crt /home/$USER/.minikube/profiles/minikube
sudo cp /root/.minikube/profiles/minikube/client.key /home/$USER/.minikube/profiles/minikube
sudo chown -R $USER:$USER /home/$USER/.kube
sudo chown -R $USER:$USER /home/$USER/.minikube
sed -i "s/root/home\/$USER/" ~/.kube/config
一旦完成了 minikube 的安装,请验证一切是否正常工作。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kubevirt Ready control-plane,master 4m5s v1.20.2
只要你没有得到任何错误,你的基础 Kubernetes 集群就准备好了。
我们的一体化 Kubernetes 集群现在可以安装 KubeVirt 了。使用 minikube 插件管理器,我们将安装 KubeVirt 到我们的集群:
$ sudo minikube addons enable kubevirt
$ kubectl -n kubevirt wait kubevirt kubevirt --for condition=Available --timeout=300s
此时,我们需要更新集群中的 KubeVirt 实例。我们需要配置 KubeVirt,通过给它一个要查找的 mdevNameSelector 和一个要分配给它的 resourceName 来检测 Intel vGPU。mdevNameSelector 来自于“mdev_type”,我们之前在创建两个虚拟 GPU 时确定了它。当 KubeVirt 设备管理器发现此 mdev 类型的实例时,它将记录此信息,并用标识的 resourceName 标记该节点。稍后在启动虚拟机时,我们将使用这个 resourceName。
cat > kubevirt-patch.yaml << EOF
spec:
configuration:
developerConfiguration:
featureGates:
- GPU
permittedHostDevices:
mediatedDevices:
- mdevNameSelector: "i915-GVTg_V5_8"
resourceName: "intel.com/U630"
EOF
kubectl patch kubevirt kubevirt -n kubevirt --patch "$(cat kubevirt-patch.yaml)" --type=merge
现在我们需要等待 KubeVirt 重新加载它的配置。
现在 KubeVirt 已经安装并运行,让我们确保 vGPU 被正确识别。使用 kubectl describe node 命令描述 minikube 节点,并查找“Capacity”部分。如果 KubeVirt 正确地检测到 vGPU,你将看到一个容量值大于 0 的“intel.com/U630”条目。
$ kubectl describe node
Name: kubevirt
Roles: control-plane,master
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
...
Capacity:
cpu: 12
devices.kubevirt.io/kvm: 110
devices.kubevirt.io/tun: 110
devices.kubevirt.io/vhost-net: 110
ephemeral-storage: 71645Mi
hugepages-1Gi: 0
hugepages-2Mi: 0
intel.com/U630: 2
memory: 11822640Ki
pods: 110
在这里,intel.com/U630——其中两个是可用的。现在我们只需要一个虚拟机来消耗它们。
为了安装 Windows 10,我们需要将 Windows 10 安装 ISO 上传到集群中。这可以通过使用容器数据导入器(Containerized Data Importer)来促进。以下步骤摘自“容器数据导入器(CDI)实验[8]”网页:
export VERSION=$(curl -s https://github.com/kubevirt/containerized-data-importer/releases/latest | grep -o "v[0-9]\.[0-9]*\.[0-9]*")
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-operator.yaml
kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-cr.yaml
kubectl -n cdi wait cdi cdi --for condition=Available --timeout=300s
现在我们的 CDI 已经可用,我们将使用 nodePort 暴露它以供使用。这将允许我们在接下来的步骤中连接到 cdi-proxy。
cat > cdi-nodeport.yaml << EOF
apiVersion: v1
kind: Service
metadata:
name: cdi-proxy-nodeport
namespace: cdi
spec:
type: NodePort
selector:
cdi.kubevirt.io: cdi-uploadproxy
ports:
- port: 8443
nodePort: 30443
EOF
kubectl create -f cdi-nodeport.yaml
最后一步,让我们获得 virtctl 的最新版本,我们将在安装 Windows 时使用它。
VERSION=$(kubectl get kubevirt.kubevirt.io/kubevirt -n kubevirt -o=jsonpath="{.status.observedKubeVirtVersion}")
curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/virtctl-${VERSION}-linux-amd64
sudo install virtctl /usr/local/bin
现在,我们可以安装一个 Windows VM 来测试这个特性。下面的步骤是基于KubeVirt:从 ISO 安装 Microsoft Windows[9],但是我们将使用 Windows 10 而不是 Windows Server 2012。下面的命令假设你有一个名为 win10-virtio.iso 的 Windows 10 ISO 文件。如果你需要 Windows 10 光盘,请参见下载 Windows 10 磁盘镜像[10],并在获得安装光盘后返回这里。
$ virtctl image-upload \
--image-path=win10-virtio.iso \
--pvc-name=iso-win10 \
--access-mode=ReadWriteOnce \
--pvc-size=6G \
--uploadproxy-url=https://127.0.0.1:30443 \
--insecure \
--wait-secs=240
我们需要一个地方来存储我们的 Windows 10 虚拟磁盘,使用以下方法来创建一个 40Gb 的空间来存储我们的文件。为了在 minikube 中做到这一点,我们将手动创建一个 PersistentVolume(PV)和一个 PersistentVolumeClaim(PVC)。这些步骤假设在“/”中有 45+ GiB 的空闲空间。我们将创建一个“/data”目录以及一个用于存储 PV 的子目录。如果“/”中没有至少 45gib 的空闲空间,则需要释放空间,或将存储挂载到“/data”上才能继续。
cat > win10-pvc.yaml << EOF
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pvwinhd1
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 43Gi
claimRef:
namespace: default
name: winhd1
hostPath:
path: /data/winhd1-pv
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: winhd1
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 40Gi
EOF
kubectl create -f win10-pvc.yaml
现在我们可以创建 Windows 10 虚拟机了。使用以下方法创建一个包含 vGPU 的虚拟机定义文件:
cat > win10vm1.yaml << EOF
apiVersion: kubevirt.io/v1alpha3
kind: VirtualMachine
metadata:
name: win10vm1
spec:
running: false
template:
metadata:
creationTimestamp: null
labels:
kubevirt.io/domain: win10vm1
spec:
domain:
clock:
timer:
hpet:
present: false
hyperv: {}
pit:
tickPolicy: delay
rtc:
tickPolicy: catchup
utc: {}
cpu:
cores: 1
sockets: 2
threads: 1
devices:
gpus:
- deviceName: intel.com/U630
name: gpu1
disks:
- cdrom:
bus: sata
name: windows-guest-tools
- bootOrder: 1
cdrom:
bus: sata
name: cdrom
- bootOrder: 2
disk:
bus: sata
name: disk-1
inputs:
- bus: usb
name: tablet
type: tablet
interfaces:
- masquerade: {}
model: e1000e
name: nic-0
features:
acpi: {}
apic: {}
hyperv:
relaxed: {}
spinlocks:
spinlocks: 8191
vapic: {}
machine:
type: pc-q35-rhel8.2.0
resources:
requests:
memory: 8Gi
hostname: win10vm1
networks:
- name: nic-0
pod: {}
terminationGracePeriodSeconds: 3600
volumes:
- name: cdrom
persistentVolumeClaim:
claimName: iso-win10
- name: disk-1
persistentVolumeClaim:
claimName: winhd1
- containerDisk:
image: quay.io/kubevirt/virtio-container-disk
name: windows-guest-tools
EOF
kubectl create -f win10vm1.yaml
请注意:该虚拟机没有优化使用 virtio 设备来简化操作系统的安装。通过使用 SATA 设备和模拟的 e1000 网卡,我们不需要担心加载额外的驱动程序。
我们已经添加到这个虚拟机定义中的关键信息是 yaml 的这段代码:
devices:
gpus:
- deviceName: intel.com/U630
name: gpu1
在这里,我们正在标识我们想要绑定到这个 VM 的 GPU 设备。deviceName 与我们给 KubeVirt 的用来识别 Intel GPU 资源的名称有关。它也是运行 kubectl describe node 时出现在节点“Capacity”部分的相同标识符。
我们现在可以启动虚拟机了:
virtctl start win10vm1
kubectl get vmi --watch
当输出显示虚拟机处于“Running”阶段时,你可以“CTRL+C”结束监视命令。
因为我们在本地机器上运行这个 VM,所以现在可以利用 virtctl 命令连接到虚拟机的 VNC 控制台。
$ virtctl vnc win10vm1
一个新的 VNC Viewer 窗口将打开,你现在应该会看到 Windows 10 安装屏幕。此时请遵循标准的 Windows 10 安装步骤。
安装完成后,你就拥有了一个运行有可用 GPU 的 Windows 10 虚拟机。你可以通过打开 Windows 10 任务管理器,选择 Advanced,然后选择“Performance”选项卡来测试 GPU 加速是否可用。注意,在你第一次启动时,Windows 仍然在检测和安装适当的驱动程序。在“Performance”页签中显示 GPU 信息可能需要一到两分钟。
尝试测试 GPU 加速。在你的虚拟机中打开浏览器,浏览到“http://www.fishgl.com”,但是不要对糟糕的性能感到惊讶。默认的KubeVirt控制台不利用GPU。为此,我们需要采取最后一步,使用Windows远程桌面协议(RDP),它可以使用GPU。
为了利用我们添加的虚拟 GPU,我们需要通过远程桌面协议(RDP)连接到虚拟机。请按照以下步骤启用 RDP:
现在,运行以下命令向 Kubernetes 集群外部暴露 RDP 服务器:
$ virtctl expose vm win10vm1 --port=3389 --type=NodePort --name=win10vm1-rdp
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 18h
win10vm1-rdp NodePort 10.105.159.184 <none> 3389:30627/TCP 39s
请注意分配给该服务的端口,我们将在下一步中使用它。在上面的输出中,端口是 30627。
我们现在可以使用 rdesktop 工具连接到我们的 VM,并充分利用 vGPU 的优势。从命令行运行 rdesktop localhost:端口,确保根据上面的输出更新端口。当 rdesktop 提示时,接受证书。登录到你的 Windows 10 客户端。现在可以测试 vGPU 了。
让我们再试试 FishGL。打开浏览器,登录http://www.fishgl.com。你应该注意到应用程序性能有了很大的改进。你也可以打开Task Manager,查看性能选项卡来查看 GPU 加载情况。
请注意,由于你在同一 GPU 上运行 Fedora 32 工作站,你已经在主桌面之间共享图形工作负载,并且虚拟 Windows 桌面也在此机器上运行。
恭喜你!你现在有了一个使用 Intel vGPU 在 Kubernetes 中运行的 VM。如果你的测试机器有足够的资源,你可以重复这些步骤并创建多个共享同一个 Intel GPU 的虚拟机。
[1]
KubeVirt 0.40: https://github.com/kubevirt/kubevirt/releases/tag/v0.40.0
[2]
创建 vGPU(仅 KVMGT): https://github.com/intel/gvt-linux/wiki/GVTg_Setup_Guide#53-create-vgpu-kvmgt-only
[3]
minikube start: https://minikube.sigs.k8s.io/docs/start/
[4]
KubeVirt releases: https://github.com/kubevirt/kubevirt/releases
[5]
在 Linux 上安装和设置 kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
[6]
下载 Windows 10 磁盘镜像文件: https://www.microsoft.com/en-us/software-download/windows10
[7]
Minikube: https://minikube.sigs.k8s.io/docs/
[8]
容器数据导入器(CDI)实验: https://kubevirt.io/labs/kubernetes/lab2.html
[9]
KubeVirt:从 ISO 安装 Microsoft Windows: https://kubevirt.io/2020/KubeVirt-installing_Microsoft_Windows_from_an_iso.html
[10]
下载 Windows 10 磁盘镜像: https://www.microsoft.com/en-us/software-download/windows10