From e01b4491bc16024f7410f621411ed1dabc343f45 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 21 Apr 2026 13:01:29 +0200 Subject: [PATCH] feat(tenants): add dedicatedCPU quota sugar for pinned-CPU workloads Introduce an optional `dedicatedCPU` key in the cozy-lib resource sanitizer (and therefore in tenant `resourceQuotas`). Its value is added 1:1 to both `limits.cpu` and `requests.cpu`, bypassing the `cpuAllocationRatio` divisor. This fixes the quota mismatch between regular VMs (where KubeVirt divides pod `requests.cpu` by `cpuAllocationRatio`) and instance types with `dedicatedCPUPlacement` (where pod `requests == limits`): previously, a single dedicated-CPU VM could exhaust the tenant's `requests.cpu` quota while the `limits.cpu` quota stayed largely unused, effectively shrinking the usable tenant capacity for CX-series workloads to `cpu / cpuAllocationRatio`. With the new key, operators can declare dedicated capacity explicitly: resourceQuotas: cpu: 10 # oversubscribable (burstable) CPU budget dedicatedCPU: 2 # plus 2 cores of dedicated physical CPU This yields `limits.cpu=12, requests.cpu=3` at the default ratio of 10, so both sides of the ResourceQuota stay in lockstep regardless of which instance-type series a workload uses. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- packages/apps/tenant/README.md | 24 +++++++++++- .../library/cozy-lib/templates/_resources.tpl | 27 +++++++++++++ .../cozy-lib-tests/tests/quota_test.yaml | 38 +++++++++++++++++++ .../tests/quota_values_combined.yaml | 6 +++ .../tests/quota_values_dedicated.yaml | 5 +++ 5 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 packages/tests/cozy-lib-tests/tests/quota_values_combined.yaml create mode 100644 packages/tests/cozy-lib-tests/tests/quota_values_dedicated.yaml diff --git a/packages/apps/tenant/README.md b/packages/apps/tenant/README.md index 96cf5c6e..071de75d 100644 --- a/packages/apps/tenant/README.md +++ b/packages/apps/tenant/README.md @@ -92,7 +92,15 @@ tenant-u1 The `resourceQuotas` parameter allows you to limit resources available to the tenant. Supported keys include: **Compute resources** (converted to `requests.X` and `limits.X`): -- `cpu` - Total CPU cores (e.g., `"4"` or `"500m"`) +- `cpu` - Total CPU cores (e.g., `"4"` or `"500m"`). `requests.cpu` is derived + as `cpu / cpuAllocationRatio` (default ratio is 10), matching KubeVirt's + behavior for regular VMs and burstable containers. +- `dedicatedCPU` - Additional CPU cores reserved for workloads that pin + physical cores and cannot be oversubscribed (KubeVirt instance types with + `dedicatedCPUPlacement`, pods with `Guaranteed` QoS). The value is added to + both `limits.cpu` and `requests.cpu` at a 1:1 ratio, so that one dedicated + core consumes one full unit of `requests.cpu` quota. Can be combined with + `cpu`. - `memory` - Total memory (e.g., `"4Gi"` or `"512Mi"`) - `ephemeral-storage` - Ephemeral storage limit (e.g., `"10Gi"`) - `storage` - Total persistent storage (e.g., `"100Gi"`) @@ -115,3 +123,17 @@ resourceQuotas: services.loadbalancers: "3" pods: "50" ``` + +**Example with dedicated CPU** (for tenants that run VMs from the `cx*` +instance-type series or other `dedicatedCPUPlacement` workloads): + +```yaml +resourceQuotas: + cpu: 10 # 10 cores of oversubscribable (burstable) CPU budget + dedicatedCPU: 2 # plus 2 cores of dedicated physical CPU + memory: 16Gi +``` + +With the default `cpuAllocationRatio=10` this produces a `ResourceQuota` of +`limits.cpu=12`, `requests.cpu=3` — enough for either 12 cores of regular VMs +(plus burst headroom) or 3 cores of dedicated VMs, or any mix in between. diff --git a/packages/library/cozy-lib/templates/_resources.tpl b/packages/library/cozy-lib/templates/_resources.tpl index cb055ea1..41bbfbf9 100644 --- a/packages/library/cozy-lib/templates/_resources.tpl +++ b/packages/library/cozy-lib/templates/_resources.tpl @@ -67,6 +67,12 @@ {{ include "cozy-lib.resources.sanitize" list (.Values.resources $) }} + The optional **dedicatedCPU** key is sugar for workloads with + `dedicatedCPUPlacement` (KubeVirt CX-series, Guaranteed-QoS pods). Its value + is added to **both** `limits.cpu` and `requests.cpu` at a 1:1 ratio (the + CPU-allocation ratio does not apply), reflecting that such workloads pin + physical cores and cannot be oversubscribed. + Example input: ============== cpu: "2" @@ -83,6 +89,18 @@ cpu: 200m # 2 / 10 memory: 256Mi # = limit devices.com/nvidia: "1" # = limit + + Example input with dedicatedCPU: + ================================ + cpu: "10" + dedicatedCPU: "2" + + Example output (cpuAllocationRatio = 10): + ========================================= + limits: + cpu: "12" # 10 + 2 + requests: + cpu: "3" # 10/10 + 2 */}} {{- define "cozy-lib.resources.sanitize" }} {{- $cpuAllocationRatio := include "cozy-lib.resources.cpuAllocationRatio" . | float64 }} @@ -99,6 +117,8 @@ {{- $cpuRequestF64 := divf $vcpuRequestF64 $cpuAllocationRatio }} {{- $_ := set $output.requests $k ($cpuRequestF64 | toString) }} {{- $_ := set $output.limits $k ($v | toString) }} +{{- else if eq $k "dedicatedCPU" }} +{{- /* merged into cpu after the loop */}} {{- else if eq $k "memory" }} {{- $vMemoryRequestF64 := (include "cozy-lib.resources.toFloat" $v) | float64 }} {{- $memoryRequestF64 := divf $vMemoryRequestF64 $memoryAllocationRatio }} @@ -114,6 +134,13 @@ {{- $_ := set $output.limits $k $v }} {{- end }} {{- end }} +{{- if hasKey $args "dedicatedCPU" }} +{{- $dedicatedF := (include "cozy-lib.resources.toFloat" (index $args "dedicatedCPU")) | float64 }} +{{- $curLimitF := (include "cozy-lib.resources.toFloat" (dig "cpu" "0" $output.limits)) | float64 }} +{{- $curRequestF := (include "cozy-lib.resources.toFloat" (dig "cpu" "0" $output.requests)) | float64 }} +{{- $_ := set $output.limits "cpu" (addf $curLimitF $dedicatedF | toString) }} +{{- $_ := set $output.requests "cpu" (addf $curRequestF $dedicatedF | toString) }} +{{- end }} {{- $output | toYaml }} {{- end }} diff --git a/packages/tests/cozy-lib-tests/tests/quota_test.yaml b/packages/tests/cozy-lib-tests/tests/quota_test.yaml index 0ba2260c..87e099a6 100644 --- a/packages/tests/cozy-lib-tests/tests/quota_test.yaml +++ b/packages/tests/cozy-lib-tests/tests/quota_test.yaml @@ -48,3 +48,41 @@ tests: - notExists: path: spec.hard["requests.services.loadbalancers"] + + - it: dedicatedCPU alone produces 1:1 cpu quota + values: + - quota_values_dedicated.yaml + + release: + name: myrelease + namespace: default + revision: 1 + isUpgrade: false + + asserts: + - equal: + path: spec.hard["limits.cpu"] + value: "2" + + - equal: + path: spec.hard["requests.cpu"] + value: "2" + + - it: cpu and dedicatedCPU are summed with correct ratios + values: + - quota_values_combined.yaml + + release: + name: myrelease + namespace: default + revision: 1 + isUpgrade: false + + asserts: + - equal: + path: spec.hard["limits.cpu"] + value: "12" + + - equal: + path: spec.hard["requests.cpu"] + value: "3" diff --git a/packages/tests/cozy-lib-tests/tests/quota_values_combined.yaml b/packages/tests/cozy-lib-tests/tests/quota_values_combined.yaml new file mode 100644 index 00000000..4efaba95 --- /dev/null +++ b/packages/tests/cozy-lib-tests/tests/quota_values_combined.yaml @@ -0,0 +1,6 @@ +quota: + cpu: "10" + dedicatedCPU: "2" + +_cluster: {} +_namespace: {} diff --git a/packages/tests/cozy-lib-tests/tests/quota_values_dedicated.yaml b/packages/tests/cozy-lib-tests/tests/quota_values_dedicated.yaml new file mode 100644 index 00000000..68f7a9b7 --- /dev/null +++ b/packages/tests/cozy-lib-tests/tests/quota_values_dedicated.yaml @@ -0,0 +1,5 @@ +quota: + dedicatedCPU: "2" + +_cluster: {} +_namespace: {}