Containerized Data Importer

The Containerized Data Importer (CDI) project provides facilities for enabling Persistent Volume Claims (PVCs) to be used as disks for KubeVirt VMs by way of DataVolumes. The three main CDI use cases are:

  • Import a disk image from a web server or container registry to a DataVolume
  • Clone an existing PVC to a DataVolume
  • Upload a local disk image to a DataVolume

This document deals with the third use case. So you should have CDI installed in your cluster, a VM disk that you’d like to upload, and virtctl in your path.

Install CDI

Install the latest CDI release here

  1. export TAG=$(curl -s -w %{redirect_url} https://github.com/kubevirt/containerized-data-importer/releases/latest)
  2. export VERSION=$(echo ${TAG##*/})
  3. kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-operator.yaml
  4. kubectl create -f https://github.com/kubevirt/containerized-data-importer/releases/download/$VERSION/cdi-cr.yaml

Expose cdi-uploadproxy service

The cdi-uploadproxy service must be accessible from outside the cluster. Here are some ways to do that:

Look here for example manifests.

Supported image formats

CDI supports the raw and qcow2 image formats which are supported by qemu. See the qemu documentation for more details. Bootable ISO images can also be used and are treated like raw images. Images may be compressed with either the gz or xz format.

The example in this document uses this CirrOS image

virtctl image-upload

virtctl has an image-upload command with the following options:

  1. virtctl image-upload --help
  2. Upload a VM image to a DataVolume/PersistentVolumeClaim.
  3. Usage:
  4. virtctl image-upload [flags]
  5. Examples:
  6. # Upload a local disk image to a newly created DataVolume:
  7. virtctl image-upload dv fedora-dv --size=10Gi --image-path=/images/fedora30.qcow2
  8. # Upload a local disk image to an existing DataVolume
  9. virtctl image-upload dv fedora-dv --no-create --image-path=/images/fedora30.qcow2
  10. # Upload a local disk image to a newly created PersistentVolumeClaim
  11. virtctl image-upload pvc fedora-pvc --size=10Gi --image-path=/images/fedora30.qcow2
  12. # Upload a local disk image to a newly created PersistentVolumeClaim and label it with a default instance type and preference
  13. virtctl image-upload pvc fedora-pvc --size=10Gi --image-path=/images/fedora30.qcow2 --default-instancetype=n1.medium --default-preference=fedora
  14. # Upload a local disk image to an existing PersistentVolumeClaim
  15. virtctl image-upload pvc fedora-pvc --no-create --image-path=/images/fedora30.qcow2
  16. # Upload to a DataVolume with explicit URL to CDI Upload Proxy
  17. virtctl image-upload dv fedora-dv --uploadproxy-url=https://cdi-uploadproxy.mycluster.com --image-path=/images/fedora30.qcow2
  18. # Upload a local disk archive to a newly created DataVolume:
  19. virtctl image-upload dv fedora-dv --size=10Gi --archive-path=/images/fedora30.tar
  20. Flags:
  21. --access-mode string The access mode for the PVC.
  22. --archive-path string Path to the local archive.
  23. --default-instancetype string The default instance type to associate with the image.
  24. --default-instancetype-kind string The default instance type kind to associate with the image.
  25. --default-preference string The default preference to associate with the image.
  26. --default-preference-kind string The default preference kind to associate with the image.
  27. --force-bind Force bind the PVC, ignoring the WaitForFirstConsumer logic.
  28. -h, --help help for image-upload
  29. --image-path string Path to the local VM image.
  30. --insecure Allow insecure server connections when using HTTPS.
  31. --no-create Don't attempt to create a new DataVolume/PVC.
  32. --size string The size of the DataVolume to create (ex. 10Gi, 500Mi).
  33. --storage-class string The storage class for the PVC.
  34. --uploadproxy-url string The URL of the cdi-upload proxy service.
  35. --volume-mode string Specify the VolumeMode (block/filesystem) used to create the PVC. Default is the storageProfile default. For archive upload default is filesystem.
  36. --wait-secs uint Seconds to wait for upload pod to start. (default 300)
  37. Use "virtctl options" for a list of global command-line options (applies to all commands).

virtctl image-upload works by creating a DataVolume of the requested size, sending an UploadTokenRequest to the cdi-apiserver, and uploading the file to the cdi-uploadproxy.

  1. virtctl image-upload dv cirros-vm-disk --size=500Mi --image-path=/home/mhenriks/images/cirros-0.4.0-x86_64-disk.img --uploadproxy-url=<url to upload proxy service>

Addressing Certificate Issues when Uploading Images

Issues with the certificates can be circumvented by using the --insecure flag to prevent the virtctl command from verifying the remote host. It is better to resolve certificate issues that prevent uploading images using the virtctl image-upload command and not use the --insecure flag.

The following are some common issues with certificates and some easy ways to fix them.

Does not contain any IP SANs

This issue happens when trying to upload images using an IP address instead of a resolvable name. For example, trying to upload to the IP address 192.168.39.32 at port 31001 would produce the following error.

  1. virtctl image-upload dv f33 \
  2. --size 5Gi \
  3. --image-path Fedora-Cloud-Base-33-1.2.x86_64.raw.xz \
  4. --uploadproxy-url https://192.168.39.32:31001
  5. PVC default/f33 not found
  6. DataVolume default/f33 created
  7. Waiting for PVC f33 upload pod to be ready...
  8. Pod now ready
  9. Uploading data to https://192.168.39.32:31001
  10. 0 B / 193.89 MiB [-------------------------------------------------------] 0.00% 0s
  11. Post https://192.168.39.32:31001/v1beta1/upload: x509: cannot validate certificate for 192.168.39.32 because it doesn't contain any IP SANs

It is easily fixed by adding an entry it your local name resolution service. This could be a DNS server or the local hosts file. The URL used to upload the proxy should be changed to reflect the resolvable name.

The Subject and the Subject Alternative Name in the certificate contain valid names that can be used for resolution. Only one of these names needs to be resolvable. Use the openssl command to view the names of the cdi-uploadproxy service.

  1. echo | openssl s_client -showcerts -connect 192.168.39.32:31001 2>/dev/null \
  2. | openssl x509 -inform pem -noout -text \
  3. | sed -n -e '/Subject.*CN/p' -e '/Subject Alternative/{N;p}'
  4. Subject: CN = cdi-uploadproxy
  5. X509v3 Subject Alternative Name:
  6. DNS:cdi-uploadproxy, DNS:cdi-uploadproxy.cdi, DNS:cdi-uploadproxy.cdi.svc

Adding the following entry to the /etc/hosts file, if it provides name resolution, should fix this issue. Any service that provides name resolution for the system could be used.

  1. echo "192.168.39.32 cdi-uploadproxy" >> /etc/hosts

The upload should now work.

  1. virtctl image-upload dv f33 \
  2. --size 5Gi \
  3. --image-path Fedora-Cloud-Base-33-1.2.x86_64.raw.xz \
  4. --uploadproxy-url https://cdi-uploadproxy:31001
  5. PVC default/f33 not found
  6. DataVolume default/f33 created
  7. Waiting for PVC f33 upload pod to be ready...
  8. Pod now ready
  9. Uploading data to https://cdi-uploadproxy:31001
  10. 193.89 MiB / 193.89 MiB [=============================================] 100.00% 1m38s
  11. Uploading data completed successfully, waiting for processing to complete, you can hit ctrl-c without interrupting the progress
  12. Processing completed successfully
  13. Uploading Fedora-Cloud-Base-33-1.2.x86_64.raw.xz completed successfully

Certificate Signed by Unknown Authority

This happens because the cdi-uploadproxy certificate is self signed and the system does not trust the cdi-uploadproxy as a Certificate Authority.

  1. virtctl image-upload dv f33 \
  2. --size 5Gi \
  3. --image-path Fedora-Cloud-Base-33-1.2.x86_64.raw.xz \
  4. --uploadproxy-url https://cdi-uploadproxy:31001
  5. PVC default/f33 not found
  6. DataVolume default/f33 created
  7. Waiting for PVC f33 upload pod to be ready...
  8. Pod now ready
  9. Uploading data to https://cdi-uploadproxy:31001
  10. 0 B / 193.89 MiB [-------------------------------------------------------] 0.00% 0s
  11. Post https://cdi-uploadproxy:31001/v1beta1/upload: x509: certificate signed by unknown authority

This can be fixed by adding the certificate to the systems trust store. Download the cdi-uploadproxy-server-cert.

  1. kubectl get secret -n cdi cdi-uploadproxy-server-cert \
  2. -o jsonpath="{.data['tls\.crt']}" \
  3. | base64 -d > cdi-uploadproxy-server-cert.crt

Add this certificate to the systems trust store. On Fedora, this can be done as follows.

  1. sudo cp cdi-uploadproxy-server-cert.crt /etc/pki/ca-trust/source/anchors
  2. sudo update-ca-trust

The upload should now work.

  1. virtctl image-upload dv f33 \
  2. --size 5Gi \
  3. --image-path Fedora-Cloud-Base-33-1.2.x86_64.raw.xz \
  4. --uploadproxy-url https://cdi-uploadproxy:31001
  5. PVC default/f33 not found
  6. DataVolume default/f33 created
  7. Waiting for PVC f33 upload pod to be ready...
  8. Pod now ready
  9. Uploading data to https://cdi-uploadproxy:31001
  10. 193.89 MiB / 193.89 MiB [=============================================] 100.00% 1m36s
  11. Uploading data completed successfully, waiting for processing to complete, you can hit ctrl-c without interrupting the progress
  12. Processing completed successfully
  13. Uploading Fedora-Cloud-Base-33-1.2.x86_64.raw.xz completed successfully

Setting the URL of the cdi-upload Proxy Service

Setting the URL for the cdi-upload proxy service allows the virtctl image-upload command to upload the images without specifying the --uploadproxy-url flag. Permanently setting the URL is done by patching the CDI configuration.

The following will set the default upload proxy to use port 31001 of cdi-uploadproxy. An IP address could also be used instead of the dns name.

See the section Addressing Certificate Issues when Uploading for why cdi-uploadproxy was chosen and issues that can be encountered when using an IP address.

  1. kubectl patch cdi cdi \
  2. --type merge \
  3. --patch '{"spec":{"config":{"uploadProxyURLOverride":"https://cdi-uploadproxy:31001"}}}'

Create a VirtualMachineInstance

To create a VirtualMachineInstance from a DataVolume, you can execute the following:

  1. cat <<EOF | kubectl apply -f -
  2. apiVersion: kubevirt.io/v1
  3. kind: VirtualMachineInstance
  4. metadata:
  5. name: cirros-vm
  6. spec:
  7. domain:
  8. devices:
  9. disks:
  10. - disk:
  11. bus: virtio
  12. name: dvdisk
  13. machine:
  14. type: ""
  15. resources:
  16. requests:
  17. memory: 64M
  18. terminationGracePeriodSeconds: 0
  19. volumes:
  20. - name: dvdisk
  21. dataVolume:
  22. name: cirros-vm-disk
  23. status: {}
  24. EOF

Connect to VirtualMachineInstance console

Use virtctl to connect to the newly create VirtualMachineInstance.

  1. virtctl console cirros-vm