跳转至

2 卷管理

  1. 磁盘(Disks): - 磁盘通常指的是物理存储设备,如机械硬盘(HDD)或固态硬盘(SSD),它们提供非易失性存储空间。磁盘有固定的容量,并且实际存在于服务器、个人计算机或其他计算设备中。

  2. 卷(Volumes): - 卷是逻辑存储资源,它建立在物理磁盘之上,通过分区或逻辑卷管理技术(LVM)来抽象物理存储。一个卷可以是一个分区,也可以是由多个磁盘的空间集合组成的一个逻辑单元。 卷对操作系统来说就像是一个独立的存储区域,可以格式化并挂载为文件系统,用于存储数据。

  3. 文件系统(Filesystems): - 文件系统是一种组织、管理和检索存储在磁盘上的数据的方法。它将物理存储空间转换成目录结构和文件层次,使得用户和应用程序能够以文件的形式读写数据。 每个卷都需要一个文件系统才能被操作系统识别和利用,比如XFS、NTFS、ext4等都是不同类型的文件系统。

总结起来: - 磁盘是硬件层面的物理存储介质。 - 是基于物理磁盘创建的逻辑存储容器,它可以跨越单个磁盘甚至多个磁盘。 - 文件系统则是应用在卷上的一套规则和数据结构,用来管理存储在卷中的文件和目录结构。

kubevirt是如何为虚拟机提供持久化存储的?先来看一段创建虚拟机的yaml

    spec:
      domain:
        cpu:
          cores: 1
          model: host-passthrough
        memory:
          guest: 2Gi
        devices:
          disks:
            - name: root-disk
              disk:
                bus: virtio
          interfaces:
            - name: default
              masquerade: {}
        resources:
          requests:
            memory: 2048M
      networks:
        - name: default
          pod: {}
      volumes:
        - name: root-disk
          persistentVolumeClaim:
            claimName: pvc-centos01-bootdisk

  1. 定义卷(Volumes): 首先,在VirtualMachine的spec.volumes部分指定所需的持久化存储卷。这可以是PersistentVolumeClaim(PVC)、HostPath等不同类型的卷。

  2. 添加磁盘到虚拟机(Disks): 接下来,在VirtualMachine的spec.domain.devices.disks 部分,为虚拟机添加相应的磁盘。这些磁盘将使用虚拟化技术(如 virtio 或 SCSI)模拟实际物理磁盘,并连接到虚拟机内。

  3. 引用已定义的卷: 最后,在上述磁盘配置中通过名称引用之前定义好的卷。这样,虚拟机内的操作系统就可以识别并挂载这个卷,从而访问和存储数据。例如,在磁盘规范中设置 volumeName 字段以关联到对应的 volume 名称。

通过这三个步骤,KubeVirt就能够在k8s中将持久化存储卷与虚拟机内部的磁盘资源关联起来,使得虚拟机能够像物理服务器一样利用集群中的存储服务。 另外,VM中的磁盘名称spec.domain.devices.disks.name引用了spec.volumes.name,所以要保持一致。

为虚拟机添加磁盘,并且指定磁盘总线bus类型

    spec:
      domain:
        cpu:
          cores: 1
          model: host-passthrough
        memory:
          guest: 2Gi
        devices:
          disks:
            - name: root-disk
              disk:
                bus: virtio
          interfaces:
            - name: default
              masquerade: {}
        resources:
          requests:
            memory: 2048M
      networks:
        - name: default
          pod: {}
      volumes:
        - name: root-disk
          persistentVolumeClaim:
            claimName: pvc-centos01-bootdisk

将磁盘以CDROM类型分配给虚拟机,默认只读

    spec:
      domain:
        cpu:
          cores: 1
          model: host-passthrough
        memory:
          guest: 2Gi
        devices:
          disks:
            - name: root-disk
              disk:
                bus: virtio
            - name: cdrom
              cdrom:
                 # 默认只读,可以设置为false,表示可写
                 readonly: false
                 # 设置总线类型
                 bus: sata  
          interfaces:
            - name: default
              masquerade: {}
        resources:
          requests:
            memory: 2048M
      networks:
        - name: default
          pod: {}
      volumes:
        - name: root-disk
          persistentVolumeClaim:
            claimName: pvc-centos01-bootdisk
        - name: cdrom
-         persistentVolumeClaim:
            claimName: pvc-centos7-iso

为虚拟机添加一个filesystem类型的设备

filesystem设备会将存储卷作为文件系统添加到虚拟机。filesystem依赖virtiofs技术。 相较于diskfilesystem允许源端的变更能动态地反映到虚拟机内的卷中,比如,将一个 configMap通过filesystem共享给虚拟机,对configMap做出的任何修改都会在虚拟机内得到实时反映。但是,虚拟机使用了filesystem后就不支持实时迁移了liveMigration了。

此外,filesystem设备必须在虚拟内部进行挂载,可通过cloudInitNoCloud自动完成挂载,或者进入到虚拟机中手动进行挂载。如若是手动挂载,则使用命令mount -t virtiofs [设备标签] [路径]完成挂载。 在虚拟机规格(VM spec)中的spec.domain.devices.filesystems.name字段会为文件系统分配一个标签。 例如,如果在某个虚拟机规格中设置了spec.domain.devices.filesystems.name: foo,则在虚拟机内将文件系统挂载到/tmp/foo路径所需的命令是mount -t virtiofs foo /tmp/foo

spec:
  domain:
    devices:
      filesystems:
        - name: foo
          virtiofs: {}
      disks:
        - name: containerdisk
          disk:
            bus: virtio
        - name: cloudinitdisk
          disk:
            bus: virtio
    volumes:
      - containerDisk:
          image: quay.io/containerdisks/fedora:latest
        name: containerdisk 
      - cloudInitNoCloud:
            userData: |-
              #cloud-config
              chpasswd:
                expire: false
              password: fedora
              user: fedora
              bootcmd:
                - "sudo mkdir /tmp/foo"
                - "sudo mount -t virtiofs foo /tmp/foo"
      - persistentVolumeClaim:
          claimName: mypvc
        name: foo
注意 设备为filesystem类型需要依赖virtiofs,而virtiofs需要内核支持才能在VM中工作,因此,要检查VM所使用的Linux镜像是否支持virtiofs,可以在制作镜像时执行命令modprobe virtiofs, 如果返回modprobe: FATAL: Module virtiofs not found,则表示该VM的Linux镜像不支持不支持virtiofs。 此外,可以检查Linux内核版本是否达到5.4及以上(针对任何Linux发行版)或者centos/rhel系统内核要达到4.18及以上,可以执行命令uname -r查看。

kubevirt支持的卷源包括:

  • cloudInitNoCloud

    允许将cloudInitNoCloud数据源附加到虚拟机中。如果虚拟机配置了正确的cloud-init设置,它将会识别并使用这块磁盘作为用户数据源。简单示例如下:

    ## 将一个Secret作为cloud-init disk数据源进行连接
    spec:
      domain:
        resources:
          requests:
            memory: 64M
        devices:
          disks:
          - name: root-disk
            disk:
               bus: virtio
          - name: mynoclouddisk
            disk: {}
      volumes:
        - name: root-disk
          persistentVolumeClaim:
            claimName: pvc-centos01-bootdisk
        - name: mynoclouddisk
          cloudInitNoCloud:
            secretRef:
              name: testsecret
    

  • cloudInitConfigDrive

    允许将cloudInitConfigDrive数据源附加到虚拟机中。若虚拟机中配置了正确的cloud-init设置,它将会识别并将该磁盘作为用户数据源。简单示例如下:

    ## 将一个Secret作为cloud-init disk数据源进行连接
    spec:
      domain:
        resources:
          requests:
            memory: 64M
        devices:
          disks:
          - name: root-disk
            disk:
               bus: virtio
          - name: myconfigdrivedisk
            disk: {}
      volumes:
        - name: root-disk
          persistentVolumeClaim:
            claimName: pvc-centos01-bootdisk
        - name: myconfigdrivedisk
          cloudInitConfigDrive:
            secretRef:
              name: testsecret
    

  • persistentVolumeClaim(持久化卷声明)

    允许将持久化卷声明persistentVolumeClaim数据源附加到虚拟机中。 虚拟机需要在关机后磁盘仍然存在,就应该使用PVC,这样可以确保虚拟机的数据在重启时被持久化不会丢失。 持久化卷可以是文件系统filesystem或块设备block模式。 filesystem模式:为了让KubeVirt能够消费位于持久化卷文件系统上的磁盘,磁盘必须命名为disk.img,并放置在文件系统的根路径下。 目前,磁盘还必须以raw格式存储。 注意:为了规避权限问题,disk.img镜像文件的所有者必须为用户ID是107。如果在启动虚拟机之前尚未手动创建disk.img镜像文件, 那么系统将在启动时根据持久化卷声明的大小自动创建。然而,由于并非所有的存储供应者都能提供精确等于请求量的可用空间(比如,可能因文件系统开销而导致), KubeVirt允许可用空间最多比请求量少10%。这一阈值可以通过编辑KubeVirt自定义资源(CR)中的developerConfiguration.pvcTolerateLessSpaceUpToPercent值来配置 可以使用命令kubectl edit kubevirt kubevirt -n kubevirt。 使用PVC作为虚拟机的数据源附加到虚拟机中,简单示例如下:

    spec:
       domain:
          resources:
             requests:
                memory: 64M
          devices:
             disks:
                - name: root-disk
                  disk:
                     bus: virtio
                - name: mypvcdisk
                  disk: 
                     bus: virtio
       volumes:
          - name: root-disk
            persistentVolumeClaim:
               claimName: pvc-centos01-bootdisk
          - name: mypvcdisk
            persistentVolumeClaim:
               claimName: mypvc
    

  • dataVolume(数据卷)

    允许将数据卷dataVolume数据源附加到虚拟机中。在kubevirt中,dataVolume简化了虚拟机磁盘镜像的生命周期管理。当创建一个虚拟机实例时,可以通过dataVolume自动地将所需的磁盘镜像导入到PVC中, 也就是说,在虚拟机启动过程中,dataVolume会自动创建PVC,并从指定的源(PVC、HTTP、S3等)接取磁盘镜像文件填充到PVC中,从而省去了用户手动创建PVC并导入镜像来填充PVC的过程。dataVolume 可以直接在VM spec中定义,通过将dataVolume添加致dataVolumeTemplates列表中实现,简单示例如下:

    apiVersion: kubevirt.io/v1
    kind: VirtualMachine
    metadata:
      labels:
        kubevirt.io/vm: vm-alpine-datavolume
      name: vm-alpine-datavolume
    spec:
      running: false
      template:
        metadata:
          labels:
            kubevirt.io/vm: vm-alpine-datavolume
        spec:
          domain:
            devices:
              disks:
              - disk:
                  bus: virtio
                name: datavolumedisk1
            resources:
              requests:
                memory: 64M
          volumes:
          - dataVolume:
              name: alpine-dv
            name: datavolumedisk1
      dataVolumeTemplates:
      - metadata:
          name: alpine-dv
        spec:
          pvc:
            accessModes:
            - ReadWriteOnce
            resources:
              requests:
                storage: 2Gi
          source:
            http:
              url: http://cdi-http-import-server.kubevirt/images/alpine.iso
    
    dataVolumeTemplates部分定义了两部分内容:pvcsourcepvc部分则声明了应用于创建承载源数据的PVC的规格;source部分声明了存在一个位于HTTP服务器上的磁盘镜像。 当这份虚拟机配置文件提交到集群时,在启动流程中,会根据提供的规格自动创建一个PVC,并在虚拟机启动前将源数据自动导入到该PVC中。当虚拟机被删除时,由DataVolume配置的存储也会随之自动删除

另外,dataVolume是由Containerized Data Importer(CDI)项目提供的一个自定义资源。KubevirtCDI进行了整合,目的是为用户提供一种能够动态创建PVC并在PVC中导入数据的工作流程。 为了在虚拟机(VM)或虚拟机实例(VMI)上利用DataVolume卷源,必须先安装CDI。

  • ephemeral(临时卷)

    临时卷是一种本地的COW(Copy-On-Write)镜像,它使用网络卷作为只读底层存储。在使用临时卷时,网络底层存储永远不会被修改。 相反,所有的写入操作都会存储在位于本地存储上的临时镜像上。当虚拟机(VM)启动时,KubeVirt会动态生成与其关联的临时镜像,并在虚拟机停止时丢弃这些临时镜像。 临时卷在无需硬盘持久化的场景中特别有用。当虚拟机达到最终状态(例如成功结束或失败)时,COW镜像会被丢弃。 当前,仅支持使用PVC作为临时卷的底层存储。 临时卷适合那些生命周期短暂且不需要持久化磁盘状态的虚拟机实例。这种情况下,网络底层存储作为只读源,而所有更改都在本地的临时COW镜像上发生,从而实现高效的资源利用和安全的数据管理。

    metadata:
      name: testvmi-ephemeral-pvc
    apiVersion: kubevirt.io/v1
    kind: VirtualMachineInstance
    spec:
      domain:
        resources:
          requests:
            memory: 64M
        devices:
          disks:
          - name: mypvcdisk
            lun: {}
      volumes:
        - name: mypvcdisk
          ephemeral:
            persistentVolumeClaim:
              claimName: mypvc
    

  • containerDisk(容器磁盘)

    containerDisk 曾被称为registryDiskcontainerDisk功能提供了将虚拟机磁盘存储和分发在容器镜像仓库中的能力。 containerDisks 可以在VirtualMachineInstance 规范的disks部分分配给虚拟机。 containerDisks 并不会使用网络共享存储设备。这些磁盘是从容器镜像仓库中拉取,并驻留托管在虚拟机所在节点的本地存储上。

何时使用 containerDisk? 当需要将临时存储设备分配给任意数量活跃的VirtualMachineInstances时,应使用containerDisk。 这使得它们成为理想工具,适用于希望复制大量不需要持久化数据的虚拟机工作负载的用户。containerDisks常常与VirtualMachineInstanceReplicaSets结合使用。

何时不应使用 containerDisk? 对于任何需要在虚拟机重启间持久化根磁盘的工作负载,containerDisks 并不是一个好的解决方案。 containerDisk 工作流示例 用户可以将 VirtualMachineInstance 磁盘以 KubeVirt 运行时可消耗的方式注入到容器镜像中。磁盘必须放置在容器内的/disk 目录下。 支持 Raw 和 qcow2 格式,推荐使用 qcow2 以减小容器镜像的大小。containerdisks 应该且可以基于scratch构建,除了镜像外,不需要任何其他内容。

注意:在 kubevirt 0.20 版本之前,containerDisk镜像需要以kubevirt/container-disk-v1alpha为基础镜像;containerDisk必须对用户 UID 为 107(qemu)的用户可读。

容器磁盘制作示例:

## 示例1
cat << END > Dockerfile
FROM scratch
ADD --chown=107:107 fedora25.qcow2 /disk/
END

docker build -t vmidisks/fedora25:latest .
示例2
cat << END > Dockerfile
FROM scratch
ADD --chown=107:107 https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2 /disk/
END

docker build -t vmidisks/centos7:latest .

  • emptyDisk(空磁盘)

    emptyDisk与k8s中的emptyDir类似,它会为虚拟机分配一个指定大小的qcow2格式的磁盘,该磁盘文件是稀疏的,这意味着仅在写入数据时才会实际占用存储空间,并且该磁盘是临时的,随着虚拟机的关闭数据会丢失。 但如果是在虚拟机内部执行重启reboot操作,数据不丢失。

何时使用 emptyDdisk? 当虚拟机需要一个临时磁盘来存放临时文件、日志等数据,这些数据需要在每次虚拟机关闭时自动被删除,可以使用emptyDisk

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: pvc-cirros01-bootdisk
   namespace: default
spec:
   storageClassName: "ceph-hdd-block"
   volumeMode: Block
   accessModes:
      - ReadWriteMany
   dataSource:
      kind: PersistentVolumeClaim
      name: img-cirros
   resources:
      requests:
         storage: 1Gi
---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
   name: vm-cirros01
spec:
   instancetype:
      kind: VirtualMachineInstancetype
      name: p1-1
   running: true
   template:
      metadata:
         labels:
            kubevirt.io/domain: vm-cirros01
      spec:
         architecture: amd64
         domain:
            devices:
               disks:
                  - disk:
                       bus: virtio
                    name: root-disk
                  - disk:
                       bus: virtio
                    name: emptydisk
               rng: {}
               interfaces:
                  - name: default
                    masquerade: {}
         networks:
            - name: default
              pod: {}
         terminationGracePeriodSeconds: 0
         volumes:
            - name: root-disk
              persistentVolumeClaim:
                 claimName: pvc-cirros01-bootdisk
            - name: emptydisk
              emptyDisk:
                 capacity: 2Gi

进入虚拟机查看,发现有一块2GB的vdb磁盘,格式化挂载并写入数据

# lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
vda     252:0    0    1G  0 disk 
|-vda1  252:1    0 1015M  0 part /
`-vda15 252:15   0    8M  0 part 
vdb     252:16   0    2G  0 disk 


# mkfs.ext4 /dev/vdb 
mke2fs 1.44.5 (15-Dec-2018)
Discarding device blocks: done                            
Creating filesystem with 524288 4k blocks and 131072 inodes
Filesystem UUID: b60be2d9-912a-4efd-95d1-7d70dfc4b223
Superblock backups stored on blocks: 
        32768, 98304, 163840, 229376, 294912

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done 

# mount /dev/vdb /mnt
## 验证在虚拟机内部重启虚拟机,数据不丢失
# echo "hello" > /mnt/hello.txt
# reboot
注意 创建的emptydisk.qcow2文件位于/var/lib/kubelet/pods/<pod-id>/volumes/kubernetes.io~empty-dir/libvirt-runtime/empty-disks目录下`
## 查看pod-id
# kubectl get vmi vm-cirros01 -o yaml
...
status:
  activePods:
    a08287bf-00a4-4c57-9ba0-398698fae8a3: k8s04

## 到k8s04节点查看    
# ls /var/lib/kubelet/pods/a08287bf-00a4-4c57-9ba0-398698fae8a3/volumes/kubernetes.io~empty-dir/libvirt-runtime/empty-disks
emptydisk.qcow2

  • hostDisk(宿主机磁盘)

    hostDisk 卷类型提供了在宿主机节点上的某个位置创建或使用磁盘镜像的能力。其工作方式类似于k8s中的hostPath。相比emptyDiskhostDisk允许持久化磁盘数据。

    使用hostDisk需要先在kubevirt的featureGates中开启特性门控HostDisk

    hostDisk有两种使用类型:

  • DiskOrCreate:如果在指定的宿主机路径下没有找到磁盘镜像文件,会自动创建一个新的磁盘镜像文件;
  • Disk:这种方式要求在指定的宿主机路径下已经存在一个磁盘镜像文件。以下给出具体示例:
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
       name: pvc-hostdisk01-bootdisk
       namespace: default
    spec:
       storageClassName: "ceph-hdd-block"
       volumeMode: Block
       accessModes:
          - ReadWriteMany
       dataSource:
          kind: PersistentVolumeClaim
          name: img-cirros
       resources:
          requests:
             storage: 1Gi
    ---
    apiVersion: kubevirt.io/v1
    kind: VirtualMachine
    metadata:
       name: vm-hostdisk01
    spec:
       instancetype:
          kind: VirtualMachineInstancetype
          name: p1-1
       running: true
       template:
          metadata:
             labels:
                kubevirt.io/domain: vm-hostdisk01
          spec:
             #nodeSelector:
             #   kubernetes.io/hostname: k8s04
             architecture: amd64
             domain:
                devices:
                   disks:
                      - disk:
                           bus: virtio
                        name: root-disk
                      - disk:
                           bus: virtio
                        name: host-disk
                   rng: {}
                   interfaces:
                      - name: default
                        masquerade: {}
             networks:
                - name: default
                  pod: {}
             terminationGracePeriodSeconds: 0
             volumes:
                - name: root-disk
                  persistentVolumeClaim:
                     claimName: pvc-hostdisk01-bootdisk
                - name: host-disk
                  hostDisk:
                    # 仅在type为DiskOrCreate时,需要指定容量,如果为Disk,则不需要指定容量,因为在要先创建,创建时就指定了容量
                    #capacity: 2Gi
                    # 会在目标宿主机节点的指定路径下创建一个磁盘镜像文件
                    path: /var/lib/libvirt/images/hostdisk01-data.img
                    #type: DiskOrCreate
                    # 如果type: Disk,则需要事先在目标宿主机节点上创建一个磁盘镜像文件
                    type: Disk
    
    手工创建qcow2格式的磁盘文件
    # qemu-img create -f qcow2 /var/lib/libvirt/images/hostdisk01-data.img 2G
    Formatting '/var/lib/libvirt/images/hostdisk01-data.img', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2147483648 lazy_refcounts=off refcount_bits=16 cache=writeback
    
    注意 如果使用hostDisk,建议在VM定义中指定节点选择器(可以在"实例规格"中定义节点选择),以保证虚拟机始终运行在指定的节点上。虚拟机被删除时,对应宿主机节点上的磁盘文件不会被删除。
  • configMap(配置映射)

    configMap可以以磁盘形式或文件系统形式呈现给虚拟机。每种方法都有优缺点,例如:磁盘方式不支持动态变更传播;而文件系统方式则不支持实时迁移。 当configMap以磁盘形式呈现给虚拟机时,将会分配一个额外的ISO格式的磁盘,该磁盘需要在虚拟机(VM)上挂载。为了自动挂载configMap,可以利用cloudInit 挂载。需要注意的是configMap的更新并不会传播到VMI中。

    configMap以文件系统形式呈现给虚拟机时,configMap是通过virtiofs进行共享,与通过磁盘方式共享configMap相比,使用文件系统方式允许将configMap上的动态变更实时传播到VMI(即虚拟机实例无需重启即可获取更改)。但使用了virtiofsVM就不支持实时迁移了。

示例,以磁盘形式

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: pvc-cm01-bootdisk
   namespace: default
spec:
   storageClassName: "ceph-hdd-block"
   volumeMode: Block
   accessModes:
      - ReadWriteMany
   dataSource:
      kind: PersistentVolumeClaim
      name: img-cirros
   resources:
      requests:
         storage: 1Gi
---
apiVersion: v1
kind: ConfigMap
metadata:
   name: cm01-config
   namespace: default
data:
   test.conf: |
      testcm01
      testcm02
---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
   name: vm-cm01
spec:
   instancetype:
      kind: VirtualMachineInstancetype
      name: p1-1
   running: true
   template:
      metadata:
         labels:
            kubevirt.io/domain: vm-cm01
      spec:
         #nodeSelector:
         #   kubernetes.io/hostname: k8s04
         architecture: amd64
         domain:
            devices:
               disks:
                  - disk:
                       bus: virtio
                    name: root-disk
                  - disk:
                       bus: virtio
                    name: cm01-disk
                    serial: "CM01"
               rng: {}
               interfaces:
                  - name: default
                    masquerade: {}
         networks:
            - name: default
              pod: {}
         terminationGracePeriodSeconds: 0
         volumes:
            - name: root-disk
              persistentVolumeClaim:
                 claimName: pvc-cm01-bootdisk
            - name: cm01-disk
              configMap:
                 name: cm01-config

进入虚拟机查看,发现有一块vdb磁盘,不需要格式化,直接挂载。 注意 挂载的configMap是只读的,不能写入。

# mount /dev/vdb /mnt/
# cd /mnt/
# ls
test.conf
## 不能往test.conf中写入文件,并且修改configMap cm01-config 不会传播到虚拟机中
# cat test.conf 
testcm01
testcm02
示例,以virtiofs格式,注意,不能再使用cirros镜像,不支持virtiofs
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: pvc-cm02-bootdisk
   namespace: default
spec:
   storageClassName: "ceph-hdd-block"
   volumeMode: Block
   accessModes:
      - ReadWriteMany
   dataSource:
      kind: PersistentVolumeClaim
      # 不能用cirros镜像,因为不支持 virtiofs
      name: img-ubuntu2204
   resources:
      requests:
         storage: 10Gi
---
apiVersion: v1
kind: ConfigMap
metadata:
   name: cm02-config
   namespace: default
data:
   test.conf: |
      testcm01
      testcm02
---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
   name: vm-cm02
spec:
   instancetype:
      kind: VirtualMachineInstancetype
      name: p1-1
   running: true
   template:
      metadata:
         labels:
            kubevirt.io/domain: vm-cm02
      spec:
         #nodeSelector:
         #   kubernetes.io/hostname: k8s04
         architecture: amd64
         domain:
            devices:
               disks:
                 - disk:
                     bus: virtio
                   name: root-disk
                 - disk:
                      bus: virtio
                   name: cloudinitdisk
               filesystems:
                 - name: cm02-fs
                   virtiofs: {}        
               rng: {}
               interfaces:
                  - name: default
                    masquerade: {}
         networks:
            - name: default
              pod: {}
         terminationGracePeriodSeconds: 0
         volumes:
            - name: root-disk
              persistentVolumeClaim:
                 claimName: pvc-cm02-bootdisk
            - name: cm02-fs
              configMap:
                 name: cm02-config
            - name: cloudinitdisk
              cloudInitConfigDrive:
                 userData: |-
                    #cloud-config
                    hostname: vm-cm02
                    ssh_pwauth: True
                    disable_root: False
                    timezone: Asia/Shanghai
                    password: 123456
                    chpasswd: {"list":"root:12345678",expire: False}
                    bootcmd:
                      - setenforce 0
                      - sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
                      - mount -t virtiofs cm02-fs /mnt
                    runcmd:
                      - sed -i "/PermitRootLogin/s/^.*$/PermitRootLogin yes/g" /etc/ssh/sshd_config
                      - systemctl restart sshd.service
                      #- echo root:12345678 |chpasswd
注意 修改cm02-config 不会马上传播到虚拟机中,因为cm刷新到pod需要一定时间。kubelet通过 --sync-frequency控制同步配置的时间间隔,默认是1min,所以更新configMap后,在容器中真正看到变化,需要0-1min之后。该值改得太小会导致kubelet占用过多资源,影响性能。 可通过修改:/var/lib/kubelet/config.yaml中的syncFrequency字段的值来并重启kubelet生效。 例如:syncFrequency: 10s

  • secret(秘钥)

    secret可以以磁盘形式或文件系统形式呈现给虚拟机。每种方法都有优缺点,例如:磁盘方式不支持动态变更传播;而文件系统方式则不支持实时迁移。 当secret以磁盘形式呈现给虚拟机时,将会分配一个额外的ISO格式的磁盘,该磁盘需要在虚拟机(VM)上挂载。为了自动挂载secret,可以利用cloudInit 挂载。需要注意的是secret的更新并不会传播到VMI中。

    secret以文件系统形式呈现给虚拟机时,secret是通过virtiofs进行共享,与通过磁盘方式共享secret相比,使用文件系统方式允许将secret上的动态变更实时传播到VMI(即虚拟机实例无需重启即可获取更改)。但使用了virtiofsVM就不支持实时迁移了。

    具体示例可参考configMap,需要将configMap改成secret 示例,以virtiofs格式,注意,不能再使用cirros镜像,不支持virtiofs

    ---
    apiVersion: v1
    kind: Secret
    data:
       secret-file: YWRtaW4K  # admin base64后的值
    metadata:
      name: secret02-config
      namespace: default
    type: Opaque
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
       name: pvc-secret02-bootdisk
       namespace: default
    spec:
       storageClassName: "ceph-hdd-block"
       volumeMode: Block
       accessModes:
          - ReadWriteMany
       dataSource:
          kind: PersistentVolumeClaim
          # 不能用cirros镜像,因为不支持 virtiofs
          name: img-ubuntu2204
       resources:
          requests:
             storage: 10Gi
    ---
    apiVersion: kubevirt.io/v1
    kind: VirtualMachine
    metadata:
       name: vm-secret02
    spec:
       instancetype:
          kind: VirtualMachineInstancetype
          name: p1-1
       running: true
       template:
          metadata:
             labels:
                kubevirt.io/domain: vm-secret02
          spec:
             #nodeSelector:
             #   kubernetes.io/hostname: k8s04
             architecture: amd64
             domain:
                devices:
                   disks:
                      - disk:
                           bus: virtio
                        name: root-disk
                      - disk:
                           bus: virtio
                        name: cloudinitdisk
                   filesystems:
                      - name: secret02-fs
                        virtiofs: {}
                   rng: {}
                   interfaces:
                      - name: default
                        masquerade: {}
             networks:
                - name: default
                  pod: {}
             terminationGracePeriodSeconds: 0
             volumes:
                - name: root-disk
                  persistentVolumeClaim:
                     claimName: pvc-secret02-bootdisk
                - name: secret02-fs
                  secret:
                     secretName: secret02-config
                - name: cloudinitdisk
                  cloudInitConfigDrive:
                     userData: |-
                        #cloud-config
                        hostname: vm-secret02
                        ssh_pwauth: True
                        disable_root: False
                        timezone: Asia/Shanghai
                        password: 123456
                        chpasswd: {"list":"root:12345678",expire: False}
                        bootcmd:
                          - setenforce 0
                          - sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
                          - mount -t virtiofs secret02-fs /mnt
                        runcmd:
                          - sed -i "/PermitRootLogin/s/^.*$/PermitRootLogin yes/g" /etc/ssh/sshd_config
                          - systemctl restart sshd.service
                          #- echo root:12345678 |chpasswd
    
    注意 同样,通过 kubectl edit secret secret02-config修改secret不会马上传播到虚拟机中,因为secret刷新到pod需要一定时间。kubelet通过 --sync-frequency控制同步配置的时间间隔,默认是1min,所以更新secret后,在容器中真正看到变化,需要0-1min之后。该值改得太小会导致kubelet占用过多资源,影响性能。 可通过修改:/var/lib/kubelet/config.yaml中的syncFrequency字段的值来并重启kubelet生效。 例如:syncFrequency: 10s

  • serviceAccount(服务账户)

    serviceAccount也可以以磁盘形式或文件系统形式呈现给虚拟机。每种方法都有优缺点,例如:磁盘方式不支持动态变更传播;而文件系统方式则不支持实时迁移。 当serviceAccount以磁盘形式呈现给虚拟机时,将会分配一个额外的ISO格式的磁盘,该磁盘需要在虚拟机(VM)上挂载。为了自动挂载serviceAccount,可以利用cloudInit 挂载。需要注意的是serviceAccount的更新并不会传播到VMI中。

    serviceAccount以文件系统形式呈现给虚拟机时,serviceAccount是通过virtiofs进行共享,与通过磁盘方式共享serviceAccount相比,使用文件系统方式允许将serviceAccount上的动态变更实时传播到VMI(即虚拟机实例无需重启即可获取更改)。但使用了virtiofsVM就不支持实时迁移了。

挂载serviceAccount 后,在挂载目录下会看到命名空间(namespace)、令牌(token)和 CA 证书(ca.crt)三个文件

具体示例可参考configMap,需要将configMap改成serviceAccount 示例,以virtiofs格式,注意,不能再使用cirros镜像,不支持virtiofs

---
apiVersion: v1
kind: ServiceAccount
metadata:
   name: vm-acc02
# 默认会自动挂载API凭据
automountServiceAccountToken: true
---
# 手动为ServiceAccount创建长期有效的 API 令牌
apiVersion: v1
kind: Secret
metadata:
   name: build-robot-secret
   annotations:
      kubernetes.io/service-account.name: vm-acc02
type: kubernetes.io/service-account-token
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: pvc-acc02-bootdisk
   namespace: default
spec:
   storageClassName: "ceph-hdd-block"
   volumeMode: Block
   accessModes:
      - ReadWriteMany
   dataSource:
      kind: PersistentVolumeClaim
      # 不能用cirros镜像,因为不支持 virtiofs
      name: img-ubuntu2204
   resources:
      requests:
         storage: 10Gi
---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
   name: vm-acc02
spec:
   instancetype:
      kind: VirtualMachineInstancetype
      name: p1-1
   running: true
   template:
      metadata:
         labels:
            kubevirt.io/domain: vm-acc02
      spec:
         #nodeSelector:
         #   kubernetes.io/hostname: k8s04
         architecture: amd64
         domain:
            devices:
               disks:
                  - disk:
                       bus: virtio
                    name: root-disk
                  - disk:
                       bus: virtio
                    name: cloudinitdisk
               filesystems:
                  - name: serviceaccount-fs
                    virtiofs: {}
               rng: {}
               interfaces:
                  - name: default
                    masquerade: {}
         networks:
            - name: default
              pod: {}
         terminationGracePeriodSeconds: 0
         volumes:
            - name: root-disk
              persistentVolumeClaim:
                 claimName: pvc-acc02-bootdisk
            - name: serviceaccount-fs
              serviceAccount:
                 serviceAccountName: vm-acc02
            - name: cloudinitdisk
              cloudInitConfigDrive:
                 userData: |-
                    #cloud-config
                    hostname: vm-acc02
                    ssh_pwauth: True
                    disable_root: False
                    timezone: Asia/Shanghai
                    password: 123456
                    chpasswd: {"list":"root:12345678",expire: False}
                    bootcmd:
                      - setenforce 0
                      - sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
                      - mount -t virtiofs serviceaccount-fs /mnt
                    runcmd:
                      - sed -i "/PermitRootLogin/s/^.*$/PermitRootLogin yes/g" /etc/ssh/sshd_config
                      - systemctl restart sshd.service
                      #- echo root:12345678 |chpasswd
> 进入虚拟机查看,发现/mnt下有ca.crt、namespace、token文件
# ls -l /mnt/
total 0
lrwxrwxrwx 1 root root 13 Feb 24 15:00 ca.crt -> ..data/ca.crt
lrwxrwxrwx 1 root root 16 Feb 24 15:00 namespace -> ..data/namespace
lrwxrwxrwx 1 root root 12 Feb 24 15:00 token -> ..data/token
- downwardMetrics(向下指标)

downwardMetrics允许将有限的虚拟机和主机指标暴露给虚拟机访问,其格式与vhostmd 兼容。 在某些情况下,允许虚拟机内的应用程序访问由宿主机收集的虚拟机和宿主机性能指标数据,这对于第三方诊断其应用设备上的性能问题是必要的。

为了实现虚拟机访问downwardMetrics的指标数据,KubeVirt 支持两种传输途径:一种是通过创建一个带有相关指标数据的disk磁盘文件;另一种是通过virtio-serial端口进行传输。

当配置为disk类型时,kubeVirt会为虚拟机创建并提供一个原始块存储设备,这个卷的内容可以是持久化存储或其他类型的数据源,并且kubeVirt会按照默认设定每隔5秒周期性地检查并更新这个卷的状态, 确保虚拟机与其关联的存储卷之间数据的一致性和实时性。这意味着虚拟机可以直接读取和写入这个卷,就像操作一个真实的硬件磁盘一样。

当配置为virtio-serial port类型时,kubeVirt在虚拟机内部创建一个名为/dev/virtio-ports/org.github.vhostmd.1的端口,该端口支持virtio传输协议。 可以通过这个端口获取downwardMetrics 数据。另外,若要采用virtio-serial port方式对外提供downwardMetrics,需要在VM的domain 设备部分显式声明一个downwardMetrics设备条目。 这样,kubeVirt将会自动将相应的指标数据通过virtio-serial port 传递给虚拟机内部,便于虚拟机内部应用或服务能够访问和使用这些性能监控指标。

使用 downwardMetrics 需要先在kubevirt的featureGates中开启特性门控DownwardMetrics

示例,以disk磁盘文件方式

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: pvc-dm01-bootdisk
   namespace: default
spec:
   storageClassName: "ceph-hdd-block"
   volumeMode: Block
   accessModes:
      - ReadWriteMany
   dataSource:
      kind: PersistentVolumeClaim
      name: img-ubuntu2204
   resources:
      requests:
         storage: 10Gi
---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
   name: vm-dm01
spec:
   instancetype:
      kind: VirtualMachineInstancetype
      name: p1-1
   running: true
   template:
      metadata:
         labels:
            kubevirt.io/domain: vm-dm01
      spec:
         #nodeSelector:
         #   kubernetes.io/hostname: k8s04
         architecture: amd64
         domain:
            devices:
               disks:
                  - disk:
                       bus: virtio
                    name: root-disk
                  - disk:
                       bus: virtio
                    name: cloudinitdisk
                  - disk:
                       bus: virtio
                    name: downwardmetrics
               rng: {}
               interfaces:
                  - name: default
                    masquerade: {}
         networks:
            - name: default
              pod: {}
         terminationGracePeriodSeconds: 0
         volumes:
            - name: root-disk
              persistentVolumeClaim:
                 claimName: pvc-dm01-bootdisk
            - name: downwardmetrics
              downwardMetrics: {}
            - name: cloudinitdisk
              cloudInitConfigDrive:
                 userData: |-
                    #cloud-config
                    hostname: vm-dm01
                    ssh_pwauth: True
                    disable_root: False
                    timezone: Asia/Shanghai
                    password: 123456
                    chpasswd: {"list":"root:12345678",expire: False}
                    bootcmd:
                      - setenforce 0
                      - sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
                    runcmd:
                      - sed -i "/PermitRootLogin/s/^.*$/PermitRootLogin yes/g" /etc/ssh/sshd_config
                      - systemctl restart sshd.service
                      #- echo root:12345678 |chpasswd

示例,以Virtio-serial port方式(推荐)

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
   name: pvc-dm02-bootdisk
   namespace: default
spec:
   storageClassName: "ceph-hdd-block"
   volumeMode: Block
   accessModes:
      - ReadWriteMany
   dataSource:
      kind: PersistentVolumeClaim
      name: img-ubuntu2204
   resources:
      requests:
         storage: 10Gi
---
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
   name: vm-dm02
spec:
   instancetype:
      kind: VirtualMachineInstancetype
      name: p1-1
   running: true
   template:
      metadata:
         labels:
            kubevirt.io/domain: vm-dm02
      spec:
         #nodeSelector:
         #   kubernetes.io/hostname: k8s04
         architecture: amd64
         domain:
            devices:
               # 暴露指标数据
               downwardMetrics: {}
               disks:
                  - disk:
                       bus: virtio
                    name: root-disk
                  - disk:
                       bus: virtio
                    name: cloudinitdisk
               rng: {}
               interfaces:
                  - name: default
                    masquerade: {}
         networks:
            - name: default
              pod: {}
         terminationGracePeriodSeconds: 0
         volumes:
            - name: root-disk
              persistentVolumeClaim:
                 claimName: pvc-dm02-bootdisk
            - name: cloudinitdisk
              cloudInitConfigDrive:
                 userData: |-
                    #cloud-config
                    hostname: vm-dm02
                    ssh_pwauth: True
                    disable_root: False
                    timezone: Asia/Shanghai
                    password: 123456
                    chpasswd: {"list":"root:12345678",expire: False}
                    bootcmd:
                      - setenforce 0
                      - sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
                    runcmd:
                      - sed -i "/PermitRootLogin/s/^.*$/PermitRootLogin yes/g" /etc/ssh/sshd_config
                      - systemctl restart sshd.service
                      #- echo root:12345678 |chpasswd
查看指标数据

进入虚拟机,在/dev/vdc中个原始存储盘中查看到有类似如下指标数据

## 这里直接用cat命令查看了,可以在虚拟机中安装`vhostmd`,安装方式参照 https://github.com/vhostmd/vhostmd,安装成功后会生成`vm_dump_metrics`命令,也可以通过`vhostmd`命令查看指标数据
# cat /dev/vdc 
mvbd  <metrics>
  <metric type="string" context="host">
    <name>HostName</name>
    <value>k8s04</value>
  </metric>
  <metric type="string" context="host">
    <name>HostSystemInfo</name>
    <value>linux</value>
  </metric>
  <metric type="string" context="host">
    <name>VirtualizationVendor</name>
    <value>kubevirt.io</value>
  </metric>
  <metric type="string" context="host">
    <name>VirtProductInfo</name>
    <value>QEMU 8.0.0</value>
  </metric>
  <metric type="real64" context="vm" unit="s">
    <name>TotalCPUTime</name>
    <value>96.370000</value>
  </metric>
 ...
ubuntu 安装vm-dump-metrics
# wget https://github.com/vhostmd/vhostmd/archive/refs/tags/v1.1.zip
# apt -y install make unzip libtool pkg-config libxml2-dev libvirt-daemon libvirt-daemon-system libvirt-dev
# cd vhostmd-1.1
# sh autogen.sh 
# ./configure --prefix=/usr --libdir=/usr/lib64 --disable-shared
#  make
# make install
## 直接执行命令
# vm-dump-metrics
<metrics>
  <metric type="string" context="host">
    <name>HostName</name>
    <value>k8s04</value>
  </metric>
  <metric type="string" context="host">
    <name>HostSystemInfo</name>
    <value>linux</value>
  </metric>
  <metric type="string" context="host">
    <name>VirtualizationVendor</name>
    <value>kubevirt.io</value>
  </metric>
  <metric type="string" context="host">
    <name>VirtProductInfo</name>
    <value>QEMU 8.0.0</value>
  </metric>
  <metric type="real64" context="vm" unit="s">
    <name>TotalCPUTime</name>
    <value>342.840000</value>
  </metric>
  <metric type="uint64" context="vm">
    <name>ResourceProcessorLimit</name>
    <value>1</value>
  </metric>
  <metric type="uint64" context="vm" unit="KiB">
    <name>PhysicalMemoryAllocatedToVirtualSystem</name>
    <value>1048576</value>
  </metric>
  <metric type="uint64" context="vm" unit="KiB">
    <name>ResourceMemoryLimit</name>
    <value>1048576</value>
  </metric>
  <metric type="int64" context="host">
    <name>NumberOfPhysicalCPUs</name>
    <value>1</value>
  </metric>
  <metric type="real64" context="host" unit="s">
    <name>TotalCPUTime</name>
    <value>2514.730000</value>
  </metric>
  <metric type="uint64" context="host" unit="KiB">
    <name>FreePhysicalMemory</name>
    <value>6735648</value>
  </metric>
  <metric type="uint64" context="host" unit="KiB">
    <name>FreeVirtualMemory</name>
    <value>6735648</value>
  </metric>
  <metric type="uint64" context="host" unit="KiB">
    <name>MemoryAllocatedToVirtualServers</name>
    <value>2968996</value>
  </metric>
  <metric type="uint64" context="host" unit="KiB">
    <name>UsedVirtualMemory</name>
    <value>2968996</value>
  </metric>
  <metric type="uint64" context="host" unit="KiB">
    <name>PagedInMemory</name>
    <value>0</value>
  </metric>
  <metric type="uint64" context="host" unit="KiB">
    <name>PagedOutMemory</name>
    <value>0</value>
  </metric>
  <metric type="int64" context="host" unit="s">
    <name>Time</name>
    <value>1708766512</value>
  </metric>
</metrics>
如果是以Virtio-serial port方式,可以用命令vm-dump-metrics --virtio查看指标数据

如果是centos或者openeuler,可以直接用yum install vm-dump-metrics 进行安装。