From 1f91cd1ee0f49aac7bc9a779e3b4f0acbffefe35 Mon Sep 17 00:00:00 2001 From: Elisamara Aoki Goncalves Date: Thu, 6 Jun 2024 15:39:42 +0000 Subject: [PATCH] Update documentation for Kubevirt Add Usage Examples sections. Create KubeVirt Architecture section. Fix minor editorial issues. Fix grammar and formatting issues. Story: 2010931 Task: 50286 Change-Id: I6118d0af848d07f3764eeae5ea8467864c65fceb Signed-off-by: Elisamara Aoki Goncalves --- doc/source/_vendor/rl-strings.txt | 2 +- ...nfiguration-as-filesystem-0a5023c8386e.rst | 102 ++++++ .../figures/kubevirt-architecture.png | Bin 0 -> 35890 bytes .../figures/kubrvirt-simplified-diagram.png | Bin 0 -> 31615 bytes .../host-device-assignment-a6feb2f0c3bc.rst | 85 +++++ .../kube-virt/index-kubevirt-f1bfd2a21152.rst | 17 +- .../kube-virt/installation-66477d7646db.rst | 178 ++++++--- .../interface-and-networks-7cadb7bdb80b.rst | 321 ++++++++++++++++ .../kube-virt/introduction-bb3a04279bf5.rst | 7 +- .../kubevirt-architecture-c92c8908e775.rst | 124 +++++++ ....rst => kubevirt-removal-97cc897941bc.rst} | 8 +- ...migration-support-for-vms-bea635bacc50.rst | 156 ++++++++ ...ffinity-and-antiminusaffi-06ad222ceb13.rst | 183 ++++++++++ ...ersistent-storage-for-vms-8ddc8fa611aa.rst | 89 +++++ .../startup-scripts-6402154a6f37.rst | 93 +++++ ...a-cloudinit-configuration-d053375e78fa.rst | 121 +++++++ .../use-shared-resource-cpu-ad295227aa0c.rst | 339 +++++++++++++++++ .../virtual-machine-dd561f6db3fd.rst | 342 ++++++++++++++++++ ...machineinstancereplicaset-8518d55de52b.rst | 102 ++++++ .../vm-snapshot-and-restore-21158b60cd56.rst | 129 +++++++ ...-as-startup-configuration-4a8255e26b1f.rst | 95 +++++ ...ice-account-as-filesystem-5fd4deb7339a.rst | 72 ++++ doc/source/shared/abbrevs.txt | 7 + doc/source/spelling_wordlist.txt | 1 + 24 files changed, 2520 insertions(+), 53 deletions(-) create mode 100644 doc/source/kube-virt/configuration-as-filesystem-0a5023c8386e.rst create mode 100644 doc/source/kube-virt/figures/kubevirt-architecture.png create mode 100644 doc/source/kube-virt/figures/kubrvirt-simplified-diagram.png create mode 100644 doc/source/kube-virt/host-device-assignment-a6feb2f0c3bc.rst create mode 100644 doc/source/kube-virt/interface-and-networks-7cadb7bdb80b.rst create mode 100644 doc/source/kube-virt/kubevirt-architecture-c92c8908e775.rst rename doc/source/kube-virt/{removal-97cc897941bc.rst => kubevirt-removal-97cc897941bc.rst} (90%) create mode 100644 doc/source/kube-virt/live-migration-support-for-vms-bea635bacc50.rst create mode 100644 doc/source/kube-virt/node-assignment-using-nodeselector-affinity-and-antiminusaffi-06ad222ceb13.rst create mode 100644 doc/source/kube-virt/persistent-storage-for-vms-8ddc8fa611aa.rst create mode 100644 doc/source/kube-virt/startup-scripts-6402154a6f37.rst create mode 100644 doc/source/kube-virt/static-ip-assignment-via-cloudinit-configuration-d053375e78fa.rst create mode 100644 doc/source/kube-virt/use-shared-resource-cpu-ad295227aa0c.rst create mode 100644 doc/source/kube-virt/virtual-machine-dd561f6db3fd.rst create mode 100644 doc/source/kube-virt/virtualmachineinstancereplicaset-8518d55de52b.rst create mode 100644 doc/source/kube-virt/vm-snapshot-and-restore-21158b60cd56.rst create mode 100644 doc/source/kube-virt/vm-using-secret-as-startup-configuration-4a8255e26b1f.rst create mode 100644 doc/source/kube-virt/vm-using-service-account-as-filesystem-5fd4deb7339a.rst diff --git a/doc/source/_vendor/rl-strings.txt b/doc/source/_vendor/rl-strings.txt index 86cb81870..bbb806e53 100644 --- a/doc/source/_vendor/rl-strings.txt +++ b/doc/source/_vendor/rl-strings.txt @@ -445,7 +445,7 @@ .. |applying-a-custom-branding-tarball-to-running-systems| replace:: :ref:`Apply a Custom Horizon Branding Tarball to Running Systems ` .. |release-notes| replace:: :ref:`R10.0 Release Notes ` .. |create-an-ubuntu-vm-fafb82ec424b| replace:: :ref:`Create an Ubuntu VM ` -.. |removal-97cc897941bc| replace:: :ref:`Removal ` +.. |kubevirt-removal-97cc897941bc| replace:: :ref:`Removal ` .. |hello-world-kubevirt-vm-05503659173c| replace:: :ref:`Hello World KubeVirt VM ` .. |index-kubevirt-f1bfd2a21152| replace:: :ref:`KubeVirt ` .. |set-up-cdi-proxy-ad165d884417| replace:: :ref:`Set up CDI Proxy ` diff --git a/doc/source/kube-virt/configuration-as-filesystem-0a5023c8386e.rst b/doc/source/kube-virt/configuration-as-filesystem-0a5023c8386e.rst new file mode 100644 index 000000000..462899954 --- /dev/null +++ b/doc/source/kube-virt/configuration-as-filesystem-0a5023c8386e.rst @@ -0,0 +1,102 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _configuration-as-filesystem-0a5023c8386e: + +=========================== +Configuration as Filesystem +=========================== + +By using filesystem, ``configMaps`` are shared through ``virtiofs``. In +contrast with using disks for sharing ``configMaps``, filesystem allows you to +dynamically propagate changes on ``configMaps`` to |VMIs| (i.e. the VM does not +need to be rebooted). + +.. rubric:: Limitation + +Currently, |VMIs| cannot be live migrated since ``virtiofs`` does not support +live migration. + +Example of ``configMap``: + +.. code-block:: none + + apiVersion: v1 + kind: ConfigMap + metadata: + name: app-config + data: + DATABASE: staging + USERNAME: admin + PASSWORD: secret + +Example of VM using ``configMap`` as a filesystem: + +.. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: VirtualMachineInstance + metadata: + labels: + special: vmi-fedora + name: vmi-fedora + spec: + domain: + devices: + filesystems: + - name: config-fs + virtiofs: {} + disks: + - disk: + bus: virtio + name: containerdisk + machine: + type: "" + resources: + requests: + memory: 1024M + terminationGracePeriodSeconds: 0 + volumes: + - name: containerdisk + containerDisk: + image: quay.io/containerdisks/fedora:latest + - cloudInitNoCloud: + userData: |- + #cloud-config + chpasswd: + expire: false + password: fedora + user: fedora + bootcmd: + # mount the ConfigMap + - "sudo mkdir /mnt/app-config" + - "sudo mount -t virtiofs config-fs /mnt/app-config" + name: cloudinitdisk + - configMap: + name: app-config + name: config-fs + +To login and verify the VM run: + +.. code-block:: none + + [fedora@vmi-fedora ~]$ df -h + Filesystem Size Used Avail Use% Mounted on + /dev/vda5 4.0G 461M 3.1G 13% / + devtmpfs 4.0M 0 4.0M 0% /dev + tmpfs 450M 0 450M 0% /dev/shm + tmpfs 180M 720K 179M 1% /run + tmpfs 450M 0 450M 0% /tmp + /dev/vda2 966M 61M 840M 7% /boot + /dev/vda3 100M 12M 89M 12% /boot/efi + /dev/vda5 4.0G 461M 3.1G 13% /home + config-fs 9.8G 910M 8.4G 10% /mnt/app-config + tmpfs 90M 4.0K 90M 1% /run/user/1000 + [fedora@vmi-fedora ~]$ ls -lrth /mnt/app-config + total 0 + lrwxrwxrwx. 1 root 107 15 Jan 15 16:20 USERNAME -> ..data/USERNAME + lrwxrwxrwx. 1 root 107 15 Jan 15 16:20 PASSWORD -> ..data/PASSWORD + lrwxrwxrwx. 1 root 107 15 Jan 15 16:20 DATABASE -> ..data/DATABASE + + + diff --git a/doc/source/kube-virt/figures/kubevirt-architecture.png b/doc/source/kube-virt/figures/kubevirt-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..04414970c5eae32f859b8bc5e433337caf6e6e23 GIT binary patch literal 35890 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 Ry-45}URhJ=wW4Xz{{emJ@>l=> literal 0 HcmV?d00001 diff --git a/doc/source/kube-virt/figures/kubrvirt-simplified-diagram.png b/doc/source/kube-virt/figures/kubrvirt-simplified-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..d9ccd32a719322fb581b5248c4000f446d814002 GIT binary patch literal 31615 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 Wy5L@`0F!3_^Y)GE>oS@50sjj^+GMo= literal 0 HcmV?d00001 diff --git a/doc/source/kube-virt/host-device-assignment-a6feb2f0c3bc.rst b/doc/source/kube-virt/host-device-assignment-a6feb2f0c3bc.rst new file mode 100644 index 000000000..af1feb9e4 --- /dev/null +++ b/doc/source/kube-virt/host-device-assignment-a6feb2f0c3bc.rst @@ -0,0 +1,85 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _host-device-assignment-a6feb2f0c3bc: + +====================== +Host Device Assignment +====================== + +KubeVirt provides a mechanism for assigning host devices to a virtual machine. +This mechanism is generic and allows various types of |PCI| devices, such as +accelerators (including GPUs) or any other devices attached to a |PCI| bus, +to be assigned. It also allows Linux Mediated devices, such as pre-configured +virtual GPUs to be assigned using the same mechanism. + + +Preparation of Mediated Devices such as vGPU +-------------------------------------------- + +In general, configuration of Mediated devices (mdevs), such as |vGPUs|, should +be done according to vendor directions. KubeVirt can now facilitate the +creation of the Mediated devices/|vGPUs| on the cluster nodes. This assumes +that the required vendor driver is already installed on the nodes. + + + +List Permitted Devices +---------------------- + +Administrators can control which host devices are exposed and permitted to be +used in the cluster. Permitted host devices in the cluster will need to be +listed in KubeVirt Custom Resource (CR) by its vendor:product selector for +|PCI| devices or Mediated device names. See the example yaml segment below, +that would be added to the KubeVirt CR with ``kubectl edit kubevirt -n +kubevirt``. + +.. code-block:: none + + configuration: + permittedHostDevices: + pciHostDevices: + - pciVendorSelector: "10DE:1EB8" + resourceName: "nvidia.com/TU104GL_Tesla_T4" + externalResourceProvider: true + - pciVendorSelector: "8086:6F54" + resourceName: "intel.com/qat" + mediatedDevices: + - mdevNameSelector: "GRID T4-1Q" + resourceName: "nvidia.com/GRID_T4-1Q" + + +.. note:: + + ``pciVendorSelector`` is a |PCI| vendor ID and product ID tuple in the form + ``vendor_id:product_id``. This tuple can identify specific types of devices + on a host. For example, the identifier ``10de:1eb8``, shown above, can be + found using ``lspci``. + + .. code-block:: none + + $ lspci -nnv|grep -i nvidia + 65:00.0 3D controller [0302]: NVIDIA Corporation TU104GL [Tesla T4] [10de:1eb8] (rev a1) + + +Start VM Using vGPU +------------------- + +Host devices can be assigned to VMs via the ``gpus`` and ``hostDevices`` +fields. The ``deviceNames`` can reference both |PCI| and Mediated device +resource names. + +.. code-block:: none + + kind: VirtualMachineInstance + spec: + domain: + devices: + gpus: + - deviceName: nvidia.com/TU104GL_Tesla_T4 + name: gpu1 + - deviceName: nvidia.com/GRID_T4-1Q + name: gpu2 + hostDevices: + - deviceName: intel.com/qat + NAME: QUICKACCESS1 diff --git a/doc/source/kube-virt/index-kubevirt-f1bfd2a21152.rst b/doc/source/kube-virt/index-kubevirt-f1bfd2a21152.rst index 2649e453c..cbf16c5fa 100644 --- a/doc/source/kube-virt/index-kubevirt-f1bfd2a21152.rst +++ b/doc/source/kube-virt/index-kubevirt-f1bfd2a21152.rst @@ -10,8 +10,9 @@ KubeVirt :maxdepth: 1 introduction-bb3a04279bf5 + kubevirt-architecture-c92c8908e775 installation-66477d7646db - removal-97cc897941bc + kubevirt-removal-97cc897941bc Usage Examples ============== @@ -21,6 +22,20 @@ Usage Examples hello-world-kubevirt-vm-05503659173c set-up-cdi-proxy-ad165d884417 + virtual-machine-dd561f6db3fd + use-shared-resource-cpu-ad295227aa0c + live-migration-support-for-vms-bea635bacc50 + interface-and-networks-7cadb7bdb80b + startup-scripts-6402154a6f37 + persistent-storage-for-vms-8ddc8fa611aa + host-device-assignment-a6feb2f0c3bc + vm-snapshot-and-restore-21158b60cd56 + virtualmachineinstancereplicaset-8518d55de52b + static-ip-assignment-via-cloudinit-configuration-d053375e78fa + configuration-as-filesystem-0a5023c8386e + vm-using-secret-as-startup-configuration-4a8255e26b1f + vm-using-service-account-as-filesystem-5fd4deb7339a + node-assignment-using-nodeselector-affinity-and-antiminusaffi-06ad222ceb13 create-an-ubuntu-vm-fafb82ec424b set-up-remote-management-of-vms-a082461d660e create-a-windows-vm-82957181df02 diff --git a/doc/source/kube-virt/installation-66477d7646db.rst b/doc/source/kube-virt/installation-66477d7646db.rst index 21a9859bd..7ac3400ac 100644 --- a/doc/source/kube-virt/installation-66477d7646db.rst +++ b/doc/source/kube-virt/installation-66477d7646db.rst @@ -1,69 +1,161 @@ .. _installation-66477d7646db: -============ -Installation -============ +===================== +KubeVirt Installation +===================== + +.. rubric:: |prereq| + +- |prod| is up and running. + +- System is Alarm free. + + .. code-block:: none + + $ fm alarm-list .. rubric:: |proc| Complete the following steps to install KubeVirt. -#. Upload the KubeVirt system application tarball and check the KubeVirt - application status: - - .. only:: starlingx - - .. code-block:: none - - ~(keystone_admin)$ system application-upload /usr/local/share/applications/helm/kubevirt-app-.tgz - - ~(keystone_admin)$ system application-list - - .. only:: partner - - .. include:: /_includes/installation-66477d7646db.rest - :start-after: kubevirt-app-version - :end-before: kubevirt-app-version - -#. Apply the KubeVirt system application and check the KubeVirt and |CDI| - status: +#. Upload and apply the KubeVirt package (Helm). .. code-block:: none - ~(keystone_admin)$ system application-apply kubevirt-app + # system application-upload /usr/local/share/applications/helm/kubevirt-app-1.0-17.tgz + # system application-apply kubevirt-app + # system application-show kubevirt-app + # system application-list - Wait for kubevirt-app status to complete. +#. Check all the objects are up and running. - .. code-block:: bash + .. code-block:: none - $ watch -n 5 system application-list + # kubectl get all -n cdi + # kubectl get all -n kubevirt - # Wait for all pods in kubevirt namespace to be Running - $ watch -n 5 kubectl get pods -n kubevirt + .. note:: - # Wait for all pods in cdi namespace to be Running - $ watch -n 5 kubectl get pods -n cdi + When installing the containerized KubeVirt application, the virtctl + client is installed on the host. You can test the client by running + virtctl in an SSH / Local Console shell of the controller. -#. Setup 'virtctl' client executable to be accessible from sysadmin's PATH +#. Optionally, Install noVNC in order to access a VM's graphical console thru a + browser. - .. code-block:: bash + .. note:: - # Create /home/sysadmin/bin directory, if it doesn't exist already - $ mkdir -p /home/sysadmin/bin + noVNC is used to access a VM's graphical console in a browser, where + virtctl provides access to the VM's serial console. - # Create symbolic link in /home/sysadmin/bin to virtctl client executable installed on host in step 2) - $ cd /home/sysadmin/bin - $ ln -s /var/opt/kubevirt/virtctl-v0.53.1-linux-amd64 virtctl + Create and apply ``vnc-install.yaml``. - # Logout and log back in to ensure that /home/sysadmin/bin gets added to your PATH variable. - $ exit + .. code-block:: yaml - login: sysadmin - password: + apiVersion: v1 + kind: ServiceAccount + metadata: + name: virtvnc + namespace: kubevirt + --- + kind: ClusterRoleBinding + apiVersion: rbac.authorization.k8s.io/v1 + metadata: + name: virtvnc + subjects: + - kind: ServiceAccount + name: virtvnc + namespace: kubevirt + roleRef: + kind: ClusterRole + name: virtvnc + apiGroup: rbac.authorization.k8s.io + --- + kind: ClusterRole + apiVersion: rbac.authorization.k8s.io/v1 + metadata: + name: virtvnc + rules: + - apiGroups: + - subresources.kubevirt.io + resources: + - virtualmachineinstances/console + - virtualmachineinstances/vnc + verbs: + - get + - apiGroups: + - kubevirt.io + resources: + - virtualmachines + - virtualmachineinstances + - virtualmachineinstancepresets + - virtualmachineinstancereplicasets + - virtualmachineinstancemigrations + verbs: + - get + - list + - watch + --- + apiVersion: v1 + kind: Service + metadata: + labels: + app: virtvnc + name: virtvnc + namespace: kubevirt + spec: + ports: + - port: 8001 + protocol: TCP + targetPort: 8001 + nodePort: 31002 + selector: + app: virtvnc + type: NodePort + --- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: virtvnc + namespace: kubevirt + spec: + replicas: 1 + selector: + matchLabels: + app: virtvnc + template: + metadata: + labels: + app: virtvnc + spec: + serviceAccountName: virtvnc + nodeSelector: + node-role.kubernetes.io/master: '' + tolerations: + - key: "node-role.kubernetes.io/master" + operator: "Equal" + value: "" + effect: "NoSchedule" + containers: + - name: virtvnc + image: quay.io/samblade/virtvnc:latest + livenessProbe: + httpGet: + port: 8001 + path: / + scheme: HTTP + failureThreshold: 30 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 - $ which virtctl - /home/sysadmin/bin/virtctl + .. code-block:: none + + kubectl apply -f vnc-install.yaml + + To access the VNC: http://:31002 .. rubric:: |result| diff --git a/doc/source/kube-virt/interface-and-networks-7cadb7bdb80b.rst b/doc/source/kube-virt/interface-and-networks-7cadb7bdb80b.rst new file mode 100644 index 000000000..e5361bacf --- /dev/null +++ b/doc/source/kube-virt/interface-and-networks-7cadb7bdb80b.rst @@ -0,0 +1,321 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _interface-and-networks-7cadb7bdb80b: + +====================== +Interface and Networks +====================== + +Connecting a |VM| to a network consists of two parts. First, networks are +specified in ``spec.networks``. Then, interfaces backed by the networks are +added to the VM by specifying them in ``spec.domain.devices.interfaces``. + +Each interface must have a corresponding network with the same name. + +An interface defines a virtual network interface of a virtual machine (also +called a frontend). A network specifies the backend of an interface and +declares which logical or physical device it is connected to (also called as +backend). + + +MacVTap +------- + +In MacVTap mode, virtual machines are directly exposed to the Kubernetes nodes +L2 network. This is achieved by 'extending' an existing network interface with +a virtual device that has its own MAC address. + +MacVTap interfaces are feature gated; to enable the feature, follow +instructions, in order to activate the MacVTap feature gate (case sensitive). + + +How to Activate a Feature Gate +------------------------------ + +.. code-block:: none + + cat << END > enable-feature-gate.yaml + --- + apiVersion: kubevirt.io/v1 + kind: KubeVirt + metadata: + name: kubevirt + namespace: kubevirt + spec: + configuration: + developerConfiguration: + featureGates: + - LiveMigration + - Macvtap + END + + kubectl apply -f enable-feature-gate.yaml + +.. note:: + + Make sure to add all existing feature gates in overrides file. + +Alternatively, the existing kubevirt custom resources can be altered: + +.. code-block:: none + + kubectl edit kubevirt kubevirt -n kubevirt + ... + spec: + configuration: + developerConfiguration: + featureGates: + - DataVolumes + - LiveMigration + - Macvtap + +.. note:: + + The names of the feature gates are case sensitive. + +Below is the usage example of MacVTap interface used by |VM|: + +Create network attachment for the MacVTap network: + +.. code-block:: none + + kind: NetworkAttachmentDefinition + apiVersion: k8s.cni.cncf.io/v1 + metadata: + name: macvtapnetwork + annotations: + k8s.v1.cni.cncf.io/resourceName: macvtap.network.kubevirt.io/dataplane1 + spec: + config: '{ + "cniVersion": "0.3.1", + "name": "macvtapnetwork", + "type": "macvtap", + "mtu": 1500 + }' + +.. note:: + + By running this yaml, the system will create 10 + ``macvtap.network.kubevirt.io/dataplane1`` network attachments or + interfaces. + +Now you can create the |VM| using MacVTap network, for example: + +.. code-block:: none + + apiVersion: kubevirt.io/v1alpha3 + kind: VirtualMachine + metadata: + labels: + special: vmi-host-network + name: vmi-host-network + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: fedora + spec: + domain: + cpu: + cores: 1 + devices: + disks: + - name: containerdisk + disk: + bus: virtio + - name: cloudinitdisk + disk: + bus: virtio + interfaces: + - name: hostnetwork + macvtap: {} + resources: + requests: + memory: 1024M + networks: + - name: hostnetwork + multus: + networkName: macvtapnetwork + volumes: + - name: containerdisk + containerDisk: + image: docker.io/kubevirt/fedora-cloud-container-disk-demo:devel + - name: cloudinitdisk + cloudInitNoCloud: + userData: |- + #!/bin/bash + echo "fedora" |passwd fedora --stdin + + +Multus +------ + +It is also possible to connect VMIs to secondary networks using Multus. This +assumes that multus is installed across your cluster and a corresponding +``NetworkAttachmentDefinition`` |CRD| was created. + +Example: + +.. note:: + + First create the respective network attachment, for example if you want to + use MacVTap or |SRIOV| for a secondary interface. + +.. code-block:: none + + apiVersion: kubevirt.io/v1alpha3 + kind: VirtualMachine + metadata: + labels: + special: vmi-host-network + name: vmi-host-network-2 + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: fedora + spec: + domain: + cpu: + cores: 2 + devices: + disks: + - name: containerdisk + disk: + bus: virtio + - name: cloudinitdisk + disk: + bus: virtio + interfaces: + - name: default + masquerade: {} + - name: hostnetwork + macvtap: {} + + resources: + requests: + memory: 1024M + networks: + - name: default + pod: {} + - name: hostnetwork + multus: + networkName: macvtapnetwork + volumes: + - name: containerdisk + containerDisk: + image: docker.io/kubevirt/fedora-cloud-container-disk-demo:devel + - name: cloudinitdisk + cloudInitNoCloud: + userData: |- + #!/bin/bash + echo "fedora" |passwd fedora --stdin + +In the example manifest above, the |VM| uses the first interface as default and +second interface is MacVTap which is mapped using multus. + +SRIOV +----- + +In |SRIOV| mode, virtual machines are directly exposed to an |SRIOV| |PCI| +device, usually allocated by Intel |SRIOV| device plugin. The device is passed +through into the guest operating system as a host device, using the |VFIO| +userspace interface, to maintain high networking performance. + +.. note:: + + In |prod| |SRIOV| device plugin is part of the default platform + functionality. + + +.. note:: + + KubeVirt relies on |VFIO| userspace driver to pass |PCI| devices into the + |VMI| guest. As a result, when configuring |SRIOV|, define a pool of VF + resources that uses driver: vfio. + +Example: + +.. note:: + + Make sure an |SRIOV| interface is configured on a ``DATANETWORK`` + (``sriovnet0`` for example below) on the |prod| host. For more details see + :ref:`provisioning-sr-iov-interfaces-using-the-cli`. + +#. Create the Network attachment. + + .. code-block:: none + + apiVersion: "k8s.cni.cncf.io/v1" + kind: NetworkAttachmentDefinition + metadata: + name: sriov-net1 + annotations: + k8s.v1.cni.cncf.io/resourceName: intel.com/pci_sriov_net_sriovnet0 + spec: + config: '{ + "type": "sriov", + "vlan": 5, + "cniVersion": "0.3.1", + "name": "sriov-net1" + }' + +#. Launch the VM. + + .. code-block:: none + + apiVersion: kubevirt.io/v1alpha3 + kind: VirtualMachine + metadata: + labels: + special: vmi-sriov-network + name: vmi-sriov-network + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: fedora + spec: + domain: + cpu: + cores: 1 + devices: + disks: + - name: containerdisk + disk: + bus: virtio + - name: cloudinitdisk + disk: + bus: virtio + interfaces: + - masquerade: {} + name: default + - name: sriov-net1 + sriov: {} + resources: + requests: + memory: 1024M + networks: + - name: default + pod: {} + - multus: + networkName: sriov-net1 + name: sriov-net1 + volumes: + - name: containerdisk + containerDisk: + image: docker.io/kubevirt/fedora-cloud-container-disk-demo:devel + - name: cloudinitdisk + cloudInitNoCloud: + userData: |- + #!/bin/bash + echo "fedora" |passwd fedora --stdin + + diff --git a/doc/source/kube-virt/introduction-bb3a04279bf5.rst b/doc/source/kube-virt/introduction-bb3a04279bf5.rst index f97bca064..9debe00b3 100644 --- a/doc/source/kube-virt/introduction-bb3a04279bf5.rst +++ b/doc/source/kube-virt/introduction-bb3a04279bf5.rst @@ -1,8 +1,8 @@ .. _introduction-bb3a04279bf5: -============ -Introduction -============ +===================== +KubeVirt Introduction +===================== The KubeVirt system application in StarlingX includes: KubeVirt, Containerized Data Importer (|CDI|), and the Virtctl client tool. @@ -26,4 +26,3 @@ migrating |VMs|, canceling live migrations and uploading |VM| disk images. See https://kubevirt.io/user-guide for more details. - diff --git a/doc/source/kube-virt/kubevirt-architecture-c92c8908e775.rst b/doc/source/kube-virt/kubevirt-architecture-c92c8908e775.rst new file mode 100644 index 000000000..7122da5ba --- /dev/null +++ b/doc/source/kube-virt/kubevirt-architecture-c92c8908e775.rst @@ -0,0 +1,124 @@ +.. _kubevirt-architecture-c92c8908e775: + +===================== +KubeVirt Architecture +===================== + +The following diagram describes the KubeVirt architecture: + +.. figure:: figures/kubevirt-architecture.png + :width: 800 + +.. rubric:: ``virt-api-server`` + +HTTP API server serves as the entry point for all virtualization related flows. + +The API server updates the virtualization related custom resource definition +(see below). + +As the main entry point to KubeVirt it is responsible for defaulting and +validation of the provided VMI |CRDs|. + +.. rubric:: VMI (CRD) + +|VMI| definitions are kept as custom resources inside the Kubernetes API +server. + +The |VMI| definition defines all properties of the |VM| itself, for example: + +- Machine type + +- CPU type + +- Amount of RAM and |vCPUs| + +- Number and type of |NICs| + + +.. rubric:: ``virt-controller`` + +The ``virt-controller`` has all the cluster wide virtualization functionality +and it is responsible for monitoring the |VMI| (|CRs|) and managing the +associated pods. Currently, the controller ensure the creation and management +of the life-cycle of the pods associated with the |VMI| objects. + +A |VMI| object will always be associated with a pod during its life-time. +However, migration of a |VMI| the pod instance might change over time. + + +.. rubric:: ``virt-launcher`` + +For every |VMI| object one pod is created. This pod's primary container runs +the ``virt-launcher`` KubeVirt component. + +Kubernetes or kubelet is not running the |VMIs|. Instead a daemon on every host +in the cluster launches a |VMI| process for every pod which is associated with +a |VMI| object whenever it is scheduled on a host. + +The main purpose of the ``virt-launcher`` pod is to provide ``cgroups`` and +``namespaces``, used to host the |VMI| process. + +``virt-handler`` signals ``virt-launcher`` to start a |VMI| by passing the +|VMI|'s |CRD| object to ``virt-launcher``. ``virt-launcher`` uses a local +``libvirtd`` instance within its container to start the |VMI|. The +``virt-launcher`` monitors the |VMI| process and terminates once the |VMI| has +exited. + +If the Kubernetes runtime attempts to shutdown the ``virt-launcher`` pod before +|VMI| has exited, ``virt-launcher`` forwards signals from Kubernetes to the +|VMI| process and attempts to hold off the termination of the pod until the +|VMI| has shutdown successfully. + + +.. rubric:: ``virt-handler`` + +Every host needs a single instance of ``virt-handler``. It can be delivered as +a DaemonSet. + +Like the virt-controller, the ``virt-handler`` is also reactive and watches +for changes of the |VMI| object, once detected it will perform all necessary +operations to change a |VMI| to meet the required state. + +This behavior is similar to the choreography between the Kubernetes |VMI| +server and the kubelet. + +The functions of the ``virt-handler`` are: + +- Keeps a cluster-level |VMI| specification in sync with a corresponding + ``libvirt`` domain. + +- Reports domain state and spec changes to the cluster. + +- Invokes node-centric plugins which can fulfill networking and storage + requirements defined in |VMI| specs. + +.. rubric:: ``Libvirtd`` + +An instance of ``libvirtd`` is present in every |VMI| pod. ``virt-launcher`` +uses ``libvirtd`` to manage the life-cycle of the |VMI| process. + +Additional components from |prod| Kubernetes are: + +- Storage + +- Networking + +- |RBAC| + +.. rubric:: Simplified Diagram + +.. figure:: figures/kubrvirt-simplified-diagram.png + :width: 800 + +--------------------------- +Containerized Data Importer +--------------------------- + +The |CDI| project provides facilities for enabling |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. \ No newline at end of file diff --git a/doc/source/kube-virt/removal-97cc897941bc.rst b/doc/source/kube-virt/kubevirt-removal-97cc897941bc.rst similarity index 90% rename from doc/source/kube-virt/removal-97cc897941bc.rst rename to doc/source/kube-virt/kubevirt-removal-97cc897941bc.rst index 9d217cc11..57b4cc721 100644 --- a/doc/source/kube-virt/removal-97cc897941bc.rst +++ b/doc/source/kube-virt/kubevirt-removal-97cc897941bc.rst @@ -1,8 +1,8 @@ -.. _removal-97cc897941bc: +.. _kubevirt-removal-97cc897941bc: -======= -Removal -======= +================ +KubeVirt Removal +================ .. rubric:: |proc| diff --git a/doc/source/kube-virt/live-migration-support-for-vms-bea635bacc50.rst b/doc/source/kube-virt/live-migration-support-for-vms-bea635bacc50.rst new file mode 100644 index 000000000..fa4585669 --- /dev/null +++ b/doc/source/kube-virt/live-migration-support-for-vms-bea635bacc50.rst @@ -0,0 +1,156 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _live-migration-support-for-vms-bea635bacc50: + +============================== +Live Migration Support for VMs +============================== + +Live migration is a process during which a running |VMI| moves to another +compute node while the guest workload continues to run and remain accessible. + + +Enable the Live Migration Support +--------------------------------- + +Live migration is enabled by default in recent versions of KubeVirt. Versions +prior to v0.56 must be enabled in the feature gates. The feature gates field in +the KubeVirt Custom Resource must be expanded by adding the live migration to +it. + +Limitation for live migrations: + +- |VMs| using a |PVC| must have a shared |RWX| access mode to be live + migrated. + +- Live migration is not allowed with a pod network binding of bridge + interface type (). + +- Live migration requires ports 49152, 49153 to be available in the + ``virt-launcher`` pod. If these ports are explicitly specified in + a masquarade interface, live migration will not function. + + +Initiate Live Migration +----------------------- + +Live migration is initiated by posting a |VMIM| object to the cluster. The +example below starts a migration process for a virtual machine instance +``vmi-fedora``. + +.. code-block:: yaml + + cat < migration.yaml + apiVersion: kubevirt.io/v1 + kind: VirtualMachineInstanceMigration + metadata: + name: migration-job + spec: + vmiName: vmi-fedora + EOF + + kubectl apply -f migration.yaml + +Use virtctl to initiate Live Migration +-------------------------------------- + +Live migration can also be initiated using ``virtctl``. + +.. code-block:: none + + virtctl migrate vmi-fedora + + +Live Migration for SRIOV based VMs +---------------------------------- + +It is possible to live migrate |SRIOV| based |VMs|, but there are some +limitations: + +- Specify the MAC address statically and on host the name of the |SRIOV| + interface should be same on source and target host. + +- Specify the Static IP address. + +Below is an example manifest for |SRIOV| |VM| using a static MAC address: + +.. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: VirtualMachine + metadata: + labels: + special: vmi-sriov-network + name: vmi-sriov-test-vm + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: fedora + spec: + domain: + cpu: + cores: 4 + devices: + disks: + - name: containerdisk + disk: + bus: virtio + - name: cloudinitdisk + disk: + bus: virtio + interfaces: + - macAddress: "02:00:00:00:00:01" + name: sriov-net1 + sriov: {} + rng: {} + resources: + requests: + memory: 1024M + networks: + - multus: + networkName: sriov-net1 + name: sriov-net1 + volumes: + - containerDisk: + image: docker.io/kubevirt/fedora-cloud-container-disk-demo:devel + name: containerdisk + - cloudInitNoCloud: + networkData: | + ethernets: + sriov-net1: + addresses: + - 10.10.10.12/24 + gateway: 10.10.10.1 + match: + macAddress: "02:00:00:00:00:01" + nameservers: + addresses: + - 10.96.0.10 + search: + - default.svc.cluster.local + - svc.cluster.local + - cluster.local + set-name: sriov-link-enabled + version: 2 + userData: |- + #!/bin/bash + echo "fedora" |passwd fedora --stdin + name: cloudinitdisk + + +Below is an example manifest to initiate the live migration: + +.. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: VirtualMachineInstanceMigration + metadata: + name: migration-job-1 + spec: + vmiName: vmi-sriov-test-vm + + diff --git a/doc/source/kube-virt/node-assignment-using-nodeselector-affinity-and-antiminusaffi-06ad222ceb13.rst b/doc/source/kube-virt/node-assignment-using-nodeselector-affinity-and-antiminusaffi-06ad222ceb13.rst new file mode 100644 index 000000000..0de6ebf24 --- /dev/null +++ b/doc/source/kube-virt/node-assignment-using-nodeselector-affinity-and-antiminusaffi-06ad222ceb13.rst @@ -0,0 +1,183 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _node-assignment-using-nodeselector-affinity-and-antiminusaffi-06ad222ceb13: + +======================================================================================= +Node Assignment Using NodeSelector, Affinity and Anti-Affinity, Taints, and Tolerations +======================================================================================= + +Setting ``spec.nodeSelector`` requirements, constrains the scheduler to only +schedule VMs on nodes, which contain the specified labels. In the following +example the |VMI| contains the label ``app: kload`` (in this case on +controller-1). + +Example of a VM manifest using ``nodeSelector``: + +.. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: VirtualMachineInstance + metadata: + name: testvmi-nocloud + spec: + nodeSelector: + app: kload + terminationGracePeriodSeconds: 30 + domain: + resources: + requests: + memory: 1024M + devices: + disks: + - name: containerdisk + disk: + bus: virtio + - name: emptydisk + disk: + bus: virtio + - disk: + bus: virtio + name: cloudinitdisk + volumes: + - name: containerdisk + containerDisk: + image: kubevirt/fedora-cloud-container-disk-demo:latest + - name: emptydisk + emptyDisk: + capacity: "2Gi" + - name: cloudinitdisk + cloudInitNoCloud: + userData: |- + #cloud-config + password: fedora + chpasswd: { expire: False } + + +The ``spec.affinity`` field allows specifying hard- and soft-affinity for +|VMs|. It is possible to write matching rules against workloads (|VMs| and +pods) and Nodes. Since |VMs| are a workload type based on pods, Pod-affinity +affects |VMs| as well. + +Pod affinity allows you to specify which POD/VM should be scheduled together +(on the same node or in the same topology, like a zone). + +Where ``requiredDuringSchedulingIgnoredDuringExecution`` means that the +scheduler must place the pod/VM on a node that matches the affinity rules +during scheduling, but once the pod is running, the rule is ignored if other +changes happen (e.g., pods with the same label are removed). + +The rule here says that the pod must be scheduled on a node that has other +POD/VM with the following characteristics: Label Selector: It looks for POD/VM +with the label security and the value ``S1``. This is defined in the +``matchExpressions`` part of the configuration. + +``TopologyKey: failure-domain.beta.kubernetes.io/zone`` means that the pods +with the security: ``S1`` label should be in the same zone as the POD/VM being +scheduled. Essentially, the pod should be scheduled in the same failure domain +(zone) as other pods with the security: ``S1`` label. + +Pod anti-affinity specifies that certain pods should not be scheduled together +(on the same node or topology). + +Where ``preferredDuringSchedulingIgnoredDuringExecution`` is a "soft" rule +where the scheduler prefers to place the pod according to the anti-affinity +rule, but it is not required. If the rule cannot be met, the POD/VM will still +be scheduled, but it will try to follow the rule when possible. + +The rule here says that the scheduler prefers to place the POD/VM on a node +that avoids other pods with the following characteristics: Label Selector: +POD/VM with the label security and value ``S2``. + +``TopologyKey: kubernetes.io/hostname`` means that the anti-affinity applies to +the hostname (i.e., the pod should prefer not to be placed on the same node as +POD/VM with the security: ``S2`` label). + +``Weight``: The weight of 100 indicates the strength of the preference. A +higher weight means the scheduler will try harder to respect the rule, but it +is still not guaranteed. + +Example of a |VM| manifest using affinity and anti-affinity: + +.. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: VirtualMachineInstance + spec: + nodeSelector: + cpu: slow + storage: fast + domain: + resources: + requests: + memory: 64M + devices: + disks: + - name: mypvcdisk + lun: {} + affinity: + podAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: security + operator: In + values: + - S1 + topologyKey: failure-domain.beta.kubernetes.io/zone + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: security + operator: In + values: + - S2 + topologyKey: kubernetes.io/hostname + volumes: + - name: mypvcdisk + persistentVolumeClaim: + claimName: mypvc + +Affinity as described above, is a property of |VMs| that attracts them to a set +of nodes (either as a preference or a hard requirement). Taints are the +opposite - they allow a node to repel a set of |VMs|. + +Taints and tolerations work together to ensure that |VMs| are not scheduled +onto inappropriate nodes. One or more taints are applied to a node; this +ensures that the node should not accept any |VMs| that do not tolerate the +taints. Tolerations are applied to |VMs|, and allow (but do not require) the +|VMs| to schedule onto nodes with matching taints. + +Example of |VM| manifest using taint and tolerance. + +You add a taint to a node using ``kubectl taint``. For example, ``kubectl taint +nodes node1 key=value:NoSchedule``. + +Below is an example of adding a toleration of this taint to a VM: + +.. code-block:: none + + metadata: + name: testvmi-ephemeral + apiVersion: kubevirt.io/v1 + kind: VirtualMachineInstance + spec: + nodeSelector: + cpu: slow + storage: fast + domain: + resources: + requests: + memory: 64M + devices: + disks: + - name: mypvcdisk + lun: {} + tolerations: + - key: "key" + operator: "Equal" + value: "value" + effect: "NoSchedule" diff --git a/doc/source/kube-virt/persistent-storage-for-vms-8ddc8fa611aa.rst b/doc/source/kube-virt/persistent-storage-for-vms-8ddc8fa611aa.rst new file mode 100644 index 000000000..13fa8603d --- /dev/null +++ b/doc/source/kube-virt/persistent-storage-for-vms-8ddc8fa611aa.rst @@ -0,0 +1,89 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _persistent-storage-for-vms-8ddc8fa611aa: + +========================== +Persistent Storage for VMs +========================== + +Allows connecting a ``PersistentVolumeClaim`` to a |VM| disk. + +Use a ``PersistentVolumeClaim`` when the ``VirtualMachineInstance`` disk needs +to persist after the |VM| terminates. This allows for the |VM|'s data to remain +persistent between restarts. + +A ``PersistentVolume`` can be in ``filesystem`` or ``block`` mode. + +Below is a simple example which attaches a ``PersistentVolumeClaim`` as a disk +to a VM: + +.. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: VirtualMachineInstance + metadata: + name: testvmi-pvc + spec: + domain: + resources: + requests: + memory: 64M + devices: + disks: + - name: mypvcdisk + lun: {} + volumes: + - name: mypvcdisk + persistentVolumeClaim: + claimName: mypvc + + +Upload VM Image to CDI +---------------------- + +You can upload/import a VM image to |CDI|, creating a |PVC| on system, so you +can use the |PVC| to boot or create the |VM|. + +#. Get the upload proxy Server IP. + + .. code-block:: none + + kubectl -n cdi get svc |grep cdi-uploadproxy + cdi-uploadproxy ClusterIP 10.111.132.43 443/TCP 85m + +#. Use ``virtctl`` client upload the image to |CDI|. + + .. code-block:: none + + virtctl image-upload --pvc-name=cirros-vm-disk-test-2 --pvc-size=500Mi --image-path=/home/sysadmin/kubevirt-valid/cirros-0.5.1-x86_64-disk.img --uploadproxy-url=https:// --insecure + +.. note:: + + Now you can use ``pvc cirros-vm-disk-test-2`` in |VM| specs. + + .. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: VirtualMachineInstance + metadata: + name: testvmi-pvc-1 + spec: + domain: + resources: + requests: + memory: 64M + devices: + disks: + - name: mypvcdisk + lun: {} + volumes: + - name: mypvcdisk + persistentVolumeClaim: + claimName: cirros-vm-disk-test-2 + + If you want to upload an image located outside the cluster, the cluster + admin should expose the |CDI| upload proxy to the outside, for example with + NodePort. + + For more details see :ref:`set-up-cdi-proxy-ad165d884417`. \ No newline at end of file diff --git a/doc/source/kube-virt/startup-scripts-6402154a6f37.rst b/doc/source/kube-virt/startup-scripts-6402154a6f37.rst new file mode 100644 index 000000000..afe3d0ada --- /dev/null +++ b/doc/source/kube-virt/startup-scripts-6402154a6f37.rst @@ -0,0 +1,93 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _startup-scripts-6402154a6f37: + +=============== +Startup Scripts +=============== + +KubeVirt supports the ability to assign a startup script to a |VMI| instance +which is executed automatically when the |VM| initializes. + +These scripts are commonly used to automate injection of users and |SSH| keys +into |VMs| in order to provide remote access to the machine. For example, a +startup script can be used to inject credentials into a |VM| that allows an +Ansible job running on a remote host to access and provision the |VM|. + +Startup scripts are not limited to any specific use case though. They can be +used to run any arbitrary script in a |VM| on boot. + +Example of use: + +Assign static IP using ``cloudInitNoCloud``. + +.. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: VirtualMachine + metadata: + labels: + special: vmi-sriov-network + name: vmi-sriov-network + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: fedora + spec: + domain: + cpu: + cores: 4 + devices: + disks: + - name: containerdisk + disk: + bus: virtio + - name: cloudinitdisk + disk: + bus: virtio + interfaces: + - masquerade: {} + name: default + - macAddress: "02:00:00:00:00:01" + name: sriov-net1 + sriov: {} + rng: {} + resources: + requests: + memory: 1024M + networks: + - name: default + pod: {} + - multus: + networkName: sriov-net1 + name: sriov-net1 + volumes: + - containerDisk: + image: docker.io/kubevirt/fedora-cloud-container-disk-demo:devel + name: containerdisk + - cloudInitNoCloud: + networkData: | + ethernets: + sriov-net1: + addresses: + - 10.10.10.14/24 + gateway: 10.10.10.1 + match: + macAddress: "02:00:00:00:00:01" + nameservers: + addresses: + - 10.96.0.10 + search: + - default.svc.cluster.local + - svc.cluster.local + set-name: sriov-link-enabled + version: 2 + # userData: '#!/bin/bash echo "fedora" | passwd fedora --stdin' + userData: |- + #!/bin/bash + echo "fedora" |passwd fedora --stdin + name: cloudinitdisk diff --git a/doc/source/kube-virt/static-ip-assignment-via-cloudinit-configuration-d053375e78fa.rst b/doc/source/kube-virt/static-ip-assignment-via-cloudinit-configuration-d053375e78fa.rst new file mode 100644 index 000000000..3a56d181c --- /dev/null +++ b/doc/source/kube-virt/static-ip-assignment-via-cloudinit-configuration-d053375e78fa.rst @@ -0,0 +1,121 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _static-ip-assignment-via-cloudinit-configuration-d053375e78fa: + +================================================ +Static IP Assignment via Cloudinit Configuration +================================================ + +KubeVirt supports cloud-init's NoCloud and ConfigDrive datasources, which +involve injecting startup scripts into a |VM| instance using of an ephemeral +disk. |VMs| with the cloud-init package installed detect the ephemeral disk and +execute custom userdata scripts at boot. + +In the example below, the |VM| is using NoCloud for assigning the static IP and +MAC address on |SRIOV| interface. + +.. code-block:: none + + cat <cloud-NoCloud-test.yaml + apiVersion: kubevirt.io/v1 + kind: VirtualMachine + metadata: + labels: + special: vmi-sriov-network + name: vmi-sriov-test-vm-1 + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: fedora + spec: + domain: + cpu: + cores: 4 + devices: + disks: + - name: containerdisk + disk: + bus: virtio + - name: cloudinitdisk + disk: + bus: virtio + interfaces: + - masquerade: {} + name: default + - macAddress: "02:00:00:00:00:03" + name: sriov-net1 + sriov: {} + rng: {} + resources: + requests: + memory: 1024M + networks: + - name: default + pod: {} + - multus: + networkName: sriov-net1 + name: sriov-net1 + volumes: + - containerDisk: + image: docker.io/kubevirt/fedora-cloud-container-disk-demo:devel + name: containerdisk + - cloudInitNoCloud: + networkData: | + ethernets: + sriov-net1: + addresses: + - 10.10.10.42/24 + gateway: 10.10.10.1 + match: + macAddress: "02:00:00:00:00:03" + nameservers: + addresses: + - 10.96.0.10 + search: + - default.svc.cluster.local + - svc.cluster.local + - cluster.local + set-name: sriov-link-enabled + version: 2 + userData: |- + #!/bin/bash + echo "fedora" |passwd fedora --stdin + name: cloudinitdisk + EOF + + kubectl apply -f cloud-NoCloud-test.yaml + + + [sysadmin@controller-0 kubevirt-GA-testing(keystone_admin)]$ kubectl get vmi + NAME AGE PHASE IP NODENAME READY + vmi-sriov-test-vm-1 3h17m Running 172.16.166.152 controller-1 True + + [sysadmin@controller-0 kubevirt-GA-testing(keystone_admin)]$ virtctl console vmi-sriov-test-vm-1 + Successfully connected to vmi-sriov-test-vm-1 console. The escape sequence is ^]vmi-sriov-test-vm-1 login: fedora + Password: + [fedora@vmi-sriov-test-vm-1 ~]$ ip a + 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever + 2: eth0: mtu 1500 qdisc fq_codel state UP group default qlen 1000 + link/ether 52:54:00:90:b4:d7 brd ff:ff:ff:ff:ff:ff + altname enp1s0 + inet 10.0.2.2/24 brd 10.0.2.255 scope global dynamic noprefixroute eth0 + valid_lft 86301690sec preferred_lft 86301690sec + inet6 fe80::5054:ff:fe90:b4d7/64 scope link + valid_lft forever preferred_lft forever + 3: eth1: mtu 1500 qdisc mq state UP group default qlen 1000 + link/ether 02:00:00:00:00:03 brd ff:ff:ff:ff:ff:ff + altname enp8s0 + inet 10.10.10.42/24 brd 10.10.10.10.255 scope global noprefixroute eth1 + valid_lft forever preferred_lft forever + inet6 fe80::ff:fe00:3/64 scope link + valid_lft forever preferred_lft forever + [fedora@vmi-sriov-test-vm-1 ~]$ diff --git a/doc/source/kube-virt/use-shared-resource-cpu-ad295227aa0c.rst b/doc/source/kube-virt/use-shared-resource-cpu-ad295227aa0c.rst new file mode 100644 index 000000000..623d43448 --- /dev/null +++ b/doc/source/kube-virt/use-shared-resource-cpu-ad295227aa0c.rst @@ -0,0 +1,339 @@ +.. _use-shared-resource-cpu-ad295227aa0c: + +========================= +Use Shared Resource (CPU) +========================= + +In |prod| the shared CPUs are allocated under Application pool. All unused CPUs +are by default allocated under Application CPU pool. + +Use the manifest example below for the shared CPU: + +.. code-block:: yaml + :emphasize-lines: 15,16 + + cat < sample-vm-diff-huge-pages-size.yaml + apiVersion: kubevirt.io/v1 + kind: VirtualMachine + metadata: + name: ubuntu-bionic-1g + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: ubuntu-bionic + spec: + domain: + cpu: + cores: 1 + devices: + disks: + - name: containervolume + disk: + bus: virtio + - name: cloudinitvolume + disk: + bus: virtio + interfaces: + - name: default + masquerade: {} + resources: + requests: + memory: "10Gi" + memory: + hugepages: + pageSize: "1Gi" + networks: + - name: default + pod: {} + volumes: + - name: containervolume + containerDisk: + image: tedezed/ubuntu-container-disk:20.0 + - name: cloudinitvolume + cloudInitNoCloud: + userData: |- + #cloud-config + chpasswd: + list: | + ubuntu:ubuntu + root:root + expire: False + EOF + +.. note:: + + In VM manifest, the CPUs, which are not defined as dedicated, will be + allocated from the Application (Shared) Pool. + +-------------------------- +Assign Dedicated Resources +-------------------------- + +Dedicated CPU Resources +----------------------- + +Certain workloads requiring predictable latency and enhanced performance during +execution would benefit from obtaining dedicated CPU resources. KubeVirt, +relying on the Kubernetes CPU manager, is able to pin guest's vCPUs to the +host's pCPUs. + +.. note:: + + Enable CPU Manager on |prod| host before using dedicated CPU resources. + + Kubernetes does not provide CPU Manager detection, so you need to add + CpuManager feature gate manually to KubeVirt CR: + + .. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: KubeVirt + metadata: + name: kubevirt + namespace: kubevirt + spec: + configuration: + developerConfiguration: + featureGates: + - LiveMigration + - Macvtap + - Snapshot + - CPUManager + + Then, check the label: + + .. code-block:: none + + ~(keystone_admin)]$ kubectl describe node | grep cpumanager cpumanager=true + + +Request Dedicated CPU Resources from Application CPU Pool +--------------------------------------------------------- + +Setting ``spec.domain.cpu.dedicatedCpuPlacement`` to true in a VMI spec will +indicate the desire to allocate dedicated CPU resource to the VMI. + +Kubevirt will verify that all the necessary conditions are met, for the +Kubernetes CPU manager to pin the ``virt-launcher`` container to dedicated host +CPUs. Once, ``virt-launcher`` is running, the VMI's vCPUs will be pinned to the +pCPUS that has been dedicated for the ``virt-launcher`` container. + +Expressing the desired amount of VMI's vCPUs can be done by either setting the +guest topology in ``spec.domain.cpu`` (sockets, cores, threads) or +``spec.domain.resources.[requests/limits].cpu`` to a whole number integer +``([1-9]+)`` indicating the number of vCPUs requested for the VMI. Number of +vCPUs is counted as sockets * cores * threads or if ``spec.domain.cpu`` is +empty then it takes value from ``spec.domain.resources.requests.cpu`` or +``spec.domain.resources.limits.cpu``. + +Example of sample manifest file: + +.. code-block:: yaml + :emphasize-lines: 14,15,16,17,18 + + apiVersion: kubevirt.io/v1 + kind: VirtualMachine + metadata: + name: ubuntu-bionic-dedicated-cpu + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: ubuntu-bionic + spec: + domain: + cpu: + cores: 2 + sockets: 1 + threads: 1 + dedicatedCpuPlacement: true + devices: + disks: + - name: containervolume + disk: + bus: virtio + - name: cloudinitvolume + disk: + bus: virtio + interfaces: + - name: default + masquerade: {} + resources: + requests: + memory: 2048M + networks: + - name: default + pod: {} + volumes: + - name: containervolume + containerDisk: + image: tedezed/ubuntu-container-disk:20.0 + - name: cloudinitvolume + cloudInitNoCloud: + userData: |- + #cloud-config + chpasswd: + list: | + ubuntu:ubuntu + root:root + expire: False + +Isolated CPU Resources +---------------------- + +|prod| supports running the most critical low-latency applications on host CPUs +which are completely isolated from the host process scheduler. This allows you +to customize Kubernetes CPU management when the policy is set to static so that +low-latency applications run with optimal efficiency. + + +Request Isolated CPU Resources +------------------------------ + +.. note:: + + Make sure |prod| host is configured with isolated CPU cores. + + Refer to documentation for isolated CPU + :ref:`isolating-cpu-cores-to-enhance-application-performance`. + +By specifying the ``windriver.com/isolcpus: x`` in VM specs , allocates the +CPUs from application isolated core pools. + +Below is the example manifest requesting the isolated cores. + +.. code-block:: yaml + :emphasize-lines: 31,32,33,34,35,36 + + apiVersion: kubevirt.io/v1 + kind: VirtualMachine + metadata: + name: ubuntu-bionic-isol-cores + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: ubuntu-bionic + spec: + domain: + cpu: + sockets: 1 + cores: 4 + threads: 1 + dedicatedCpuPlacement: true + devices: + disks: + - name: containervolume + disk: + bus: virtio + - name: cloudinitvolume + disk: + bus: virtio + interfaces: + - name: default + masquerade: {} + resources: + requests: + memory: 4Gi + windriver.com/isolcpus: 4 + limits: + memory: 4Gi + windriver.com/isolcpus: 4 + networks: + - name: default + pod: {} + volumes: + - name: containervolume + containerDisk: + image: tedezed/ubuntu-container-disk:20.0 + - name: cloudinitvolume + cloudInitNoCloud: + userData: |- + #cloud-config + chpasswd: + list: | + ubuntu:ubuntu + root:root + expire: False + + +Huge Pages +---------- + +Huge pages, in the context of Kubevirt VM, refers to a Linux kernel feature +that allows for the efficient use of large pages, also known as large memory +pages. Huge pages can be leveraged to enhance the performance of +memory-intensive applications, especially those with large datasets. + +Request Huge Pages +------------------ + +.. note:: + + Make sure |prod| host is pre-configured with huge pages. + + For more details on huge pages configuration, refer to + :ref:`allocating-host-memory-using-the-cli`. + +You can request the memory in form of huge pages by specifying the +``spec.domain.memory.hugePages.pageSize``. + +Example: + +.. code-block:: yaml + + apiVersion: kubevirt.io/v1 + kind: VirtualMachine + metadata: + name: ubuntu-bionic-1g + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: ubuntu-bionic + spec: + domain: + cpu: + cores: 1 + devices: + disks: + - name: containervolume + disk: + bus: virtio + - name: cloudinitvolume + disk: + bus: virtio + interfaces: + - name: default + masquerade: {} + resources: + requests: + memory: "10Gi" + memory: + hugepages: + pageSize: "1Gi" + networks: + - name: default + pod: {} + volumes: + - name: containervolume + containerDisk: + image: tedezed/ubuntu-container-disk:20.0 + - name: cloudinitvolume + cloudInitNoCloud: + userData: |- + #cloud-config + chpasswd: + list: | + ubuntu:ubuntu + root:root + expire: False diff --git a/doc/source/kube-virt/virtual-machine-dd561f6db3fd.rst b/doc/source/kube-virt/virtual-machine-dd561f6db3fd.rst new file mode 100644 index 000000000..63da11b5c --- /dev/null +++ b/doc/source/kube-virt/virtual-machine-dd561f6db3fd.rst @@ -0,0 +1,342 @@ +.. _virtual-machine-dd561f6db3fd: + +=============== +Virtual Machine +=============== + +Every |VMI| object represents a single running virtual machine instance. + +While |VM| represents the abstraction of virtual machine instances. + +.. contents:: |minitoc| + :local: + :depth: 2 + +--------------------------------- +Create Virtual Machines Manifests +--------------------------------- + +The ``virtctl`` sub command ``create vm`` allows easy creation of +VirtualMachine manifests from the command line. It leverages instance types and +preferences and provides several flags to control details of the created |VM|. + +For example there are flags to specify the name or run strategy of a |VM| or +flags to add volumes to a |VM|. Instance types and preferences can either be +specified directly or it is possible to let KubeVirt infer those from the +volume used to boot the |VM|. + +For a full set of flags and their description use the following command: + +.. code-block:: none + + virtctl create vm -h + + +Create Virtual Machines on a Cluster +------------------------------------ + +The output of ``virtctl create vm`` can be piped into ``kubectl`` to directly +create a |VM| on a cluster. + +For example: + +.. code-block:: none + + # Create a VM with name my-vm on the cluster + virtctl create vm --name my-vm | kubectl create -f - + virtualmachine.kubevirt.io/my-vm created + +Specify Cloud-init User Data +---------------------------- + +To pass ``cloud-init`` user data to ``virtctl`` it needs to be encoded into a +``base64`` string. + +For example: + +.. code-block:: none + + # Put your cloud-init user data into a file. + # This will add an authorized key to the default user. + # To get the default username read the documentation for the cloud image + $ cat cloud-init.txt + #cloud-config + ssh_authorized_keys: + - ssh-rsa AAAA... + + # Base64 encode the contents of the file without line wraps and store it in a variable + $ CLOUD_INIT_USERDATA=$(base64 -w 0 cloud-init.txt) + + # Show the contents of the variable + $ echo $CLOUD_INIT_USERDATA I2Nsb3VkLWNvbmZpZwpzc2hfYXV0aG9yaXplZF9rZXlzOgogIC0gc3NoLXJzYSBBQUFBLi4uCg== + +You can now use this variable as an argument to the ``--cloud-init-user-data`` +flag: + +.. code-block:: none + + virtctl create vm --cloud-init-user-data $CLOUD_INIT_USERDATA + + +Create VM Directly from Manifest +-------------------------------- + +You can create the |VM| or virtual machine instances directly from manifest. + +.. code-block:: yaml + + apiVersion: kubevirt.io/v1 + + kind: VirtualMachine + metadata: + name: sample-test-vm + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: ubuntu-bionic + spec: + domain: + cpu: + cores: 1 + devices: + disks: + - name: containervolume + disk: + bus: virtio + - name: cloudinitvolume + disk: + bus: virtio + interfaces: + - name: default + masquerade: {} + resources: + requests: + memory: 1024M + networks: + - name: default + pod: {} + volumes: + - name: containervolume + containerDisk: + image: tedezed/ubuntu-container-disk:20.0 + - name: cloudinitvolume + cloudInitNoCloud: + userData: |- + #cloud-config + chpasswd: + list: | + ubuntu:ubuntu + root:root + expire: False + + +---------- +Access VMs +---------- + +Once a |VM| is started, you are able to connect to the consoles it exposes. +Usually there are two types of consoles: + +- Serial Console + + The serial console of a virtual machine can be accessed by using + the console command: + + .. code-block:: none + + virtctl console testvm + +- Graphical Console (VNC) + + If NoVnc is installed on Cluster and exposed over NodePort, you can access + virtual machines with ``http://:port``. + + .. note:: + + Refer the section :ref:`Install NoVNC ` for + more details. + + +--------- +Lifecycle +--------- + +Every |VMI| represents a single virtual machine instance. In general, the +management of |VMIs| is kept similar to how Pods are managed. Every |VM| that +is defined in the cluster is expected to be running, just like pods. Deleting a +|VMI| is equivalent to shutting it down, this is also equivalent to how pods +behave. + +Launch a Virtual Machine +------------------------ + +To start a |VMI|, you need to create a |VMI| object using ``kubectl``: + +.. code-block:: none + + kubectl create -f vmi.yaml + +Example of ``vmi.yaml``: + +.. code-block:: yaml + + apiVersion: kubevirt.io/v1 + kind: VirtualMachine + metadata: + name: sample-test-vm + spec: + running: true + template: + metadata: + labels: + kubevirt.io/size: small + kubevirt.io/domain: ubuntu-bionic + spec: + domain: + cpu: + cores: 1 + devices: + disks: + - name: containervolume + disk: + bus: virtio + - name: cloudinitvolume + disk: + bus: virtio + interfaces: + - name: default + masquerade: {} + resources: + requests: + memory: 1024M + networks: + - name: default + pod: {} + volumes: + - name: containervolume + containerDisk: + image: tedezed/ubuntu-container-disk:20.0 + - name: cloudinitvolume + cloudInitNoCloud: + userData: |- + #cloud-config + chpasswd: + list: | + ubuntu:ubuntu + root:root + expire: False + + +List the VM +----------- + +|VMIs| can be listed by querying for |VMI| objects: + +.. code-block:: none + + kubectl get vmis + + +Retrieve a VMI Definition +------------------------- + +A single |VMI| definition can be retrieved by getting the specific |VMI| +object. + +.. code-block:: none + + kubectl get vmis testvmi + + +Remove a VMI +------------ + +To stop the |VMI|, you need to delete the corresponding |VMI| object using +kubectl. + +.. code-block:: none + + $ kubectl delete -f vmi.yaml + +OR + +.. code-block:: none + + $ kubectl delete vmis testvmi + +.. note:: + + Stopping a |VMI| implies that it will be deleted from the cluster. You will + not be able to start this |VMI| object again. + + +Start and Stop a VM +------------------- + +|VMs|, in contrast to |VMIs|, have a running state. Thus, on a |VM| you can +define if it should be running, or not. |VMIs| are always running and consuming +resources, if they are defined in the cluster + +``virtctl`` is used to start a |VM|: + +.. code-block:: none + + $ virtctl start my-vm + +And to stop a |VM|: + +.. code-block:: none + + $ virtctl stop my-vm + +.. note:: + + If you run the force stop to the |VM|, it may result in data + inconsistencies, or there may be data loss. + + +Pause and Unpause a VM +---------------------- + +.. note:: + + Pausing in this context refers to ``libvirt``'s :command:`virDomainSuspend` + command. The process is frozen without further access to CPU resources and + I/O but the memory used by the domain at the hypervisor level will stay + allocated. + +To pause a |VM|, you need the ``virtctl`` command line tool. The pause command +works on either |VMs| or |VMIs|: + +.. code-block:: none + + $ virtctl pause vm testvm + +OR + +.. code-block:: none + + $ virtctl pause vmi testvm + +Paused |VMIs| have a Paused condition in their status: + +.. code-block:: none + + $ kubectl get vmi testvm -o=jsonpath='{.status.conditions[?(@.type=="Paused")].message}' + + VMI was paused by user + +Unpausing works similar to pausing: + +.. code-block:: none + + $ virtctl unpause vm testvm + +OR + +.. code-block:: none + + $ virtctl unpause vmi testvm diff --git a/doc/source/kube-virt/virtualmachineinstancereplicaset-8518d55de52b.rst b/doc/source/kube-virt/virtualmachineinstancereplicaset-8518d55de52b.rst new file mode 100644 index 000000000..2988292a2 --- /dev/null +++ b/doc/source/kube-virt/virtualmachineinstancereplicaset-8518d55de52b.rst @@ -0,0 +1,102 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _virtualmachineinstancereplicaset-8518d55de52b: + +==================================== +Virtual Machine Instance Replica Set +==================================== + +A |VMIRS| tries to ensure that a specified number of |VMI| replicas are +running at any time. Meaning, a |VMIRS| makes sure that a |VMI| or a +homogeneous set of |VMIs| is always up and ready. + + +Use VMIRS +--------- + +The |VMIRS| allows you to specify a |VMI| Template in ``spec.template``. It +consists of ``ObjectMetadata`` in ``spec.template.metadata``, and a |VMI| spec +in ``spec.template.spec``. The specification of the |VM| is equal to the +specification of the |VM| in the |VMI| workload. + +``spec.replicas`` can be used to specify how many replicas are wanted. If +unspecified, the default value is 1. This value can be updated anytime. The +controller reacts to the changes. + +Example manifest for ``replicaset``: + +.. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: VirtualMachineInstanceReplicaSet + metadata: + name: testreplicaset + spec: + replicas: 3 + selector: + matchLabels: + myvmi: myvmi + template: + metadata: + name: test + labels: + myvmi: myvmi + spec: + domain: + devices: + disks: + - disk: + name: containerdisk + resources: + requests: + memory: 64M + volumes: + - name: containerdisk + containerDisk: + image: kubevirt/cirros-container-disk-demo:latest + + +Configure Horizontal VM Autoscaler +---------------------------------- + +.. code-block:: none + + [sysadmin@controller-0 ravi-test(keystone_admin)]$ cat hpa.yaml + --- + apiVersion: autoscaling/v1 + kind: HorizontalPodAutoscaler + metadata: + name: test-hpa + spec: + scaleTargetRef: + kind: VirtualMachineInstanceReplicaSet + name: testreplicaset + apiVersion: kubevirt.io/v1 + minReplicas: 3 + maxReplicas: 5 + targetCPUUtilizationPercentage: 50 + --- + + Verify : + [sysadmin@controller-0 (keystone_admin)]$ kubectl describe horizontalpodautoscaler.autoscaling + Name: test-hpa + Namespace: default + Labels: + Annotations: autoscaling.alpha.kubernetes.io/conditions: + [{"type":"AbleToScale","status":"True","lastTransitionTime":"2023-11-30T18:17:34Z","reason":"ScaleDownStabilized","message":"recent recomm... + autoscaling.alpha.kubernetes.io/current-metrics: + [\{"type":"Resource","resource":{"name":"cpu","currentAverageUtilization":1,"currentAverageValue":"2m"}}] + CreationTimestamp: Thu, 30 Nov 2023 19:17:19 +0100 + Reference: VirtualMachineInstanceReplicaSet/testreplicaset + Target CPU utilization: 50% + Current CPU utilization: 1% + Min replicas: 3 + Max replicas: 5 + VirtualMachineInstanceReplicaSet pods: 3 current / 3 desired + Events: + +.. note:: + + Based on CPU usage in above example the autoscaler will scale up and down + the ``replicaset``. diff --git a/doc/source/kube-virt/vm-snapshot-and-restore-21158b60cd56.rst b/doc/source/kube-virt/vm-snapshot-and-restore-21158b60cd56.rst new file mode 100644 index 000000000..1b71cfad4 --- /dev/null +++ b/doc/source/kube-virt/vm-snapshot-and-restore-21158b60cd56.rst @@ -0,0 +1,129 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _vm-snapshot-and-restore-21158b60cd56: + +======================= +VM Snapshot and Restore +======================= + +|VM| snapshot allows you to snapshot the running |VM| with existing +configuration and restore back to configuration point. + + +Snapshot a VM +------------- + +Snapshotting a |VM| is supported for online and offline |VMs|. + +When snapshotting a running |VM| the controller will check for the qemu guest +agent in the |VM|. If the agent exists it will freeze the |VM| filesystems +before taking the snapshot and unfreeze after the snapshot. It is recommended +to take online snapshots with the guest agent for a better snapshot, if not +present, a best effort snapshot will be taken. + +.. rubric:: |proc| + +To enable snapshot functionality system does require snapshot |CRD| and +snapshot controller to be created on system. Follow the steps below: + +#. Run to install snapshot |CRD| and snapshot controller on Kubernets: + + - ``kubectl apply -f`` https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml + + - ``kubectl apply -f`` https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml + + - ``kubectl apply -f`` https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml + + - ``kubectl apply -f`` https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml + + - ``kubectl apply -f`` https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml + + +#. Create ``VolumeSnapshotClass`` for ``cephfs`` and ``rbd``: + + .. code-block:: none + + cat <cephfs-storageclass.yaml + — + apiVersion: snapshot.storage.k8s.io/v1 + kind: VolumeSnapshotClass + metadata: + name: csi-cephfsplugin-snapclass + driver: cephfs.csi.ceph.com + parameters: + clusterID: 60ee9439-6204-4b11-9b02-3f2c2f0a4344 + csi.storage.k8s.io/snapshotter-secret-name: ceph-pool-kube-cephfs-data + csi.storage.k8s.io/snapshotter-secret-namespace: default + deletionPolicy: Delete + EOF + cat <rbd-storageclass.yaml + — + apiVersion: snapshot.storage.k8s.io/v1 + kind: VolumeSnapshotClass + metadata: + name: csi-rbdplugin-snapclass + driver: rbd.csi.ceph.com + parameters: + clusterID: 60ee9439-6204-4b11-9b02-3f2c2f0a4344 + csi.storage.k8s.io/snapshotter-secret-name: ceph-pool-kube-rbd + csi.storage.k8s.io/snapshotter-secret-namespace: default + deletionPolicy: Delete + EOF + + .. note:: + + Get the cluster ID from: ``kubectl describe sc cephfs, rbd``. + +#. Create snapshot manifest of running |VM| using the example yaml below: + + .. code-block:: none + + cat<cirros-snapshot.yaml + apiVersion: snapshot.kubevirt.io/v1alpha1 + kind: VirtualMachineSnapshot + metadata: + name: snap-cirros + spec: + source: + apiGroup: kubevirt.io + kind: VirtualMachine + name: pvc-test-vm + failureDeadline: 3m + EOF + +#. Apply the snapshot manifest and verify if the snapshot is successfully + created. + + .. code-block:: none + + kubectl apply -f cirros-snapshot.yaml + [sysadmin@controller-0 kubevirt-GA-testing(keystone_admin)]$ kubectl get VirtualMachineSnapshot + NAME SOURCEKIND SOURCENAME PHASE READYTOUSE CREATIONTIME ERROR + snap-cirros VirtualMachine pvc-test-vm Succeeded true 28m + +Example manifest to restore the snapshot: + +.. code-block:: none + + <cirros-restore.yaml + apiVersion: snapshot.kubevirt.io/v1alpha1 + kind: VirtualMachineRestore + metadata: + name: restore-cirros + spec: + target: + apiGroup: kubevirt.io + kind: VirtualMachine + name: pvc-test-vm + virtualMachineSnapshotName: snap-cirros + EOF + kubectl apply -f cirros-restore.yaml + +Verify the snapshot restore: + +.. code-block:: none + + [sysadmin@controller-0 kubevirt-GA-testing(keystone_admin)]$ kubectl get VirtualMachineRestore + NAME TARGETKIND TARGETNAME COMPLETE RESTORETIME ERROR + restore-cirros VirtualMachine pvc-test-vm true 34m diff --git a/doc/source/kube-virt/vm-using-secret-as-startup-configuration-4a8255e26b1f.rst b/doc/source/kube-virt/vm-using-secret-as-startup-configuration-4a8255e26b1f.rst new file mode 100644 index 000000000..03c265876 --- /dev/null +++ b/doc/source/kube-virt/vm-using-secret-as-startup-configuration-4a8255e26b1f.rst @@ -0,0 +1,95 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _vm-using-secret-as-startup-configuration-4a8255e26b1f: + +======================================== +VM Using Secret as Startup Configuration +======================================== + +A secret can be presented to the |VM| as disk or as a filesystem. + +The disk method does not support dynamic change propagation and the filesystem +method does not support live migration. Therefore, depending on the use-case, +one or the other may be more suitable. + +Example of the creation of a Secret: + +.. code-block:: none + + apiVersion: v1 + kind: Secret + metadata: + name: app-secret + type: Opaque + data: + username: YWxheA== + password: TGk2OW51eCo= + +Example of a |VM| using secret as filesystem: + +.. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: VirtualMachineInstance + metadata: + labels: + special: vmi-fedora + name: vmi-fedora-secret + spec: + domain: + devices: + filesystems: + - name: app-secret-fs + virtiofs: {} + disks: + - disk: + bus: virtio + name: containerdisk + machine: + type: "" + resources: + requests: + memory: 1024M + terminationGracePeriodSeconds: 0 + volumes: + - name: containerdisk + containerDisk: + image: quay.io/containerdisks/fedora:latest + - cloudInitNoCloud: + userData: |- + #cloud-config + chpasswd: + expire: false + password: fedora + user: fedora + bootcmd: + # mount the Secret + - "sudo mkdir /mnt/app-secret" + - "sudo mount -t virtiofs app-secret-fs /mnt/app-secret" + name: cloudinitdisk + - secret: + secretName: app-secret + name: app-secret-fs + + +Then, you can login to |VM| to verify: + +.. code-block:: none + + [fedora@vmi-fedora-secret ~]$ df -h + Filesystem Size Used Avail Use% Mounted on + /dev/vda5 4.0G 453M 3.1G 13% / + devtmpfs 4.0M 0 4.0M 0% /dev + tmpfs 450M 0 450M 0% /dev/shm + tmpfs 180M 720K 179M 1% /run + tmpfs 450M 0 450M 0% /tmp + /dev/vda2 966M 61M 840M 7% /boot + /dev/vda3 100M 12M 89M 12% /boot/efi + /dev/vda5 4.0G 453M 3.1G 13% /home + app-secret-fs 94G 8.0K 94G 1% /mnt/app-secret + tmpfs 90M 4.0K 90M 1% /run/user/1000 + [fedora@vmi-fedora-secret ~]$ ls -lrth /mnt/app-secret + total 0 + lrwxrwxrwx. 1 root 107 15 Jan 15 16:43 username -> ..data/username + lrwxrwxrwx. 1 root 107 15 Jan 15 16:43 password -> ..data/password diff --git a/doc/source/kube-virt/vm-using-service-account-as-filesystem-5fd4deb7339a.rst b/doc/source/kube-virt/vm-using-service-account-as-filesystem-5fd4deb7339a.rst new file mode 100644 index 000000000..1686faf65 --- /dev/null +++ b/doc/source/kube-virt/vm-using-service-account-as-filesystem-5fd4deb7339a.rst @@ -0,0 +1,72 @@ +.. WARNING: Add no lines of text between the label immediately following +.. and the title. + +.. _vm-using-service-account-as-filesystem-5fd4deb7339a: + +====================================== +VM Using Service Account as Filesystem +====================================== + +A ``serviceaccount`` volume references a Kubernetes ``serviceaccount``. A +``serviceaccount`` can be presented to the |VM| as disk or as a filesystem. + +The disk method does not support dynamic change propagation and the filesystem +method does not support live migration. Therefore, depending on the use-case, +one or the other may be more suitable. + +By using filesystem, ``serviceaccounts`` are shared through ``virtiofs``. In +contrast with using disk for sharing ``serviceaccounts``, filesystem allows you +to dynamically propagate changes on ``serviceaccounts`` to |VMIs| (i.e. the +|VM| does not need to be rebooted). + +.. rubric:: Limitation + +Currently, |VMIs| cannot be live migrated since ``virtiofs`` does not support +live migration. + + +Example of a |VM| creation using default service account: + +.. code-block:: none + + apiVersion: kubevirt.io/v1 + kind: VirtualMachineInstance + metadata: + labels: + special: vmi-fedora-sa + name: vmi-fedora + spec: + domain: + devices: + filesystems: + - name: serviceaccount-fs + virtiofs: {} + disks: + - disk: + bus: virtio + name: containerdisk + machine: + type: "" + resources: + requests: + memory: 1024M + terminationGracePeriodSeconds: 0 + volumes: + - name: containerdisk + containerDisk: + image: quay.io/containerdisks/fedora:latest + - cloudInitNoCloud: + userData: |- + #cloud-config + chpasswd: + expire: false + password: fedora + user: fedora + bootcmd: + # mount the ConfigMap + - "sudo mkdir /mnt/serviceaccount" + - "sudo mount -t virtiofs serviceaccount-fs /mnt/serviceaccount" + name: cloudinitdisk + - name: serviceaccount-fs + serviceAccount: + serviceAccountName: default diff --git a/doc/source/shared/abbrevs.txt b/doc/source/shared/abbrevs.txt index aa9ea9b7e..516d9bbee 100755 --- a/doc/source/shared/abbrevs.txt +++ b/doc/source/shared/abbrevs.txt @@ -174,6 +174,7 @@ .. |RPC| replace:: :abbr:`RPC (Remote Procedure Call)` .. |RST| replace:: :abbr:`rST (reStructuredText)` .. |RVMC| replace:: :abbr:`RVMC (Redfish Virtual Media Controller)` +.. |RWX| replace:: :abbr:`RWX (Read Write Many)` .. |SAN| replace:: :abbr:`SAN (Subject Alternative Name)` .. |SANs| replace:: :abbr:`SANs (Subject Alternative Names)` .. |SAS| replace:: :abbr:`SAS (Serial Attached SCSI)` @@ -221,6 +222,8 @@ .. |UFT| replace:: :abbr:`UFT (Unified Flow Tool)` .. |UUID| replace:: :abbr:`UUID (Universally Unique Identifier)` .. |UUIDs| replace:: :abbr:`UUIDs (Universally Unique Identifiers)` +.. |vCPU| replace:: :abbr:`vCPU (Virtual Central Processing Unit)` +.. |vCPUs| replace:: :abbr:`vCPUs (Virtual Central Processing Units)` .. |VF| replace:: :abbr:`VF (Virtual Function)` .. |VFIO| replace:: :abbr:`VFIO (Virtual Function I/O)` .. |VFs| replace:: :abbr:`VFs (Virtual Functions)` @@ -231,6 +234,10 @@ .. |VLANs| replace:: :abbr:`VLANs (Virtual Local Area Networks)` .. |VM| replace:: :abbr:`VM (Virtual Machine)` .. |VMs| replace:: :abbr:`VMs (Virtual Machines)` +.. |VMI| replace:: :abbr:`VMI (Virtual Machine Instance)` +.. |VMIs| replace:: :abbr:`VMIs (Virtual Machine Instances)` +.. |VMIM| replace:: :abbr:`VMIM (Virtual Machine Instance Migration)` +.. |VMIRS| replace:: :abbr:`VMIRS (Virtual Machine Instance Replica Set)` .. |VNC| replace:: :abbr:`VNC (Virtual Network Computing)` .. |VNF| replace:: :abbr:`VNF (Virtual Network Function)` .. |VNFs| replace:: :abbr:`VNFs (Virtual Network Functions)` diff --git a/doc/source/spelling_wordlist.txt b/doc/source/spelling_wordlist.txt index 0e5cb96dc..c7a3d7dfc 100644 --- a/doc/source/spelling_wordlist.txt +++ b/doc/source/spelling_wordlist.txt @@ -1078,6 +1078,7 @@ virsh virt virtio Virtio +virtiofs virtualization Virtualization virtualized