Shared GPU workloads
Shared GPU lets several container workloads run on one physical GPU at the same
time, each holding a fixed fraction of the device's memory and compute. It is
the right choice when a job needs GPU acceleration but not a whole card — model
inference, notebooks, small training runs, batch scoring. You run it the same
way you run any other workload: a Kubernetes manifest applied with kubectl.
Shared GPU is a preview capability. Your operator enables it per project and grants a per-model entitlement (a GPU quota). If your project has no GPU quota, it is not enabled for you yet — ask your operator.
For a stronger isolation boundary — one whole physical GPU bound to one guest — use a Dedicated GPU VM instead. Shared GPU trades that boundary for density.
How sharing works
A Shared GPU product is a fixed fraction of a specific GPU model — for example, an 8 GiB slice of an NVIDIA V100. You request the product; you do not tune memory and compute freely.
- The memory slice is enforced. Inside the container,
nvidia-smireports the slice size, not the physical card. On the 8 GiB V100 product,nvidia-smi --query-gpu=memory.totalreturns8192 MiBeven though the card has 32 GB. - The compute share is cooperative. The platform steers each workload toward its share in steady state, but startup and some CUDA library paths can briefly exceed it. It is not guaranteed performance or a security boundary. If you need a hard boundary, use a Dedicated GPU VM.
- Quota is an entitlement, not a reservation. Holding quota does not reserve
a physical slice. When every compatible GPU is busy, a valid workload
legitimately queues — its pod stays
Pendinguntil a slice frees up. - Each GPU model is metered independently, so spending your V100 entitlement never consumes another model's headroom.
Check your entitlement
Your GPU entitlement appears in your project quota as a line per product:
kubectl get resourcequota -n {project-namespace} -o yaml | grep deviceclass
# kube-dc-nvidia-v100-shared-8g.deviceclass.resource.k8s.io/devices: "1"
The number is how many concurrent slices of that product you may hold.
Run a Shared GPU workload
A Shared GPU workload is two objects: a ResourceClaimTemplate describing the
fixed product, and a Deployment that references it. Copy the manifest below,
replace {workload-name} and the container image, and apply it. Keep
everything else exactly as shown — the platform validates the shape on admission
(see The contract below).
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: {workload-name}-gpu
labels: &labels
app.kubernetes.io/name: {workload-name}
app.kubernetes.io/managed-by: kube-dc
kube-dc.com/gpu-backend: dra
kube-dc.com/gpu-profile: nvidia-v100-hami
annotations: &annotations
kube-dc.com/gpu-workload-hash: "0000000000000000000000000000000000000000000000000000000000000000"
kube-dc.com/gpu-memory-mib: "8192"
kube-dc.com/gpu-core-percent: "25"
spec:
metadata:
labels: *labels
annotations: *annotations
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: kube-dc-nvidia-v100-shared-8g
allocationMode: ExactCount
count: 1
capacity:
requests:
memory: 8192Mi # fixed by the product
cores: "25" # fixed by the product
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {workload-name}
labels: &labels
app.kubernetes.io/name: {workload-name}
app.kubernetes.io/managed-by: kube-dc
kube-dc.com/gpu-backend: dra
kube-dc.com/gpu-profile: nvidia-v100-hami
annotations: &annotations
kube-dc.com/gpu-workload-hash: "0000000000000000000000000000000000000000000000000000000000000000"
kube-dc.com/gpu-memory-mib: "8192"
kube-dc.com/gpu-core-percent: "25"
spec:
replicas: 1
strategy:
type: Recreate # release the slice before reacquiring one
selector:
matchLabels:
app.kubernetes.io/name: {workload-name}
template:
metadata:
labels: *labels
annotations: *annotations
spec:
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: workload
image: {your-cuda-image}@sha256:{digest} # digest-pinned, glibc-based
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
resources:
claims:
- name: gpu
command: ["/bin/sh", "-ec"]
args: ["nvidia-smi -L; sleep 3600"]
resourceClaims:
- name: gpu
resourceClaimTemplateName: {workload-name}-gpu
Apply it into your project:
kubectl apply -n {project-namespace} -f shared-gpu.yaml
Each document defines its own
&labels/&annotationsand reuses them within that document — YAML anchors do not carry across the---separator, so the ResourceClaimTemplate and the Deployment each declare the labels once. If your editor strips YAML anchors, just write the same four labels and three annotations wherever*labels/*annotationsappears.
Verify
kubectl get deployment,pod -n {project-namespace} \
-l app.kubernetes.io/name={workload-name}
# GPU allocated and quota consumed:
kubectl get resourceclaim -n {project-namespace} \
-l app.kubernetes.io/name={workload-name}
# The GPU is visible inside the container (note the sliced memory):
kubectl logs -n {project-namespace} deploy/{workload-name}
# GPU 0: Tesla V100-PCIE-32GB (UUID: ...)
A workload may legitimately stay Pending while it waits for a free slice —
that is expected when your GPUs are busy. Confirm the GPU through your normal
CUDA framework (for example torch.cuda.is_available()). Do not build logic
around a GPU's UUID, PCI address, or the node name — a rescheduled workload can
land on different hardware.
Release
Delete the workload to return the slice. Delete the whole set, not just the pod — deleting only the pod lets the Deployment respawn it and keep the slice:
kubectl delete -n {project-namespace} -f shared-gpu.yaml
Watch the product's quota line return to its previous value.
The contract
The platform validates every GPU workload on admission, so a manifest that
drifts from the fixed product is rejected. If kubectl apply returns a
ValidatingAdmissionPolicy ... denied error, check these:
- Fixed capacity.
memoryandcoresmust be exactly the product's published values (8192Mi/25for the 8 GiB V100). Requesting more is rejected — the fixed product is your entitlement. - One request named
gpu,allocationMode: ExactCount,count: 1, no selectors, noadminAccess. - Labels
app.kubernetes.io/managed-by: kube-dc,kube-dc.com/gpu-backend: dra,kube-dc.com/gpu-profile, andapp.kubernetes.io/name, identical on the object, itsspec.metadata, and the Deployment's pod template. - Name of the
ResourceClaimTemplatemust be{workload-name}-gpu. strategy: Recreate, one container, and the container'sresources.claims[0].name=gpu= the pod'sresourceClaims[0].name.- No
schedulerName,nodeSelector,nodeName, node affinity, host namespaces,hostPath, or privileged containers — the platform schedules and isolates the workload for you.
You cannot exceed your entitlement: the fixed product is enforced for every claim, and your project quota bounds how many slices you can hold at once.
Limits and guarantees
- Shared GPU is a preview capability, enabled per project by your operator.
- The memory slice is a hard cap; the compute share is a cooperative target.
- Quota is an entitlement; workloads queue when GPUs are busy.
- For whole-device isolation or a workload that must not share silicon with another tenant, use a Dedicated GPU VM.