From c5b99df8ca8030f9e6688fe1298a34ebd36ccc0c Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Wed, 14 Oct 2026 01:33:24 +0300 Subject: [PATCH 1/7] docs(design-proposals): add GPU virtual machines proposal Hosters that sell GPUs need to offer tenants VM images with a working guest driver, but the vGPU guest driver may not be published and the golden image collection can only fetch from anonymous HTTP. The proposal keeps the image on the hoster's side: private sources and a GPU marking for golden images, an additive extraImages list, and the marking shown in the image picker. The VM takes its GPUs as device entries backed by GPUDevice, so the proposal is implemented after the GPU device proposal. It also states what a GPU VM does on a node drain and after a node failure. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin --- design-proposals/vgpu-vms/README.md | 206 ++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 design-proposals/vgpu-vms/README.md diff --git a/design-proposals/vgpu-vms/README.md b/design-proposals/vgpu-vms/README.md new file mode 100644 index 00000000..320b3a06 --- /dev/null +++ b/design-proposals/vgpu-vms/README.md @@ -0,0 +1,206 @@ +# GPU virtual machines with hoster-supplied guest images + +- **Title:** `GPU virtual machines: vGPU and passthrough with hoster-supplied guest images` +- **Author(s):** `@lexfrei` +- **Date:** `2026-10-14` +- **Status:** Review + +## Overview + +A hoster that sells GPUs wants a tenant to order a virtual machine with either a vGPU profile or a whole GPU in passthrough, and to boot it from an image that already has a working guest driver. For vGPU, the guest half is the NVIDIA vGPU guest driver, and its license client runs inside the guest. That driver is licensed per customer and may not be published, for example in a public image or repository, so Cozystack cannot build, ship or test such an image. Passthrough used for compute runs the NVIDIA data center driver, which has no license check, but the guest still needs that driver. + +This proposal keeps the image on the hoster's side and makes the platform able to carry it. The hoster adds its own images to the golden image collection, marks them as GPU guest images, and can fetch them from a private source. The image picker in the dashboard shows that marking. A tenant creates a disk from such an image, orders GPUs as `GPUDevice` objects, and attaches them to the VM by name. + +NVIDIA's vGPU stack changes quickly. Within the last few releases the driver branches, the way vGPUs are exposed (mediated devices or SR-IOV virtual functions), the KubeVirt device plugins and the licensing rules all changed. This proposal describes that stack as it was in October 2026. Expect it to be updated as NVIDIA and KubeVirt move, with a decision record under it for each change of course, and check the versions named here against your own before relying on them. + +## Scope and related proposals + +- [`gpudevice`](../gpudevice/README.md) adds the GPU catalog, `GPUDevice` objects that a tenant orders within a quota, usage records, and the host layer under them. This proposal builds on it and is implemented after it. +- [`kairos-vgpu-workers`](../kairos-vgpu-workers/README.md) covers GPUs in Kairos worker pools of tenant Kubernetes clusters, vGPU and passthrough. +- [Node fencing](../node-fencing/README.md) restarts VMs of a dead node automatically when its optional package is enabled and the node's BMC answers. A GPU VM with `runStrategy: Always` or `RerunOnFailure` restarts that way once another node has room for its devices. Design §5 gives the details, the other run strategies, and the exception for a device of a `Stay` class on a cordoned node. + +These proposals are written in parallel, so the links to them work only once they are merged. + +## Decisions + +## Context + +### What exists + +Golden images live in `packages/system/vm-default-images`. Its `images` list renders one CDI `DataVolume` per entry in `cozy-public`, named `vm-default-images-`, with the OS, architecture and description stored as `vm-default-images.cozystack.io/*` annotations. The only source is an HTTP URL, with no credentials and no custom CA. The package is optional, and its values can be overridden through the `cozystack.vm-default-images` Package. The documentation also describes `hack/cdi_golden_image_create.sh`, which creates such a `DataVolume` by hand, and the picker lists it like any other golden image. + +A tenant creates a disk with `vm-disk`, either from a golden image by name, which `vm-disk` clones from `cozy-public`, or from their own image by upload, HTTP URL or a clone of another disk. The dashboard fills the golden image picker from the `image` option source (`pkg/registry/core/option/providers.go`). That provider returns only names: it strips the PVC prefix and drops images whose import failed. The picker in `SourceField.tsx` reads only the value. + +A VM takes its GPUs in the `gpus` field of `vm-instance`. Today each entry is `{name: }`, the name of a device-plugin resource in the KubeVirt `permittedHostDevices`. `gpudevice` adds `{device: }` entries that reference a tenant's `GPUDevice`. + +### The problem + +A tenant who orders a GPU VM expects a guest that works on first boot, and the hoster is the one with the driver entitlement and the license server. Today the hoster can offer such an image only by hand: + +- the package's `images` list cannot authenticate, and a public URL is not an option for an image whose driver may not be published; +- adding images to that list means restating the whole default list, because Helm replaces lists instead of merging them; +- a hand-made `DataVolume` lives outside the platform values, the package health check does not cover it, and nothing reconciles it; +- the image picker shows bare names, so a tenant cannot tell a GPU guest image from a plain one. + +## Goals + +- A hoster adds GPU guest images to the golden image collection through Package values, without restating the default list and without a public URL. +- A tenant sees in the image picker which images are GPU guest images, for which mode, and for which driver branch. +- A tenant gets a VM with a vGPU profile or with whole GPUs from `vm-disk`, `vm-instance` and `GPUDevice`. +- Golden images stay a shared catalog, and tenant images stay `vm-disk` sources. +- The document states what happens to a GPU VM on node maintenance and on node failure. +- This proposal adds no NVIDIA driver or guest image to Cozystack's tree, CI or registries. + +### Non-goals + +- Building, shipping or testing guest images. The image and its contents are the hoster's. +- Licensing on the tenant's behalf. The guest licenses itself, and the hoster decides how the token gets in. +- The GPU catalog, quotas, usage records and the host layer. These belong to `gpudevice`. +- Enforcing that a GPU VM boots from a GPU guest image (Design §3). +- Making GPU VMs survive a node failure without a restart. + +## Design + +### 1. Golden image entries for GPU guest images + +Each golden image entry gets four optional fields, and the hoster gets a second list for its own entries: + +```yaml +extraImages: + - name: hoster-ubuntu-24.04-vgpu-r595 + url: https://images.hoster.example/ubuntu-24.04-vgpu-r595.qcow2 + storage: 30Gi + os: + family: Linux + name: Ubuntu + version: "24.04" + architecture: amd64 + description: "Ubuntu 24.04 with the NVIDIA vGPU guest driver" + gpu: + vendor: nvidia + mode: vgpu + driverBranch: "R595" + secretRef: images-hoster-example + certConfigMap: images-hoster-example-ca +``` + +- The `gpu` field marks the image. `mode` is `vgpu` or `passthrough`, because a guest image is built for one of the two device types. `vendor` is a free string, so later vendors need no schema change. When `gpu` is set, `vendor` and `mode` are required and the chart fails on any other `mode`. `driverBranch` is optional free text shown to tenants. The chart writes the three as `vm-default-images.cozystack.io/gpu-vendor`, `gpu-mode` and `gpu-driver-branch` annotations. +- The `secretRef`, `secretExtraHeaders` and `certConfigMap` fields go to the CDI `http` source unchanged. `secretRef` names one Secret with the basic auth user name in the key `accessKeyId` and the password in `secretKey`, the keys CDI v1.66.1 reads for an HTTP source; a Secret without them stops the importer pod from starting. `secretExtraHeaders` is a list of Secret names, each value a whole header line such as `Authorization: Bearer `, with no trailing newline. Basic auth through `secretRef` and an `Authorization` header are alternatives: when both are set, CDI v1.66.1 sends a different one on different requests, so an entry uses one of them. The hoster keeps the CA under the key `tls.crt` of the `certConfigMap`. The transfer trusts every key of it, but CDI v1.66.1 passes only `/tls.crt` to the nbdkit curl plugin it uses to probe the image ([`pkg/image/nbdkit.go`](https://github.com/kubevirt/containerized-data-importer/blob/v1.66.1/pkg/image/nbdkit.go)). CDI requires the Secrets and the ConfigMap in the namespace of the `DataVolume`, `cozy-public`, where the hoster creates them. Tenants have no read access to Secrets there. +- The `extraImages` list has the same entry type as `images`. The chart renders both and fails if a name appears twice, in both lists or within one. A hoster adds images without copying the defaults, and a later change of the default list still reaches them. + +A tenant can still upload an image with their own driver and boot a GPU VM from it, unmarked. + +### 2. Image picker + +The `image` option provider reads the marking from the `DataVolume` of each golden image, which it already lists to filter out failed imports. For an image with `gpu` it sets `Label` to the name followed by the marking, for example `hoster-ubuntu-24.04-vgpu-r595 (nvidia vgpu, driver R595)`, and leaves out an empty `driverBranch`. The picker in `SourceField.tsx` renders `Label` when it is set and the value otherwise, so an image without `gpu` shows as before. A tooltip is not enough, because the dashboard renders `Description` only as the `title` of an `