From 03579649063d669f9f57d534f74136befef98c62 Mon Sep 17 00:00:00 2001 From: Jordan Christiansen Date: Sat, 31 Oct 2020 09:32:52 -0500 Subject: Centralize cores and period/quota conversion code Signed-off-by: Jordan Christiansen --- cmd/podman/common/specgen.go | 5 +---- libpod/kube.go | 2 +- pkg/domain/infra/abi/play.go | 9 +++------ pkg/util/utils.go | 23 +++++++++++++++++++++++ pkg/util/utils_test.go | 20 ++++++++++++++++++++ 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index bd3e5fafd..ca1e25be1 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -25,11 +25,8 @@ func getCPULimits(c *ContainerCLIOpts) *specs.LinuxCPU { cpu := &specs.LinuxCPU{} hasLimits := false - const cpuPeriod = 100000 - if c.CPUS > 0 { - quota := int64(c.CPUS * cpuPeriod) - period := uint64(cpuPeriod) + period, quota := util.CoresToPeriodAndQuota(c.CPUS) cpu.Period = &period cpu.Quota = "a diff --git a/libpod/kube.go b/libpod/kube.go index cd5064c84..067e7827d 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -327,7 +327,7 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) { period := *c.config.Spec.Linux.Resources.CPU.Period if quota > 0 && period > 0 { - cpuLimitMilli := int64(1000 * float64(quota) / float64(period)) + cpuLimitMilli := int64(1000 * util.PeriodAndQuotaToCores(period, quota)) // Kubernetes: precision finer than 1m is not allowed if cpuLimitMilli >= 1 { diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 57de0f3b1..fbba00984 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -36,8 +36,6 @@ const ( kubeDirectoryPermission = 0755 // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath kubeFilePermission = 0644 - // Kubernetes sets CPUPeriod to 100000us (100ms): https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/ - defaultCPUPeriod = 100000 ) func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { @@ -515,10 +513,9 @@ func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container return nil, errors.Wrap(err, "Failed to set CPU quota") } if milliCPU > 0 { - containerConfig.Resources.CPUPeriod = defaultCPUPeriod - // CPU quota is a fraction of the period: milliCPU / 1000.0 * period - // Or, without floating point math: - containerConfig.Resources.CPUQuota = milliCPU * defaultCPUPeriod / 1000 + period, quota := util.CoresToPeriodAndQuota(float64(milliCPU) / 1000) + containerConfig.Resources.CPUPeriod = period + containerConfig.Resources.CPUQuota = quota } containerConfig.Resources.Memory, err = quantityToInt64(containerYAML.Resources.Limits.Memory()) diff --git a/pkg/util/utils.go b/pkg/util/utils.go index a9aad657d..415fd169b 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -653,3 +653,26 @@ func CreateCidFile(cidfile string, id string) error { cidFile.Close() return nil } + +// DefaultCPUPeriod is the default CPU period is 100us, which is the same default +// as Kubernetes. +const DefaultCPUPeriod uint64 = 100000 + +// CoresToPeriodAndQuota converts a fraction of cores to the equivalent +// Completely Fair Scheduler (CFS) parameters period and quota. +// +// Cores is a fraction of the CFS period that a container may use. Period and +// Quota are in microseconds. +func CoresToPeriodAndQuota(cores float64) (uint64, int64) { + return DefaultCPUPeriod, int64(cores * float64(DefaultCPUPeriod)) +} + +// PeriodAndQuotaToCores takes the CFS parameters period and quota and returns +// a fraction that represents the limit to the number of cores that can be +// utilized over the scheduling period. +// +// Cores is a fraction of the CFS period that a container may use. Period and +// Quota are in microseconds. +func PeriodAndQuotaToCores(period uint64, quota int64) float64 { + return float64(quota) / float64(period) +} diff --git a/pkg/util/utils_test.go b/pkg/util/utils_test.go index a9b37844e..cb737bd76 100644 --- a/pkg/util/utils_test.go +++ b/pkg/util/utils_test.go @@ -257,3 +257,23 @@ func TestValidateSysctlBadSysctl(t *testing.T) { _, err := ValidateSysctls(strSlice) assert.Error(t, err) } + +func TestCoresToPeriodAndQuota(t *testing.T) { + cores := 1.0 + expectedPeriod := DefaultCPUPeriod + expectedQuota := int64(DefaultCPUPeriod) + + actualPeriod, actualQuota := CoresToPeriodAndQuota(cores) + assert.Equal(t, actualPeriod, expectedPeriod, "Period does not match") + assert.Equal(t, actualQuota, expectedQuota, "Quota does not match") +} + +func TestPeriodAndQuotaToCores(t *testing.T) { + var ( + period uint64 = 100000 + quota int64 = 50000 + expectedCores = 0.5 + ) + + assert.Equal(t, PeriodAndQuotaToCores(period, quota), expectedCores) +} -- cgit v1.2.3-54-g00ecf