上传本地镜像并创建虚拟机

当你已经在本地机器上有一个虚拟机磁盘镜像时,请使用此操作步骤(例如从操作系统厂商下载的 qcow2rawimg 文件),并希望基于该镜像运行虚拟机,而不是从远程 HTTP、registry 或 S3 源导入镜像。

在 Alauda Container Platform 上,这是一个基于 KubeVirt virtctl 客户端和 CDI upload proxy 的命令行工作流——Web 控制台目前还不支持本地上传来源。上传后的镜像会以 DataSource(一个可启动卷)的形式存储,虚拟机可以从中启动,并且可被多个虚拟机复用。

提示:如果你希望改为从 ISO 安装操作系统,请参见 基于 ISO 创建 Linux 镜像基于 ISO 创建 Windows 镜像

前提条件

  • 已安装 virtctl 命令行客户端,并且其版本与集群的 KubeVirt 版本一致。virtctl 是标准的 KubeVirt CLI;请从 KubeVirt releases 下载与你的集群匹配的二进制文件,并将其放到 PATH 中。
  • 已安装并配置好 kubectl 命令行工具,以便访问集群。
  • 本地存在一个 qcow2rawimg 格式的磁盘镜像。为了加快上传速度,可先使用 virt-sparsifyxzgzip 压缩镜像。
  • 一个支持 ReadWriteOnce(RWO)访问模式的 StorageClass
  • cdi-uploadproxy Service(位于 kubevirt 命名空间)可从运行 virtctl 的位置访问——见步骤 1。

操作步骤

在集群外暴露 CDI upload proxy

virtctl image-upload 会将镜像流式传输到 cdi-uploadproxy Service。该 Service 是 kubevirt 命名空间中的内部 ClusterIP Service。你必须让它能从运行 virtctl 的位置访问。

注意:在 OpenShift 上,CDI 会自动发现指向 cdi-uploadproxy 的 OpenShift Route,并为你填充 upload URL。Alauda Container Platform 运行在上游 Kubernetes 上,没有 Route 资源,因此需要你自己暴露该 Service(此步骤),并显式设置 upload URL(步骤 2)。

使用 TLS passthrough,以便 virtctl 直接与 cdi-uploadproxy 协商 TLS(该 proxy 提供自签名证书,因此客户端需要传递 --insecure)。对于下面的每种方式,后端都应为 kubevirt 命名空间中 443 端口上的 cdi-uploadproxy Service;请注意生成的外部 URL,以便在步骤 2 中使用。

  • Gateway API(Envoy Gateway)——推荐。Gateway 添加一个处于 Passthrough 模式的 TLS listener,然后创建一个 TLSRoute,其后端为 443 端口上的 cdi-uploadproxy Service。Gateway 的外部地址——由 Gateway 的 Service Type 设置的 LoadBalancer IP 或 NodePort——将成为 upload URL。完整操作步骤请参见 配置 GatewayAPI Gateway配置 GatewayAPI Route,并参见 Envoy Gateway Operator 以安装控制器。

  • Ingress(ingress-nginx)。 使用启用 SSL passthrough 的 Ingress 暴露 cdi-uploadproxy。请参见 配置 IngressIngress Nginx Operator。旧版 ALB ingress(cpaas.io/alb2)已弃用。

  • LoadBalancer 或 NodePort Service。 如果没有 ingress controller,可以直接暴露该 Service——在配置了 MetalLB 的环境中使用 LoadBalancer,或者使用始终可用的 NodePort

    kubectl expose service cdi-uploadproxy -n kubevirt \
      --name=cdi-uploadproxy-np --type=NodePort --port=443 --target-port=8443
    kubectl get service cdi-uploadproxy-np -n kubevirt   # note the 443:<nodePort> mapping

    通过 https://<node-ip>:<nodePort> 访问该 proxy。

暴露 Service 后,请验证它是否能通过新的 endpoint 正常响应(证书是自签名的,因此请使用 -k):

curl -k https://<upload-proxy-host>/healthz
# -> OK

virtctl 指向 upload proxy URL

virtctl image-upload 需要知道步骤 1 中的外部 URL。以下两种方式任选其一。

一次性配置(持久化)。 设置 CDIConfig.spec.uploadProxyURLOverride,这样每次执行 virtctl image-upload 时都会自动发现该 URL。由于 CDI 配置由 HyperConverged(HCO)operator 管理——它会将直接修改回滚,并且不会暴露此字段——因此需要通过 HCO 的 jsonpatch annotation 来设置:

kubectl annotate hyperconverged kubevirt-hyperconverged -n kubevirt \
  'containerizeddataimporter.kubevirt.io/jsonpatch=[{"op":"add","path":"/spec/config/uploadProxyURLOverride","value":"https://cdi-uploadproxy.example.com:8443"}]' \
  --overwrite

# Verify it propagated to the calculated URL that virtctl reads:
kubectl get cdiconfig config -o jsonpath='{.status.uploadProxyURL}{"\n"}'

警告:HCO 将 jsonpatch annotation 说明为一种高级、不受支持的机制——错误的 patch 可能会使虚拟化栈不稳定。请谨慎使用,并在需要回退时删除该 annotation (kubectl annotate hyperconverged kubevirt-hyperconverged -n kubevirt containerizeddataimporter.kubevirt.io/jsonpatch-) 。

或者按命令传入。 跳过集群配置,在每次上传时通过 --uploadproxy-url 提供该 URL(见步骤 3)。

上传本地镜像

运行 virtctl image-upload--datasource 标志还会创建一个指向已上传卷的 DataSource,使其可被选作可启动卷:

virtctl image-upload dv uploaded-image \
  --datasource \
  --size=30Gi \
  --storage-class=rbd-1 \
  --image-path=./my-image.qcow2 \
  --namespace=my-namespace
# add --uploadproxy-url=https://cdi-uploadproxy.example.com:8443 --insecure
# if you did NOT set uploadProxyURLOverride in Step 2.

说明:

  • --size 必须大于镜像的虚拟磁盘大小(不是文件大小)。例如,如果你的镜像有一个 24 GiB 的虚拟磁盘,请至少请求 30 GiB 的卷。CDI 会拒绝小于未压缩磁盘大小的卷。
  • 使用 --no-create(并省略 --size)可以上传到一个已存在的 DataVolume 中。
  • --insecure 会跳过对 proxy 自签名证书的验证。

等待上传和导入完成:

kubectl get dv uploaded-image -n my-namespace -w
# PHASE: UploadReady -> ... -> Succeeded

kubectl get datasource uploaded-image -n my-namespace
# the DataSource becomes Ready

注意:使用 virtctl --datasource 创建的 DataSource 不包含用于标记可启动卷的 virtualization.cpaas.io/* labels。该镜像可完全通过 API 使用(通过 sourceRef,如下一步所示);如果还希望应用该 label 约定,请参见 Bootable Volumes

从已上传镜像创建虚拟机

通过 sourceRefdataVolumeTemplates 条目中引用该 DataSource。KubeVirt 会将已上传卷克隆为新虚拟机的一块新磁盘:

apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
  name: example-vm
  namespace: my-namespace
spec:
  runStrategy: RerunOnFailure
  dataVolumeTemplates:
    - metadata:
        name: example-vm-rootdisk
      spec:
        sourceRef:
          kind: DataSource
          name: uploaded-image
          namespace: my-namespace
        storage:
          resources:
            requests:
              storage: 30Gi
          storageClassName: rbd-1
  template:
    metadata:
      labels:
        kubevirt.io/vm: example-vm
    spec:
      domain:
        cpu:
          cores: 1
        resources:
          requests:
            memory: 2Gi
        devices:
          disks:
            - name: rootdisk
              disk:
                bus: virtio
          interfaces:
            - name: default
              masquerade: {}
      networks:
        - name: default
          pod: {}
      volumes:
        - name: rootdisk
          dataVolume:
            name: example-vm-rootdisk
kubectl apply -f example-vm.yaml
kubectl get vm example-vm -n my-namespace          # STATUS: Running
kubectl get vmi example-vm -n my-namespace         # PHASE: Running, with an IP

打开串行控制台确认操作系统已启动;出现登录提示符表示启动成功:

virtctl console example-vm -n my-namespace
# example-vm login:

提示:在满足所需 labels 后,也可以在创建虚拟机时将已上传镜像作为可启动卷引用——参见 创建虚拟机Bootable Volumes

创建 Windows 虚拟机

对于 Windows,先将镜像上传到卷中,然后在创建虚拟机时对其进行克隆,并在首次启动期间应用 autounattend.xml 应答文件。上传步骤与上面的操作步骤完全相同(使用 qcow2/raw/img 格式的 Windows 磁盘镜像);关于基于 ISO 的安装流程和 VirtIO 驱动要求,请参见 基于 ISO 创建 Windows 镜像