VirtualServer
VirtualServer 在集群中创建一个虚拟机,与容器环境相比,虚拟机:
- 提供与物理计算机类似的运行环境,在有些场景更加符合用户需求;
- 提供更强的隔离,可以限制潜在的安全影响范围。
创建 VirtualServer
下面是一个基本的 VirtualServer 示例:
apiVersion: tensorstack.dev/v1beta1
kind: VirtualServer
metadata:
name: vs-example
spec:
resources:
cpu:
cores: "1"
memory: 8Gi
storage:
root:
pvc:
size: 4Gi
volumeMode: Filesystem
accessModes: ["ReadWriteOnce"]
storageClassName: cephfs-hdd
source:
http:
url: https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-amd64.img
additionalDisks:
- name: disk-name
persistentVolumeClaim:
claimName: pvc-as-disk
filesystems:
- name: my-filesys
persistentVolumeClaim:
claimName: pvc-as-fs
network:
tcp: [80, 5901]
udp: []
macAddress: ""
dnsConfig: {}
dnsPolicy: ClusterFirst
cloudInit: |-
#cloud-config
bootcmd:
- test "$(lsblk /dev/vdb)" && mkfs.ext4 /dev/vdb
- mkdir -p /mnt/vdb
- mkdir -p /mnt/file-sys
- mount -t virtiofs my-filesys /mnt/file-sys
mounts:
- [ "/dev/vdb", "/mnt/vdb", "ext4", "defaults,nofail", "0", "2" ]
在该例中:
- 虚拟机申请使用 1 个 CPU 和 8Gi 内存(由
spec.resources
字段指定)。 - 虚拟机绑定一个 4Gi 的 PVC 作为根磁盘,并在 PVC 中下载
https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-amd64.img
系统镜像(由spec.storage.root
字段指定)。 - 虚拟机声明 PVC
pvc-as-disk
为磁盘/dev/vdb
(由spec.storage.additionalDisks
字段指定,根磁盘为/dev/vda
,其他磁盘按顺序命名,但预设名称的方式不完全可靠,建议使用serial
字段确认磁盘,参考 disk),并将该磁盘格式化为ext4
文件系统,挂载到/mnt/vdb
路径(格式化和绑定操作由spec.cloudInit
字段指定)。 - 虚拟机声明 PVC
pvc-as-fs
为文件系统my-filesys
(由spec.storage.filesystems
字段指定),并将该文件系统挂载到/mnt/file-sys
路径(绑定操作由spec.cloudInit
字段指定)。 - 虚拟机会暴露
80
和5901
两个 TCP 服务端口(由spec.network
字段指定)。
虚拟机配置详解
资源申请
VirtualServer 支持设置 spec.resources
字段来申请 CPU、GPU 和内存三种资源。
spec:
resources:
gpu:
type: nvidia.com/GA100_A100_PCIE_40GB
count: 2
cpu:
model: Conroe
cores: 1
memory: 10Gi
在该例中,虚拟机申请使用 2
张 nvidia.com/GA100_A100_PCIE_40GB
GPU 卡、1
个 Conroe
型号的 CPU 以及 10Gi
的内存。
根磁盘
VirtualServer 根据 spec.storage.root.pvc
字段中的配置创建 PVC 作为根磁盘,并从 spec.storage.root.source
字段所指定的数据源下载操作系统。
spec:
storage:
root:
ephemeral: false
pvc:
size: 4Gi
volumeMode: Filesystem
accessModes: ["ReadWriteOnce"]
storageClassName: cephfs-hdd
source:
http:
url: https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-amd64.img
在上述示例中:
- VirtualServer 创建一个 4Gi PVC,绑定为根磁盘。
- 从
https://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-amd64.img
链接下载系统镜像到根磁盘中。- VirtualServer 支持从多种数据源下载数据,比如 HTTP 链接、s3 数据库、镜像仓库、其他 PVC 等,更多信息请参考 DataVolumeSource。
- 该根磁盘以非临时磁盘的方式绑定给虚拟机(通过
spec.storage.root.ephemeral
字段指定)。所谓临时磁盘,即在虚拟机中的所有改动不会反应到 PVC 中,而是以一个临时镜像的方式存储;如果虚拟机重启,则该临时镜像消失。
其他数据卷
除了根磁盘以外,VirtualServer 支持以 disk 和 filesystem 方式绑定任意数量的数据卷。
绑定方式
filesystem
VirtualServer 通过 spec.storage.filesystems
字段声明文件系统:
spec:
storage:
filesystems:
- name: my-filesys
persistentVolumeClaim:
claimName: pvc-as-fs
在上述示例中,VirtualServer 将 PVC pvc-as-fs
声明为文件系统 my-filesys
。
上面字段值只声明了文件系统,用户还需要将文件系统挂载到一个路径才可以使用。用户可以用 spec.cloudInit
字段来挂载该文件系统:
spec:
cloudInit: |-
#cloud-config
bootcmd:
- mkdir -p /mnt/file-sys
- mount -t virtiofs my-filesys /mnt/file-sys
此外,用户也可以在虚拟机启动后,进入虚拟机手动执行上述挂载命令,此处不再演示。
disk
VirtualServer 通过 spec.storage.additionalDisks
字段声明磁盘:
spec:
storage:
additionalDisks:
- name: disk-name
persistentVolumeClaim:
claimName: pvc-as-disk
serial: CVLY623300HK240D
在上述示例中:
- VirtualServer 将 PVC
pvc-as-disk
声明为一个磁盘; - 序列号为
CVLY623300HK240D
,如果不设置该字段,则磁盘序列号会随机生成,在重启虚拟机后,序列号会发生改变。序列号有助于确认磁盘,如通过命令lsblk --nodeps -no name,serial | grep CVLY623300HK240D | cut -f1 -d' '
确认上述示例中磁盘的名称。
如果该磁盘是第一次使用,用户还需要将磁盘格式化并挂载到一个路径,可以用 spec.cloudInit
字段完成该操作:
spec:
cloudInit: |-
#cloud-config
bootcmd:
- test "$(lsblk /dev/vdb)" && mkfs.ext4 /dev/vdb
- mkdir -p /mnt/file-sys
mounts:
- [ "/dev/vdb", "/mnt/vdb", "ext4", "defaults,nofail", "0", "2" ]
此外,用户也可以在虚拟机启动后,进入虚拟机手动执行格式化和挂载操作,此处不再演示。
数据卷类型
PersistentVolumeClaim
使用一个 PVC 作为磁盘或文件系统:
spec:
storage:
filesystems:
- name: filesys-name
persistentVolumeClaim:
claimName: pvc-name
---
spec:
storage:
additionalDisks:
- name: disk-name
persistentVolumeClaim:
claimName: pvc-name
临时 PVC
临时 PVC 指的是虚拟机将 PVC 作为只读存储,虚拟机在启动后会在本地维护一个临时镜像,记录用户对 PVC 的写入都会记录在该临时镜像中,而不会实际写入到 PVC 中。如果虚拟机停止或重启,则临时镜像丢失。
spec:
storage:
filesystems:
- name: filesys-name
ephemeral:
persistentVolumeClaim:
claimName: pvc-name
---
spec:
storage:
additionalDisks:
- name: disk-name
ephemeral:
persistentVolumeClaim:
claimName: pvc-name
ConfigMap
ConfigMap 也可以作为磁盘或文件系统绑定到虚拟机上:
spec:
storage:
filesystems:
- name: config-fs
configMap:
name: app-config
cloudInit: |-
#cloud-config
bootcmd:
- "sudo mkdir /mnt/app-config"
- "sudo mount -t virtiofs config-fs /mnt/app-config"
---
spec:
storage:
additionalDisks:
- name: disk-name
configMap:
name: app-config
serial: CVLY623300HK240D
cloudInit: |-
#cloud-config
bootcmd:
- "sudo mkdir /mnt/app-config"
- "sudo mount /dev/$(lsblk --nodeps -no name,serial | grep CVLY623300HK240D | cut -f1 -d' ') /mnt/app-config"
与 PVC 不同,以 ConfigMap 作为 disk 绑定给虚拟机时不需要格式化,控制器会自动创建磁盘并将 ConfigMap data
中的内容以文件的形式存入磁盘。
用户可以给磁盘设置 serial
字段,以便查找磁盘。
ServiceAccount
spec:
storage:
filesystems:
- name: sa-fs
serviceAccount:
serviceAccountName: app-secret
cloudInit: |-
#cloud-config
bootcmd:
- "sudo mkdir /mnt/app-sa"
- "sudo mount -t virtiofs sa-fs /mnt/app-sa"
---
spec:
storage:
additionalDisks:
- name: disk-name
serviceAccount:
serviceAccountName: serviceaccountdisk
serial: SERVICEACCOUNT12
cloudInit: |-
#cloud-config
bootcmd:
- "sudo mkdir /mnt/app-sa"
- "sudo mount /dev/$(lsblk --nodeps -no name,serial | grep SERVICEACCOUNT12 | cut -f1 -d' ') /mnt/app-sa"
以 ServiceAccount 作为 disk 绑定给虚拟机时不需要格式化,控制器会自动创建磁盘,并在磁盘中创建 ca.crt
、namespace
和 token
三个文件,这些文件中记录着 ServiceAccount 的权限信息。
以 ServiceAccount 作为 filesystem 绑定给虚拟机,控制器也会在挂在路径中创建 ca.crt
、namespace
和 token
三个文件,这些文件中记录着 ServiceAccount 的权限信息。
网络策略
用户可通过 spec.network
字段设置虚拟机的 DNS 策略,暴露虚拟机服务:
spec:
network:
tcp: [80, 5901]
udp: []
macAddress: ""
dnsConfig: {}
dnsPolicy: ClusterFirst
字段说明:
tcp
和udp
:表示虚拟机所要暴露的服务端口。macAddress
:网络接口的 MAC 地址,以便在虚拟机重启前后 MAC 地址保持一致。如果不指定该字段,虚拟机每次重启都会被分配随机 MAC 地址。dnsPolicy
:DNS 策略,默认为ClusterFirst
,可选值参考 DNS Policy。dnsConfig
:虚拟机 DNS 配置,参考 DNS Config。
在设置 spec.network
字段后,控制器会自动创建一个与虚拟机同名的 Service。用户可以通过该 Service 访问虚拟机的上述 TCP/UDP 接口,比如通过以下命令将服务暴露到本地 8080
端口:
kubectl port-forward service/managed-virtualserver-9dda9 8080:80
CloudInit
CloudInit 是 Canonical 公司开发的系统初始化工具,已被许多云服务提供商和 Linux 发行版广泛接受。
用户可通过 spec.cloudInit
字段设置 CloudInit 配置来初始化虚拟机环境,包括用户密码、启动命令等:
spec:
cloudInit: |-
#cloud-config
user: ubuntu
password: ubuntu
chpasswd: { expire: False }
package_update: true
package_upgrade: true
packages:
- qemu-guest-agent
runcmd:
- [ systemctl, start, qemu-guest-agent ]
bootcmd:
- "sudo mkdir /mnt/app-configmap"
- "sudo mount -t virtiofs configmap-fs /mnt/app-configmap"
- "sudo mkdir /mnt/app-sa"
- "sudo mount /dev/$(lsblk --nodeps -no name,serial | grep SERVICEACCOUNT12 | cut -f1 -d' ') /mnt/app-sa"
在上述示例中,虚拟机会执行以下初始化操作:
- 创建用户
ubuntu:ubuntu
(用户名:密码); - 更新系统软件包;
- 安装并启动
qemu-guest-agent
服务; - 绑定文件系统
configmap-fs
和磁盘(序列号为SERVICEACCOUNT12
)。
更多 CloudInit 配置请参考 Cloud Config。
虚拟机运行策略
apiVersion: tensorstack.dev/v1beta1
kind: VirtualServer
metadata:
name: example-vs
spec:
runStrategy: Always
...
status:
runStrategy: Halted
...
在上述示例中:
spec.runStrategy
表示 VirtualServer 创建时,使用的运行策略status.runStrategy
表示当前 VirtualServer 的运行策略。
用户可以通过虚拟机操作切换虚拟机运行策略:
Run Strategy | start | stop | restart |
---|---|---|---|
Always | - | Halted | Always |
RerunOnFailure | RerunOnFailure | Halted | RerunOnFailure |
Manual | Manual | Manual | Manual |
Halted | Always | - | - |
- 第一行后三项为虚拟机操作,分别为启动、停止和重启。
- 最左侧一列为操作前的运行策略,右侧为操作后的运行策略。
-
表示在当前运行策略下,不可执行该操作。
虚拟机操作
用户可以通过 K8s 拓展 API 来操控虚拟机,常见的虚拟机操作方式包括:
kubectl virt start $vm_name
:启动虚拟机;kubectl virt stop $vm_name
:停止虚拟机;kubectl virt restart $vm_name
:重启虚拟机;kubectl virt console $vm_name
:连接虚拟机终端。
更多虚拟机操作请通过 kubectl virt -h
命令查看。
状态
虚拟机名称
为避免资源名称过长,导致创建子资源时失败,VirtualServer 在创建虚拟机时,将名称和 UID 进行哈希编码构成虚拟机名称,并将虚拟机名称记录在 status.vm.name
字段中。
status:
vm:
name: managed-virtualserver-9dda9
VirtualServer 的状态
status.conditions
字段用于描述当前 VirtualServer 的状态,包括以下 4 种类型:
DataImported
:VirtualServer 已经创建根磁盘 PVC,并下载好系统镜像。Ready
:虚拟机已经成功启动,这里指的是系统成功启动,但是不考虑 CloudInit 过程。Failure
:虚拟机意外关闭,如节点崩溃、OOMKILLED 等。Paused
:虚拟机暂停,无法使用 CPU 和内存。
在下面的示例中,VirtualServer 成功创建了根磁盘并下载好系统镜像,所以类型为 DataImported
的 condition
被设为 True
;VirtualServer 此时被停止(不是暂停),工作负载被删除,所以类型为 Ready
的 condition
被设为 False
。
status:
conditions:
- lastTransitionTime: "2024-03-21T08:26:59Z"
message: Root disk has been imported successfully.
reason: Succeeded
status: "True"
type: DataImported
- lastTransitionTime: "2024-03-21T08:35:23Z"
message: VMI does not exist
reason: VMINotExists
status: "False"
type: Ready
VirtualServer 的可读状态
status.printableStatus
字段是一个易被用户理解的状态字符串,该字段可能为以下取值:
Stopped
:虚拟机当前处于停止状态,且不会主动启动。Provisioning
:集群正在为虚拟机准备资源,包括向磁盘中下载数据。Starting
:正在启动虚拟机。Running
:虚拟机处于运行阶段。Paused
:虚拟机被暂停。Stopping
:虚拟机正在停止,包括删除工作负载等步骤。Terminating
:虚拟机正在被删除。CrashLoopBackOff
:虚拟机目前已崩溃,正等待重启。Migrating
:虚拟机正在迁移到另一个主机(目前不支持)。Unknown
:虚拟机状态未知,通常发生在虚拟机所在主机失去连接。FailedUnschedulable
:虚拟机无法被分配,可能的原因包括集群资源不足等。
参考
- API 参考:VirtualServer