Merge pull request #131076 from tallclair/quota-cleanup

Deduplicate MilliCPUToQuota function & constants
This commit is contained in:
Kubernetes Prow Robot
2025-04-23 15:59:21 -07:00
committed by GitHub
3 changed files with 4 additions and 220 deletions

View File

@@ -26,48 +26,13 @@ import (
"k8s.io/kubernetes/pkg/kubelet/cm"
)
const (
milliCPUToCPU = 1000
// 100000 microseconds is equivalent to 100ms
quotaPeriod = 100000
// 1000 microseconds is equivalent to 1ms
// defined here:
// https://github.com/torvalds/linux/blob/cac03ac368fabff0122853de2422d4e17a32de08/kernel/sched/core.c#L10546
minQuotaPeriod = 1000
)
// milliCPUToQuota converts milliCPU to CFS quota and period values
// Input parameters and resulting value is number of microseconds.
func milliCPUToQuota(milliCPU int64, period int64) (quota int64) {
// CFS quota is measured in two values:
// - cfs_period_us=100ms (the amount of time to measure usage across)
// - cfs_quota=20ms (the amount of cpu time allowed to be used across a period)
// so in the above example, you are limited to 20% of a single CPU
// for multi-cpu environments, you just scale equivalent amounts
// see https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt for details
if milliCPU == 0 {
return
}
// we then convert your milliCPU to a value normalized over a period
quota = (milliCPU * period) / milliCPUToCPU
// quota needs to be a minimum of 1ms.
if quota < minQuotaPeriod {
quota = minQuotaPeriod
}
return
}
// sharesToMilliCPU converts CpuShares (cpu.shares) to milli-CPU value
// TODO(vinaykul,InPlacePodVerticalScaling): Address issue that sets min req/limit to 2m/10m before beta
// See: https://github.com/kubernetes/kubernetes/pull/102884#discussion_r662552642
func sharesToMilliCPU(shares int64) int64 {
milliCPU := int64(0)
if shares >= int64(cm.MinShares) {
milliCPU = int64(math.Ceil(float64(shares*milliCPUToCPU) / float64(cm.SharesPerCPU)))
milliCPU = int64(math.Ceil(float64(shares*cm.MilliCPUToCPU) / float64(cm.SharesPerCPU)))
}
return milliCPU
}
@@ -77,7 +42,7 @@ func quotaToMilliCPU(quota int64, period int64) int64 {
if quota == -1 {
return int64(0)
}
return (quota * milliCPUToCPU) / period
return (quota * cm.MilliCPUToCPU) / period
}
func subtractOverheadFromResourceConfig(resCfg *cm.ResourceConfig, pod *v1.Pod) *cm.ResourceConfig {

View File

@@ -25,10 +25,7 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubelet/cm"
)
@@ -36,184 +33,6 @@ func seccompLocalhostRef(profileName string) string {
return filepath.Join(fakeSeccompProfileRoot, profileName)
}
func TestMilliCPUToQuota(t *testing.T) {
for _, testCase := range []struct {
msg string
input int64
expected int64
period uint64
}{
{
msg: "all-zero",
input: int64(0),
expected: int64(0),
period: uint64(0),
},
{
msg: "5 input default quota and period",
input: int64(5),
expected: int64(1000),
period: uint64(100000),
},
{
msg: "9 input default quota and period",
input: int64(9),
expected: int64(1000),
period: uint64(100000),
},
{
msg: "10 input default quota and period",
input: int64(10),
expected: int64(1000),
period: uint64(100000),
},
{
msg: "200 input 20k quota and default period",
input: int64(200),
expected: int64(20000),
period: uint64(100000),
},
{
msg: "500 input 50k quota and default period",
input: int64(500),
expected: int64(50000),
period: uint64(100000),
},
{
msg: "1k input 100k quota and default period",
input: int64(1000),
expected: int64(100000),
period: uint64(100000),
},
{
msg: "1500 input 150k quota and default period",
input: int64(1500),
expected: int64(150000),
period: uint64(100000),
}} {
t.Run(testCase.msg, func(t *testing.T) {
quota := milliCPUToQuota(testCase.input, int64(testCase.period))
if quota != testCase.expected {
t.Errorf("Input %v and %v, expected quota %v, but got quota %v", testCase.input, testCase.period, testCase.expected, quota)
}
})
}
}
func TestMilliCPUToQuotaWithCustomCPUCFSQuotaPeriod(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CPUCFSQuotaPeriod, true)
for _, testCase := range []struct {
msg string
input int64
expected int64
period uint64
}{
{
msg: "all-zero",
input: int64(0),
expected: int64(0),
period: uint64(0),
},
{
msg: "5 input default quota and period",
input: int64(5),
expected: minQuotaPeriod,
period: uint64(100000),
},
{
msg: "9 input default quota and period",
input: int64(9),
expected: minQuotaPeriod,
period: uint64(100000),
},
{
msg: "10 input default quota and period",
input: int64(10),
expected: minQuotaPeriod,
period: uint64(100000),
},
{
msg: "200 input 20k quota and default period",
input: int64(200),
expected: int64(20000),
period: uint64(100000),
},
{
msg: "500 input 50k quota and default period",
input: int64(500),
expected: int64(50000),
period: uint64(100000),
},
{
msg: "1k input 100k quota and default period",
input: int64(1000),
expected: int64(100000),
period: uint64(100000),
},
{
msg: "1500 input 150k quota and default period",
input: int64(1500),
expected: int64(150000),
period: uint64(100000),
},
{
msg: "5 input 10k period and default quota expected",
input: int64(5),
period: uint64(10000),
expected: minQuotaPeriod,
},
{
msg: "5 input 5k period and default quota expected",
input: int64(5),
period: uint64(5000),
expected: minQuotaPeriod,
},
{
msg: "9 input 10k period and default quota expected",
input: int64(9),
period: uint64(10000),
expected: minQuotaPeriod,
},
{
msg: "10 input 200k period and 2000 quota expected",
input: int64(10),
period: uint64(200000),
expected: int64(2000),
},
{
msg: "200 input 200k period and 40k quota",
input: int64(200),
period: uint64(200000),
expected: int64(40000),
},
{
msg: "500 input 20k period and 20k expected quota",
input: int64(500),
period: uint64(20000),
expected: int64(10000),
},
{
msg: "1000 input 10k period and 10k expected quota",
input: int64(1000),
period: uint64(10000),
expected: int64(10000),
},
{
msg: "1500 input 5000 period and 7500 expected quota",
input: int64(1500),
period: uint64(5000),
expected: int64(7500),
}} {
t.Run(testCase.msg, func(t *testing.T) {
quota := milliCPUToQuota(testCase.input, int64(testCase.period))
if quota != testCase.expected {
t.Errorf("Input %v and %v, expected quota %v, but got quota %v", testCase.input, testCase.period, testCase.expected, quota)
}
})
}
}
func TestGetSeccompProfile(t *testing.T) {
_, _, m, err := createTestRuntimeManager()
require.NoError(t, err)

View File

@@ -294,13 +294,13 @@ func (m *kubeGenericRuntimeManager) calculateLinuxResources(cpuRequest, cpuLimit
if m.cpuCFSQuota {
// if cpuLimit.Amount is nil, then the appropriate default value is returned
// to allow full usage of cpu resource.
cpuPeriod := int64(quotaPeriod)
cpuPeriod := int64(cm.QuotaPeriod)
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.CPUCFSQuotaPeriod) {
// kubeGenericRuntimeManager.cpuCFSQuotaPeriod is provided in time.Duration,
// but we need to convert it to number of microseconds which is used by kernel.
cpuPeriod = int64(m.cpuCFSQuotaPeriod.Duration / time.Microsecond)
}
cpuQuota := milliCPUToQuota(cpuLimit.MilliValue(), cpuPeriod)
cpuQuota := cm.MilliCPUToQuota(cpuLimit.MilliValue(), cpuPeriod)
resources.CpuQuota = cpuQuota
if disableCPUQuota {
resources.CpuQuota = int64(-1)