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

当您已经在本地计算机上拥有虚拟机磁盘镜像(例如从操作系统供应商处下载的 qcow2、raw 或 img 文件),并希望从该镜像运行虚拟机,而不是从远程 HTTP、registry 或 S3 源导入镜像时,请使用此操作步骤。

在 Alauda Container Platform 上,这是一个基于 KubeVirt virtctl 客户端和 CDI 上传代理的命令行工作流 — Web 控制台目前尚不提供本地上传源。上传的镜像将存储为 DataSource(可引导卷),虚拟机可以从中引导,并且多个虚拟机可以重复使用该卷。

提示:如果您希望从 ISO 安装操作系统,请参阅 基于 ISO 创建 Linux 镜像 和 基于 ISO 创建 Windows 镜像。

前提条件

  • 已安装 virtctl 命令行客户端,且其版本与集群的 KubeVirt 版本匹配。 virtctl 是标准的 KubeVirt CLI;请从 KubeVirt releases 下载与集群匹配的二进制文件,并将其放置在您的 PATH 上。
  • 已安装并配置 kubectl 命令行工具,以访问集群。
  • 一个采用 qcow2、raw 或 img 格式的本地磁盘镜像。为了加快上传速度,请先使用 virt-sparsify、xz 或 gzip 压缩镜像。
  • 一个支持 ReadWriteOnce (RWO) 访问模式的 StorageClass。
  • cdi-uploadproxy Service(位于 kubevirt namespace 中)可从运行 virtctl 的位置访问 — 请参阅步骤 1。

操作步骤

将 CDI 上传代理暴露到集群外部

virtctl image-upload 会将镜像流式传输到 cdi-uploadproxy Service。该 Service 是 kubevirt namespace 中的内部 ClusterIP Service。您必须使其可从运行 virtctl 的位置访问。

注意:在 Alauda Container Platform 上,CDI 不会自行暴露上传代理或发现其外部地址,因此您需要自行暴露该 Service(本步骤),并显式设置上传 URL(步骤 2)。

使用 TLS 透传,以便 virtctl 直接与 cdi-uploadproxy 协商 TLS(代理提供自签名证书,因此客户端需传递 --insecure)。对于以下每种方法,后端均为 kubevirt namespace 中、端口为 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 — 将成为上传 URL。有关完整操作步骤,请参阅 配置 GatewayAPI Gateway 和 配置 GatewayAPI Route;有关安装控制器的信息,请参阅 Envoy Gateway Operator。

  • Ingress (ingress-nginx)。 使用 SSL 透传的 Ingress 暴露 cdi-uploadproxy。请参阅 配置 Ingress 和 Ingress 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> 访问代理。

暴露 Service 后,通过新的端点验证其是否能够响应(证书为自签名证书,因此请使用 -k):

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

将 virtctl 指向上传代理 URL

virtctl image-upload 需要知道步骤 1 中的外部 URL。请选择以下方式之一。

配置一次(持久配置)。 设置 CDIConfig.spec.uploadProxyURLOverride,使每个 virtctl image-upload 自动发现该 URL。由于 CDI 配置由 HyperConverged (HCO) operator 管理 — 该 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 会跳过对代理自签名证书的验证。

等待上传和导入完成:

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,如下一步所示)可以完全使用该镜像;如需同时应用该标签约定,请参阅可引导卷。

从已上传的镜像创建虚拟机

通过 sourceRef,在 dataVolumeTemplates 条目中引用 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 后,创建虚拟机时也可以将已上传的镜像作为可引导卷引用 — 请参阅 创建虚拟机 和 可引导卷。

创建 Windows 虚拟机

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