mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Merge pull request #79247 from egernst/kubelet-PodOverhead
Kubelet enabling to support pod-overhead
This commit is contained in:
commit
8cf05f514c
@ -11,8 +11,12 @@ go_test(
|
|||||||
srcs = ["helpers_test.go"],
|
srcs = ["helpers_test.go"],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
|
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
||||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -22,8 +26,10 @@ go_library(
|
|||||||
srcs = ["helpers.go"],
|
srcs = ["helpers.go"],
|
||||||
importpath = "k8s.io/kubernetes/pkg/api/v1/resource",
|
importpath = "k8s.io/kubernetes/pkg/api/v1/resource",
|
||||||
deps = [
|
deps = [
|
||||||
|
"//pkg/features:go_default_library",
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,11 +23,13 @@ import (
|
|||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
// addResourceList adds the resources in newList to list
|
// addResourceList adds the resources in newList to list
|
||||||
func addResourceList(list, new v1.ResourceList) {
|
func addResourceList(list, newList v1.ResourceList) {
|
||||||
for name, quantity := range new {
|
for name, quantity := range newList {
|
||||||
if value, ok := list[name]; !ok {
|
if value, ok := list[name]; !ok {
|
||||||
list[name] = *quantity.Copy()
|
list[name] = *quantity.Copy()
|
||||||
} else {
|
} else {
|
||||||
@ -53,7 +55,9 @@ func maxResourceList(list, new v1.ResourceList) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PodRequestsAndLimits returns a dictionary of all defined resources summed up for all
|
// PodRequestsAndLimits returns a dictionary of all defined resources summed up for all
|
||||||
// containers of the pod.
|
// containers of the pod. If PodOverhead feature is enabled, pod overhead is added to the
|
||||||
|
// total container resource requests and to the total container limits which have a
|
||||||
|
// non-zero quantity.
|
||||||
func PodRequestsAndLimits(pod *v1.Pod) (reqs, limits v1.ResourceList) {
|
func PodRequestsAndLimits(pod *v1.Pod) (reqs, limits v1.ResourceList) {
|
||||||
reqs, limits = v1.ResourceList{}, v1.ResourceList{}
|
reqs, limits = v1.ResourceList{}, v1.ResourceList{}
|
||||||
for _, container := range pod.Spec.Containers {
|
for _, container := range pod.Spec.Containers {
|
||||||
@ -65,37 +69,79 @@ func PodRequestsAndLimits(pod *v1.Pod) (reqs, limits v1.ResourceList) {
|
|||||||
maxResourceList(reqs, container.Resources.Requests)
|
maxResourceList(reqs, container.Resources.Requests)
|
||||||
maxResourceList(limits, container.Resources.Limits)
|
maxResourceList(limits, container.Resources.Limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if PodOverhead feature is supported, add overhead for running a pod
|
||||||
|
// to the sum of reqeuests and to non-zero limits:
|
||||||
|
if pod.Spec.Overhead != nil && utilfeature.DefaultFeatureGate.Enabled(features.PodOverhead) {
|
||||||
|
addResourceList(reqs, pod.Spec.Overhead)
|
||||||
|
|
||||||
|
for name, quantity := range pod.Spec.Overhead {
|
||||||
|
if value, ok := limits[name]; ok && !value.IsZero() {
|
||||||
|
value.Add(quantity)
|
||||||
|
limits[name] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetResourceRequest finds and returns the request for a specific resource.
|
// GetResourceRequestQuantity finds and returns the request quantity for a specific resource.
|
||||||
|
func GetResourceRequestQuantity(pod *v1.Pod, resourceName v1.ResourceName) resource.Quantity {
|
||||||
|
requestQuantity := resource.Quantity{}
|
||||||
|
|
||||||
|
switch resourceName {
|
||||||
|
case v1.ResourceCPU:
|
||||||
|
requestQuantity = resource.Quantity{Format: resource.DecimalSI}
|
||||||
|
case v1.ResourceMemory, v1.ResourceStorage, v1.ResourceEphemeralStorage:
|
||||||
|
requestQuantity = resource.Quantity{Format: resource.BinarySI}
|
||||||
|
default:
|
||||||
|
requestQuantity = resource.Quantity{Format: resource.DecimalSI}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resourceName == v1.ResourceEphemeralStorage && !utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolation) {
|
||||||
|
// if the local storage capacity isolation feature gate is disabled, pods request 0 disk
|
||||||
|
return requestQuantity
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, container := range pod.Spec.Containers {
|
||||||
|
if rQuantity, ok := container.Resources.Requests[resourceName]; ok {
|
||||||
|
requestQuantity.Add(rQuantity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, container := range pod.Spec.InitContainers {
|
||||||
|
if rQuantity, ok := container.Resources.Requests[resourceName]; ok {
|
||||||
|
if requestQuantity.Cmp(rQuantity) < 0 {
|
||||||
|
requestQuantity = rQuantity.DeepCopy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if PodOverhead feature is supported, add overhead for running a pod
|
||||||
|
// to the total requests if the resource total is non-zero
|
||||||
|
if pod.Spec.Overhead != nil && utilfeature.DefaultFeatureGate.Enabled(features.PodOverhead) {
|
||||||
|
if podOverhead, ok := pod.Spec.Overhead[resourceName]; ok && !requestQuantity.IsZero() {
|
||||||
|
requestQuantity.Add(podOverhead)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestQuantity
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResourceRequest finds and returns the request value for a specific resource.
|
||||||
func GetResourceRequest(pod *v1.Pod, resource v1.ResourceName) int64 {
|
func GetResourceRequest(pod *v1.Pod, resource v1.ResourceName) int64 {
|
||||||
if resource == v1.ResourcePods {
|
if resource == v1.ResourcePods {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
totalResources := int64(0)
|
|
||||||
for _, container := range pod.Spec.Containers {
|
requestQuantity := GetResourceRequestQuantity(pod, resource)
|
||||||
if rQuantity, ok := container.Resources.Requests[resource]; ok {
|
|
||||||
if resource == v1.ResourceCPU {
|
if resource == v1.ResourceCPU {
|
||||||
totalResources += rQuantity.MilliValue()
|
return requestQuantity.MilliValue()
|
||||||
} else {
|
|
||||||
totalResources += rQuantity.Value()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// take max_resource(sum_pod, any_init_container)
|
|
||||||
for _, container := range pod.Spec.InitContainers {
|
return requestQuantity.Value()
|
||||||
if rQuantity, ok := container.Resources.Requests[resource]; ok {
|
|
||||||
if resource == v1.ResourceCPU {
|
|
||||||
if rQuantity.MilliValue() > totalResources {
|
|
||||||
totalResources = rQuantity.MilliValue()
|
|
||||||
}
|
|
||||||
} else if rQuantity.Value() > totalResources {
|
|
||||||
totalResources = rQuantity.Value()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return totalResources
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractResourceValueByContainerName extracts the value of a resource
|
// ExtractResourceValueByContainerName extracts the value of a resource
|
||||||
|
@ -22,7 +22,11 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResourceHelpers(t *testing.T) {
|
func TestResourceHelpers(t *testing.T) {
|
||||||
@ -64,27 +68,53 @@ func TestDefaultResourceHelpers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetResourceRequest(t *testing.T) {
|
func TestGetResourceRequest(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodOverhead, true)()
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
pod *v1.Pod
|
pod *v1.Pod
|
||||||
res v1.ResourceName
|
cName string
|
||||||
|
resourceName v1.ResourceName
|
||||||
expectedValue int64
|
expectedValue int64
|
||||||
expectedError error
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
pod: getPod("foo", "9", "", "", ""),
|
pod: getPod("foo", podResources{cpuRequest: "9"}),
|
||||||
res: v1.ResourceCPU,
|
resourceName: v1.ResourceCPU,
|
||||||
expectedValue: 9000,
|
expectedValue: 9000,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pod: getPod("foo", "", "", "90Mi", ""),
|
pod: getPod("foo", podResources{memoryRequest: "90Mi"}),
|
||||||
res: v1.ResourceMemory,
|
resourceName: v1.ResourceMemory,
|
||||||
expectedValue: 94371840,
|
expectedValue: 94371840,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
cName: "just-overhead for cpu",
|
||||||
|
pod: getPod("foo", podResources{cpuOverhead: "5", memoryOverhead: "5"}),
|
||||||
|
resourceName: v1.ResourceCPU,
|
||||||
|
expectedValue: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cName: "just-overhead for memory",
|
||||||
|
pod: getPod("foo", podResources{memoryOverhead: "5"}),
|
||||||
|
resourceName: v1.ResourceMemory,
|
||||||
|
expectedValue: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cName: "cpu overhead and req",
|
||||||
|
pod: getPod("foo", podResources{cpuRequest: "2", cpuOverhead: "5", memoryOverhead: "5"}),
|
||||||
|
resourceName: v1.ResourceCPU,
|
||||||
|
expectedValue: 7000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cName: "mem overhead and req",
|
||||||
|
pod: getPod("foo", podResources{cpuRequest: "2", memoryRequest: "1024", cpuOverhead: "5", memoryOverhead: "5"}),
|
||||||
|
resourceName: v1.ResourceMemory,
|
||||||
|
expectedValue: 1029,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
as := assert.New(t)
|
as := assert.New(t)
|
||||||
for idx, tc := range cases {
|
for idx, tc := range cases {
|
||||||
actual := GetResourceRequest(tc.pod, tc.res)
|
actual := GetResourceRequest(tc.pod, tc.resourceName)
|
||||||
as.Equal(actual, tc.expectedValue, "expected test case [%d] to return %q; got %q instead", idx, tc.expectedValue, actual)
|
as.Equal(actual, tc.expectedValue, "expected test case [%d] %v: to return %q; got %q instead", idx, tc.cName, tc.expectedValue, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +131,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Resource: "limits.cpu",
|
Resource: "limits.cpu",
|
||||||
},
|
},
|
||||||
cName: "foo",
|
cName: "foo",
|
||||||
pod: getPod("foo", "", "9", "", ""),
|
pod: getPod("foo", podResources{cpuLimit: "9"}),
|
||||||
expectedValue: "9",
|
expectedValue: "9",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -109,7 +139,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Resource: "requests.cpu",
|
Resource: "requests.cpu",
|
||||||
},
|
},
|
||||||
cName: "foo",
|
cName: "foo",
|
||||||
pod: getPod("foo", "", "", "", ""),
|
pod: getPod("foo", podResources{}),
|
||||||
expectedValue: "0",
|
expectedValue: "0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -117,7 +147,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Resource: "requests.cpu",
|
Resource: "requests.cpu",
|
||||||
},
|
},
|
||||||
cName: "foo",
|
cName: "foo",
|
||||||
pod: getPod("foo", "8", "", "", ""),
|
pod: getPod("foo", podResources{cpuRequest: "8"}),
|
||||||
expectedValue: "8",
|
expectedValue: "8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -125,7 +155,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Resource: "requests.cpu",
|
Resource: "requests.cpu",
|
||||||
},
|
},
|
||||||
cName: "foo",
|
cName: "foo",
|
||||||
pod: getPod("foo", "100m", "", "", ""),
|
pod: getPod("foo", podResources{cpuRequest: "100m"}),
|
||||||
expectedValue: "1",
|
expectedValue: "1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -134,7 +164,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Divisor: resource.MustParse("100m"),
|
Divisor: resource.MustParse("100m"),
|
||||||
},
|
},
|
||||||
cName: "foo",
|
cName: "foo",
|
||||||
pod: getPod("foo", "1200m", "", "", ""),
|
pod: getPod("foo", podResources{cpuRequest: "1200m"}),
|
||||||
expectedValue: "12",
|
expectedValue: "12",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -142,7 +172,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Resource: "requests.memory",
|
Resource: "requests.memory",
|
||||||
},
|
},
|
||||||
cName: "foo",
|
cName: "foo",
|
||||||
pod: getPod("foo", "", "", "100Mi", ""),
|
pod: getPod("foo", podResources{memoryRequest: "100Mi"}),
|
||||||
expectedValue: "104857600",
|
expectedValue: "104857600",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -151,7 +181,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Divisor: resource.MustParse("1Mi"),
|
Divisor: resource.MustParse("1Mi"),
|
||||||
},
|
},
|
||||||
cName: "foo",
|
cName: "foo",
|
||||||
pod: getPod("foo", "", "", "100Mi", "1Gi"),
|
pod: getPod("foo", podResources{memoryRequest: "100Mi", memoryLimit: "1Gi"}),
|
||||||
expectedValue: "100",
|
expectedValue: "100",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -159,7 +189,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Resource: "limits.memory",
|
Resource: "limits.memory",
|
||||||
},
|
},
|
||||||
cName: "foo",
|
cName: "foo",
|
||||||
pod: getPod("foo", "", "", "10Mi", "100Mi"),
|
pod: getPod("foo", podResources{memoryRequest: "10Mi", memoryLimit: "100Mi"}),
|
||||||
expectedValue: "104857600",
|
expectedValue: "104857600",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -167,7 +197,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Resource: "limits.cpu",
|
Resource: "limits.cpu",
|
||||||
},
|
},
|
||||||
cName: "init-foo",
|
cName: "init-foo",
|
||||||
pod: getPod("foo", "", "9", "", ""),
|
pod: getPod("foo", podResources{cpuLimit: "9"}),
|
||||||
expectedValue: "9",
|
expectedValue: "9",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -175,7 +205,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Resource: "requests.cpu",
|
Resource: "requests.cpu",
|
||||||
},
|
},
|
||||||
cName: "init-foo",
|
cName: "init-foo",
|
||||||
pod: getPod("foo", "", "", "", ""),
|
pod: getPod("foo", podResources{}),
|
||||||
expectedValue: "0",
|
expectedValue: "0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -183,7 +213,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Resource: "requests.cpu",
|
Resource: "requests.cpu",
|
||||||
},
|
},
|
||||||
cName: "init-foo",
|
cName: "init-foo",
|
||||||
pod: getPod("foo", "8", "", "", ""),
|
pod: getPod("foo", podResources{cpuRequest: "8"}),
|
||||||
expectedValue: "8",
|
expectedValue: "8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -191,7 +221,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Resource: "requests.cpu",
|
Resource: "requests.cpu",
|
||||||
},
|
},
|
||||||
cName: "init-foo",
|
cName: "init-foo",
|
||||||
pod: getPod("foo", "100m", "", "", ""),
|
pod: getPod("foo", podResources{cpuRequest: "100m"}),
|
||||||
expectedValue: "1",
|
expectedValue: "1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -200,7 +230,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Divisor: resource.MustParse("100m"),
|
Divisor: resource.MustParse("100m"),
|
||||||
},
|
},
|
||||||
cName: "init-foo",
|
cName: "init-foo",
|
||||||
pod: getPod("foo", "1200m", "", "", ""),
|
pod: getPod("foo", podResources{cpuRequest: "1200m"}),
|
||||||
expectedValue: "12",
|
expectedValue: "12",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -208,7 +238,7 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Resource: "requests.memory",
|
Resource: "requests.memory",
|
||||||
},
|
},
|
||||||
cName: "init-foo",
|
cName: "init-foo",
|
||||||
pod: getPod("foo", "", "", "100Mi", ""),
|
pod: getPod("foo", podResources{memoryRequest: "100Mi"}),
|
||||||
expectedValue: "104857600",
|
expectedValue: "104857600",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -217,15 +247,16 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
Divisor: resource.MustParse("1Mi"),
|
Divisor: resource.MustParse("1Mi"),
|
||||||
},
|
},
|
||||||
cName: "init-foo",
|
cName: "init-foo",
|
||||||
pod: getPod("foo", "", "", "100Mi", "1Gi"),
|
pod: getPod("foo", podResources{memoryRequest: "100Mi", memoryLimit: "1Gi"}),
|
||||||
expectedValue: "100",
|
expectedValue: "100",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fs: &v1.ResourceFieldSelector{
|
fs: &v1.ResourceFieldSelector{
|
||||||
Resource: "limits.memory",
|
Resource: "limits.memory",
|
||||||
},
|
},
|
||||||
cName: "init-foo",
|
cName: "init-foo",
|
||||||
pod: getPod("foo", "", "", "10Mi", "100Mi"),
|
pod: getPod("foo", podResources{memoryRequest: "10Mi", memoryLimit: "100Mi"}),
|
||||||
|
|
||||||
expectedValue: "104857600",
|
expectedValue: "104857600",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -241,37 +272,124 @@ func TestExtractResourceValue(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPod(cname, cpuRequest, cpuLimit, memoryRequest, memoryLimit string) *v1.Pod {
|
func TestPodRequestsAndLimits(t *testing.T) {
|
||||||
resources := v1.ResourceRequirements{
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodOverhead, true)()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
pod *v1.Pod
|
||||||
|
cName string
|
||||||
|
expectedRequests v1.ResourceList
|
||||||
|
expectedLimits v1.ResourceList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cName: "just-limit-no-overhead",
|
||||||
|
pod: getPod("foo", podResources{cpuLimit: "9"}),
|
||||||
|
expectedRequests: v1.ResourceList{},
|
||||||
|
expectedLimits: v1.ResourceList{
|
||||||
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("9"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cName: "just-overhead",
|
||||||
|
pod: getPod("foo", podResources{cpuOverhead: "5", memoryOverhead: "5"}),
|
||||||
|
expectedRequests: v1.ResourceList{
|
||||||
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("5"),
|
||||||
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("5"),
|
||||||
|
},
|
||||||
|
expectedLimits: v1.ResourceList{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cName: "req-and-overhead",
|
||||||
|
pod: getPod("foo", podResources{cpuRequest: "1", memoryRequest: "10", cpuOverhead: "5", memoryOverhead: "5"}),
|
||||||
|
expectedRequests: v1.ResourceList{
|
||||||
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("6"),
|
||||||
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("15"),
|
||||||
|
},
|
||||||
|
expectedLimits: v1.ResourceList{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cName: "all-req-lim-and-overhead",
|
||||||
|
pod: getPod("foo", podResources{cpuRequest: "1", cpuLimit: "2", memoryRequest: "10", memoryLimit: "12", cpuOverhead: "5", memoryOverhead: "5"}),
|
||||||
|
expectedRequests: v1.ResourceList{
|
||||||
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("6"),
|
||||||
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("15"),
|
||||||
|
},
|
||||||
|
expectedLimits: v1.ResourceList{
|
||||||
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("7"),
|
||||||
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("17"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cName: "req-some-lim-and-overhead",
|
||||||
|
pod: getPod("foo", podResources{cpuRequest: "1", cpuLimit: "2", memoryRequest: "10", cpuOverhead: "5", memoryOverhead: "5"}),
|
||||||
|
expectedRequests: v1.ResourceList{
|
||||||
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("6"),
|
||||||
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("15"),
|
||||||
|
},
|
||||||
|
expectedLimits: v1.ResourceList{
|
||||||
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("7"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for idx, tc := range cases {
|
||||||
|
resRequests, resLimits := PodRequestsAndLimits(tc.pod)
|
||||||
|
|
||||||
|
if !equality.Semantic.DeepEqual(tc.expectedRequests, resRequests) {
|
||||||
|
t.Errorf("test case failure[%d]: %v, requests:\n expected:\t%v\ngot\t\t%v", idx, tc.cName, tc.expectedRequests, resRequests)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !equality.Semantic.DeepEqual(tc.expectedLimits, resLimits) {
|
||||||
|
t.Errorf("test case failure[%d]: %v, limits:\n expected:\t%v\ngot\t\t%v", idx, tc.cName, tc.expectedLimits, resLimits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type podResources struct {
|
||||||
|
cpuRequest, cpuLimit, memoryRequest, memoryLimit, cpuOverhead, memoryOverhead string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPod(cname string, resources podResources) *v1.Pod {
|
||||||
|
r := v1.ResourceRequirements{
|
||||||
Limits: make(v1.ResourceList),
|
Limits: make(v1.ResourceList),
|
||||||
Requests: make(v1.ResourceList),
|
Requests: make(v1.ResourceList),
|
||||||
}
|
}
|
||||||
if cpuLimit != "" {
|
|
||||||
resources.Limits[v1.ResourceCPU] = resource.MustParse(cpuLimit)
|
overhead := make(v1.ResourceList)
|
||||||
|
|
||||||
|
if resources.cpuLimit != "" {
|
||||||
|
r.Limits[v1.ResourceCPU] = resource.MustParse(resources.cpuLimit)
|
||||||
}
|
}
|
||||||
if memoryLimit != "" {
|
if resources.memoryLimit != "" {
|
||||||
resources.Limits[v1.ResourceMemory] = resource.MustParse(memoryLimit)
|
r.Limits[v1.ResourceMemory] = resource.MustParse(resources.memoryLimit)
|
||||||
}
|
}
|
||||||
if cpuRequest != "" {
|
if resources.cpuRequest != "" {
|
||||||
resources.Requests[v1.ResourceCPU] = resource.MustParse(cpuRequest)
|
r.Requests[v1.ResourceCPU] = resource.MustParse(resources.cpuRequest)
|
||||||
}
|
}
|
||||||
if memoryRequest != "" {
|
if resources.memoryRequest != "" {
|
||||||
resources.Requests[v1.ResourceMemory] = resource.MustParse(memoryRequest)
|
r.Requests[v1.ResourceMemory] = resource.MustParse(resources.memoryRequest)
|
||||||
}
|
}
|
||||||
|
if resources.cpuOverhead != "" {
|
||||||
|
overhead[v1.ResourceCPU] = resource.MustParse(resources.cpuOverhead)
|
||||||
|
}
|
||||||
|
if resources.memoryOverhead != "" {
|
||||||
|
overhead[v1.ResourceMemory] = resource.MustParse(resources.memoryOverhead)
|
||||||
|
}
|
||||||
|
|
||||||
return &v1.Pod{
|
return &v1.Pod{
|
||||||
Spec: v1.PodSpec{
|
Spec: v1.PodSpec{
|
||||||
Containers: []v1.Container{
|
Containers: []v1.Container{
|
||||||
{
|
{
|
||||||
Name: cname,
|
Name: cname,
|
||||||
Resources: resources,
|
Resources: r,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
InitContainers: []v1.Container{
|
InitContainers: []v1.Container{
|
||||||
{
|
{
|
||||||
Name: "init-" + cname,
|
Name: "init-" + cname,
|
||||||
Resources: resources,
|
Resources: r,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Overhead: overhead,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,9 +25,8 @@ import (
|
|||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
v1resource "k8s.io/kubernetes/pkg/api/v1/resource"
|
||||||
statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
|
statsapi "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
|
||||||
evictionapi "k8s.io/kubernetes/pkg/kubelet/eviction/api"
|
evictionapi "k8s.io/kubernetes/pkg/kubelet/eviction/api"
|
||||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
@ -536,8 +535,8 @@ func exceedMemoryRequests(stats statsFunc) cmpFunc {
|
|||||||
|
|
||||||
p1Memory := memoryUsage(p1Stats.Memory)
|
p1Memory := memoryUsage(p1Stats.Memory)
|
||||||
p2Memory := memoryUsage(p2Stats.Memory)
|
p2Memory := memoryUsage(p2Stats.Memory)
|
||||||
p1ExceedsRequests := p1Memory.Cmp(podRequest(p1, v1.ResourceMemory)) == 1
|
p1ExceedsRequests := p1Memory.Cmp(v1resource.GetResourceRequestQuantity(p1, v1.ResourceMemory)) == 1
|
||||||
p2ExceedsRequests := p2Memory.Cmp(podRequest(p2, v1.ResourceMemory)) == 1
|
p2ExceedsRequests := p2Memory.Cmp(v1resource.GetResourceRequestQuantity(p2, v1.ResourceMemory)) == 1
|
||||||
// prioritize evicting the pod which exceeds its requests
|
// prioritize evicting the pod which exceeds its requests
|
||||||
return cmpBool(p1ExceedsRequests, p2ExceedsRequests)
|
return cmpBool(p1ExceedsRequests, p2ExceedsRequests)
|
||||||
}
|
}
|
||||||
@ -555,11 +554,11 @@ func memory(stats statsFunc) cmpFunc {
|
|||||||
|
|
||||||
// adjust p1, p2 usage relative to the request (if any)
|
// adjust p1, p2 usage relative to the request (if any)
|
||||||
p1Memory := memoryUsage(p1Stats.Memory)
|
p1Memory := memoryUsage(p1Stats.Memory)
|
||||||
p1Request := podRequest(p1, v1.ResourceMemory)
|
p1Request := v1resource.GetResourceRequestQuantity(p1, v1.ResourceMemory)
|
||||||
p1Memory.Sub(p1Request)
|
p1Memory.Sub(p1Request)
|
||||||
|
|
||||||
p2Memory := memoryUsage(p2Stats.Memory)
|
p2Memory := memoryUsage(p2Stats.Memory)
|
||||||
p2Request := podRequest(p2, v1.ResourceMemory)
|
p2Request := v1resource.GetResourceRequestQuantity(p2, v1.ResourceMemory)
|
||||||
p2Memory.Sub(p2Request)
|
p2Memory.Sub(p2Request)
|
||||||
|
|
||||||
// prioritize evicting the pod which has the larger consumption of memory
|
// prioritize evicting the pod which has the larger consumption of memory
|
||||||
@ -567,41 +566,6 @@ func memory(stats statsFunc) cmpFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// podRequest returns the total resource request of a pod which is the
|
|
||||||
// max(max of init container requests, sum of container requests)
|
|
||||||
func podRequest(pod *v1.Pod, resourceName v1.ResourceName) resource.Quantity {
|
|
||||||
containerValue := resource.Quantity{Format: resource.BinarySI}
|
|
||||||
if resourceName == v1.ResourceEphemeralStorage && !utilfeature.DefaultFeatureGate.Enabled(features.LocalStorageCapacityIsolation) {
|
|
||||||
// if the local storage capacity isolation feature gate is disabled, pods request 0 disk
|
|
||||||
return containerValue
|
|
||||||
}
|
|
||||||
for i := range pod.Spec.Containers {
|
|
||||||
switch resourceName {
|
|
||||||
case v1.ResourceMemory:
|
|
||||||
containerValue.Add(*pod.Spec.Containers[i].Resources.Requests.Memory())
|
|
||||||
case v1.ResourceEphemeralStorage:
|
|
||||||
containerValue.Add(*pod.Spec.Containers[i].Resources.Requests.StorageEphemeral())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initValue := resource.Quantity{Format: resource.BinarySI}
|
|
||||||
for i := range pod.Spec.InitContainers {
|
|
||||||
switch resourceName {
|
|
||||||
case v1.ResourceMemory:
|
|
||||||
if initValue.Cmp(*pod.Spec.InitContainers[i].Resources.Requests.Memory()) < 0 {
|
|
||||||
initValue = *pod.Spec.InitContainers[i].Resources.Requests.Memory()
|
|
||||||
}
|
|
||||||
case v1.ResourceEphemeralStorage:
|
|
||||||
if initValue.Cmp(*pod.Spec.InitContainers[i].Resources.Requests.StorageEphemeral()) < 0 {
|
|
||||||
initValue = *pod.Spec.InitContainers[i].Resources.Requests.StorageEphemeral()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if containerValue.Cmp(initValue) > 0 {
|
|
||||||
return containerValue
|
|
||||||
}
|
|
||||||
return initValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// exceedDiskRequests compares whether or not pods' disk usage exceeds their requests
|
// exceedDiskRequests compares whether or not pods' disk usage exceeds their requests
|
||||||
func exceedDiskRequests(stats statsFunc, fsStatsToMeasure []fsStatsType, diskResource v1.ResourceName) cmpFunc {
|
func exceedDiskRequests(stats statsFunc, fsStatsToMeasure []fsStatsType, diskResource v1.ResourceName) cmpFunc {
|
||||||
return func(p1, p2 *v1.Pod) int {
|
return func(p1, p2 *v1.Pod) int {
|
||||||
@ -621,8 +585,8 @@ func exceedDiskRequests(stats statsFunc, fsStatsToMeasure []fsStatsType, diskRes
|
|||||||
|
|
||||||
p1Disk := p1Usage[diskResource]
|
p1Disk := p1Usage[diskResource]
|
||||||
p2Disk := p2Usage[diskResource]
|
p2Disk := p2Usage[diskResource]
|
||||||
p1ExceedsRequests := p1Disk.Cmp(podRequest(p1, diskResource)) == 1
|
p1ExceedsRequests := p1Disk.Cmp(v1resource.GetResourceRequestQuantity(p1, diskResource)) == 1
|
||||||
p2ExceedsRequests := p2Disk.Cmp(podRequest(p2, diskResource)) == 1
|
p2ExceedsRequests := p2Disk.Cmp(v1resource.GetResourceRequestQuantity(p2, diskResource)) == 1
|
||||||
// prioritize evicting the pod which exceeds its requests
|
// prioritize evicting the pod which exceeds its requests
|
||||||
return cmpBool(p1ExceedsRequests, p2ExceedsRequests)
|
return cmpBool(p1ExceedsRequests, p2ExceedsRequests)
|
||||||
}
|
}
|
||||||
@ -647,9 +611,9 @@ func disk(stats statsFunc, fsStatsToMeasure []fsStatsType, diskResource v1.Resou
|
|||||||
// adjust p1, p2 usage relative to the request (if any)
|
// adjust p1, p2 usage relative to the request (if any)
|
||||||
p1Disk := p1Usage[diskResource]
|
p1Disk := p1Usage[diskResource]
|
||||||
p2Disk := p2Usage[diskResource]
|
p2Disk := p2Usage[diskResource]
|
||||||
p1Request := podRequest(p1, v1.ResourceEphemeralStorage)
|
p1Request := v1resource.GetResourceRequestQuantity(p1, v1.ResourceEphemeralStorage)
|
||||||
p1Disk.Sub(p1Request)
|
p1Disk.Sub(p1Request)
|
||||||
p2Request := podRequest(p2, v1.ResourceEphemeralStorage)
|
p2Request := v1resource.GetResourceRequestQuantity(p2, v1.ResourceEphemeralStorage)
|
||||||
p2Disk.Sub(p2Request)
|
p2Disk.Sub(p2Request)
|
||||||
// prioritize evicting the pod which has the larger consumption of disk
|
// prioritize evicting the pod which has the larger consumption of disk
|
||||||
return p2Disk.Cmp(p1Disk)
|
return p2Disk.Cmp(p1Disk)
|
||||||
|
Loading…
Reference in New Issue
Block a user