使用 KubeVirt 基于 ISO 创建 Windows 镜像

本文档介绍基于开源组件 KubeVirt 的虚拟机方案,利用 KubeVirt 虚拟化技术通过 ISO 镜像文件创建 Windows 操作系统镜像。

目录

前提条件

  • 集群中的所有组件均正常运行。

  • 请提前准备好 Windows 镜像和最新的 virtio-win-tools

  • 请准备好用于存储镜像的仓库,本文以 build-harbor.example.cn 仓库为例,请根据实际环境替换。

约束与限制

  • 启动 KubeVirt 时,自定义镜像的文件系统大小会影响将镜像写入 PVC 磁盘的速度,文件系统过大可能导致创建时间延长。

  • 建议保持 Linux 根分区或 Windows C 盘小于 100G,以减少初始大小,后续可通过 cloud-init 扩容(Windows 系统需创建后手动扩容)。

操作步骤

创建镜像

通过准备好的 Windows 和 virtio-win ISO 镜像创建 Docker 镜像,并推送到仓库。本文以 Windows Server 2019 为例。

从 Windows ISO 创建 Docker 镜像

  1. 进入存放 ISO 镜像的目录,在终端执行以下命令,将 ISO 镜像重命名为 win.iso。

    mv <ISO image name> win.iso # 将 <ISO image name> 替换为实际镜像名称,例如 mv en_windows_server_2019_x64_dvd_4cb967d8.iso win.iso
  2. 执行以下命令创建 Dockerfile。

    touch Dockerfile
  3. 编辑 Dockerfile,添加以下内容并保存。

    FROM scratch
    ADD --chown=107:107 win.iso /disk/
  4. 执行以下命令构建 Docker 镜像。

    docker build -t build-harbor.example.cn/3rdparty/vmdisks/winiso:2019 . # 根据实际环境替换仓库地址
  5. 执行以下命令将镜像推送到仓库。

    docker push  build-harbor.example.cn/3rdparty/vmdisks/winiso:2019 # 根据实际环境替换仓库地址

从 virtio-win ISO 创建 Docker 镜像

  1. 进入存放 ISO 镜像的目录,执行以下命令创建 Dockerfile。

    touch Dockerfile
  2. 编辑 Dockerfile,添加以下内容并保存。

    FROM scratch
    ADD --chown=107:107 virtio-win.iso  /disk/
  3. 执行以下命令构建 Docker 镜像。

    docker build -t build-harbor.example.cn/3rdparty/vmdisks/win-virtio:latest . # 根据实际环境替换仓库地址
  4. 执行以下命令将镜像推送到仓库。

    docker push  build-harbor.example.cn/3rdparty/vmdisks/win-virtio:latest # 根据实际环境替换仓库地址

创建虚拟机

  1. 进入 容器平台

  2. 在左侧导航栏点击 虚拟化 > 虚拟机

  3. 点击 创建虚拟机

  4. 在表单页面填写必要参数,如 名称镜像 等。详细参数及配置请参考创建虚拟机

  5. 切换至 YAML。

  6. 将 spec.template.spec.domain.devices.disks 字段下的配置替换为以下内容。

      domain:
        devices:
          disks:
            - disk:
                bus: virtio
              name: cloudinitdisk
            - bootOrder: 1
              cdrom:
                bus: sata
              name: containerdisk
            - cdrom:
                bus: sata
              name: virtio
            - disk:
                bus: sata
              name: rootfs
              bootOrder: 10
  7. 在 spec.template.spec.volumes 字段下添加以下内容。

            - containerDisk:
                image: registry.example.cn:60070/3rdparty/vmdisks/winiso:2019 # 根据实际环境替换镜像地址
              name: containerdisk
            - containerDisk:
                image: registry.example.cn:60070/3rdparty/vmdisks/win-virtio # 根据实际环境替换镜像地址
              name: virtio
  8. 检查 YAML 文件,完成配置后的完整 YAML 如下。

    apiVersion: kubevirt.io/v1alpha3
    kind: VirtualMachine
    metadata:
      annotations:
        cpaas.io/creator: test@example.io
        cpaas.io/display-name: ""
        cpaas.io/updated-at: 2024-09-01T14:57:55Z
        kubevirt.io/latest-observed-api-version: v1
        kubevirt.io/storage-observed-api-version: v1
      generation: 16
      labels:
        virtualization.cpaas.io/image-name: debian-2120-x86
        virtualization.cpaas.io/image-os-arch: amd64
        virtualization.cpaas.io/image-os-type: debian
        virtualization.cpaas.io/image-supply-by: public
        vm.cpaas.io/name: aa-test
      name: aa-test
      namespace: acp-service-self
    spec:
      dataVolumeTemplates:
        - metadata:
            creationTimestamp: null
            labels:
              vm.cpaas.io/reclaim-policy: Delete
              vm.cpaas.io/used-by: aa-test
            name: aa-test-rootfs
          spec:
            pvc:
              accessModes:
                - ReadWriteOnce
              resources:
                requests:
                  storage: 100Gi
              storageClassName: vm-cephrbd
              volumeMode: Block
            source:
              http:
                url: http://192.168.254.12/kube-debian-12.2.0-x86-out.qcow2
      running: true
      template:
        metadata:
          annotations:
            cpaas.io/creator: test@example.io
            cpaas.io/display-name: ""
            cpaas.io/updated-at: 2024-09-01T14:55:44Z
            kubevirt.io/latest-observed-api-version: v1
            kubevirt.io/storage-observed-api-version: v1
          creationTimestamp: null
          labels:
            virtualization.cpaas.io/image-name: debian-2120-x86
            virtualization.cpaas.io/image-os-arch: amd64
            virtualization.cpaas.io/image-os-type: debian
            virtualization.cpaas.io/image-supply-by: public
            vm.cpaas.io/name: aa-test
        spec:
          affinity:
            nodeAffinity: {}
          architecture: amd64
          domain:
            devices:
              disks:
                - disk:
                    bus: virtio
                  name: cloudinitdisk
                - bootOrder: 1
                  cdrom:
                    bus: sata
                  name: containerdisk
                - cdrom:
                    bus: sata
                  name: virtio
                - disk:
                    bus: sata
                  name: rootfs
                  bootOrder: 10
               interfaces:
                - bridge: {}
                  name: default
            machine:
              type: q35
            resources:
              limits:
                cpu: "4"
                memory: 8Gi
              requests:
                cpu: "4"
                memory: 8Gi
          networks:
            - name: default
              pod: {}
          nodeSelector:
            kubernetes.io/arch: amd64
            vm.cpaas.io/baremetal: "true"
          volumes:
            - cloudInitConfigDrive:
                userData: >-
                  #cloud-config
                  disable_root: false
                  ssh_pwauth: true
                  users:
                    - default
                    - name: root
                      lock_passwd: false
                      hashed_passwd: $6$0vlhl57e$0rawYwaeu9jL6hBf3XP9lk6XXaMUS9/W6LPbWRinUoXujo39lP3l98VOcOObtr.LDoAv/ylm85FLQmxwNlWFe/
              name: cloudinitdisk
            - containerDisk:
                image: registry.example.cn:60070/3rdparty/vmdisks/winiso:2019 # 根据实际环境替换镜像地址
              name: containerdisk
              - containerDisk
                 image: registry.example.cn:60070/3rdparty/vmdisks/win-virtio # 根据实际环境替换镜像地址
               name: virtio
             - dataVolume:
                name: aa-test-rootfs
              name: rootfs
  9. 点击 创建

  10. 点击 操作 > VNC 登录

  11. 当出现提示 press any key boot from CD or DVD 时,按任意键进入 Windows 安装程序;若未看到提示,请点击页面左上角的 发送远程命令,从下拉菜单选择 Ctrl-Alt-Delete 重启服务器。

    注意:若虚拟机详情页顶部出现提示 当前虚拟机有配置变更需重启生效,请重启,可忽略该提示,无需重启。

安装 Windows 操作系统

  1. 进入安装页面后,按照安装指引完成系统安装。

    注意:分区选择步骤中,磁盘总线必须为 sata,系统才能正确识别磁盘。需依次选择每个分区并点击 删除,清除所有分区,由系统自动处理。

  2. 配置管理员账户密码后,点击页面左上角的 发送远程命令,选择下拉菜单中的 Ctrl-Alt-Delete

  3. 出现提示 Ctrl+Alt+Delete 组合键将重启服务器,确认重启 时,点击 确定

  4. 输入密码进入 Windows 系统桌面,至此 Windows 操作系统安装完成。

安装 virtio-win-tools

该工具主要包含必要驱动。

  1. 打开文件资源管理器。

  2. 双击 CD 驱动器(E:) virtio-win-<version>,运行 virtio-win-guest-tools 目录进入安装页面,按照安装指引完成安装。<version> 部分请根据实际情况替换。

  3. 安装完成后,关闭 Windows 系统电源。

导出自定义 Windows 镜像

具体操作请参考导出虚拟机镜像

使用 Windows 镜像

  1. 进入 容器平台

  2. 在左侧导航栏点击 虚拟化 > 虚拟机

  3. 点击列表中使用 Windows 镜像创建的虚拟机名称,进入详情页。

  4. 点击 创建虚拟机

  5. 在表单页面填写必要参数,镜像选择导出的 Windows 镜像。详细参数及配置请参考创建虚拟机

  6. (可选)若使用较新操作系统,如 Windows 11,需启用时钟、UEFI、TPM 等功能。切换至 YAML,替换原 YAML 文件为以下内容。

    apiVersion: kubevirt.io/v1
    kind: VirtualMachineInstance
    metadata:
      labels:
        special: vmi-windows
      name: vmi-windows
    spec:
      domain:
        clock:
          timer:
            hpet:
              present: false
            hyperv: {}
            pit:
              tickPolicy: delay
            rtc:
              tickPolicy: catchup
          utc: {}
        cpu:
          cores: 2
        devices:
          disks:
          - disk:
              bus: sata
            name: pvcdisk
          interfaces:
          - masquerade: {}
            model: e1000
            name: default
          tpm: {}
        features:
          acpi: {}
          apic: {}
          hyperv:
            relaxed: {}
            spinlocks:
              spinlocks: 8191
            vapic: {}
          smm: {}
        firmware:
          bootloader:
            efi:
              secureBoot: true
          uuid: 5d307ca9-b3ef-428c-8861-06e72d69f223
        resources:
          requests:
            memory: 4Gi
      networks:
      - name: default
        pod: {}
      terminationGracePeriodSeconds: 0
      volumes:
      - name: pvcdisk
        persistentVolumeClaim:
          claimName: disk-windows
      - name: winiso
        persistentVolumeClaim:
          claimName: win11cd-pvc
  7. 点击 创建

添加内部路由

通过配置 NodePort 类型的内部路由,暴露远程桌面连接端口。

  1. 进入 容器平台

  2. 在左侧导航栏点击 虚拟化 > 虚拟机

  3. 点击列表中使用 Windows 镜像创建的虚拟机名称,进入详情页。

  4. 登录信息 区域,点击 内部路由 旁的 添加 图标。

  5. 按照以下说明配置参数。

    参数说明
    类型选择 NodePort
    端口
    • 协议:选择 TCP。
    • 服务端口:使用 3389。
    • 虚拟机端口:使用 3389。
    • 服务端口名称:使用 rdp。
  6. 点击 确定 返回详情页。

  7. 点击 登录信息 区域的 内部路由 链接。

  8. 记录基本信息区域的 虚拟 IP 和端口区域的 主机端口 信息。

远程访问

本文以 Windows 操作系统远程连接为例,其他操作系统可使用支持 RDP 协议的软件进行连接。

  1. 打开 远程桌面连接

  2. 输入在 添加内部路由 步骤中保存的虚拟 IP 和主机端口,格式为 虚拟 IP:主机端口 ,例如:192.1.1.1:3389

  3. 点击 连接