mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 17:30:00 +00:00
Merge pull request #129216 from tallclair/ippr-supported
[FG:InPlacePodVerticalScaling] Never attempt a resize of windows pods and always use allocated resources for unsupported resize pods
This commit is contained in:
commit
f816be06ed
@ -1883,7 +1883,7 @@ func (kl *Kubelet) SyncPod(ctx context.Context, updateType kubetypes.SyncPodType
|
|||||||
// handlePodResourcesResize updates the pod to use the allocated resources. This should come
|
// handlePodResourcesResize updates the pod to use the allocated resources. This should come
|
||||||
// before the main business logic of SyncPod, so that a consistent view of the pod is used
|
// before the main business logic of SyncPod, so that a consistent view of the pod is used
|
||||||
// across the sync loop.
|
// across the sync loop.
|
||||||
if kuberuntime.IsInPlacePodVerticalScalingAllowed(pod) {
|
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||||
// Handle pod resize here instead of doing it in HandlePodUpdates because
|
// Handle pod resize here instead of doing it in HandlePodUpdates because
|
||||||
// this conveniently retries any Deferred resize requests
|
// this conveniently retries any Deferred resize requests
|
||||||
// TODO(vinaykul,InPlacePodVerticalScaling): Investigate doing this in HandlePodUpdates + periodic SyncLoop scan
|
// TODO(vinaykul,InPlacePodVerticalScaling): Investigate doing this in HandlePodUpdates + periodic SyncLoop scan
|
||||||
@ -2816,10 +2816,6 @@ func (kl *Kubelet) HandlePodSyncs(pods []*v1.Pod) {
|
|||||||
// pod should hold the desired (pre-allocated) spec.
|
// pod should hold the desired (pre-allocated) spec.
|
||||||
// Returns true if the resize can proceed.
|
// Returns true if the resize can proceed.
|
||||||
func (kl *Kubelet) canResizePod(pod *v1.Pod) (bool, v1.PodResizeStatus, string) {
|
func (kl *Kubelet) canResizePod(pod *v1.Pod) (bool, v1.PodResizeStatus, string) {
|
||||||
if goos == "windows" {
|
|
||||||
return false, v1.PodResizeStatusInfeasible, "Resizing Windows pods is not supported"
|
|
||||||
}
|
|
||||||
|
|
||||||
if v1qos.GetPodQOS(pod) == v1.PodQOSGuaranteed && !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScalingExclusiveCPUs) {
|
if v1qos.GetPodQOS(pod) == v1.PodQOSGuaranteed && !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScalingExclusiveCPUs) {
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.CPUManager) {
|
if utilfeature.DefaultFeatureGate.Enabled(features.CPUManager) {
|
||||||
if kl.containerManager.GetNodeConfig().CPUManagerPolicy == "static" {
|
if kl.containerManager.GetNodeConfig().CPUManagerPolicy == "static" {
|
||||||
@ -2877,6 +2873,7 @@ func (kl *Kubelet) canResizePod(pod *v1.Pod) (bool, v1.PodResizeStatus, string)
|
|||||||
// the allocation decision and pod status.
|
// the allocation decision and pod status.
|
||||||
func (kl *Kubelet) handlePodResourcesResize(pod *v1.Pod, podStatus *kubecontainer.PodStatus) (*v1.Pod, error) {
|
func (kl *Kubelet) handlePodResourcesResize(pod *v1.Pod, podStatus *kubecontainer.PodStatus) (*v1.Pod, error) {
|
||||||
allocatedPod, updated := kl.allocationManager.UpdatePodFromAllocation(pod)
|
allocatedPod, updated := kl.allocationManager.UpdatePodFromAllocation(pod)
|
||||||
|
|
||||||
if !updated {
|
if !updated {
|
||||||
// Desired resources == allocated resources. Check whether a resize is in progress.
|
// Desired resources == allocated resources. Check whether a resize is in progress.
|
||||||
resizeInProgress := !allocatedResourcesMatchStatus(allocatedPod, podStatus)
|
resizeInProgress := !allocatedResourcesMatchStatus(allocatedPod, podStatus)
|
||||||
@ -2889,6 +2886,11 @@ func (kl *Kubelet) handlePodResourcesResize(pod *v1.Pod, podStatus *kubecontaine
|
|||||||
}
|
}
|
||||||
// Pod allocation does not need to be updated.
|
// Pod allocation does not need to be updated.
|
||||||
return allocatedPod, nil
|
return allocatedPod, nil
|
||||||
|
} else if resizable, msg := kuberuntime.IsInPlacePodVerticalScalingAllowed(pod); !resizable {
|
||||||
|
// If there is a pending resize but the resize is not allowed, always use the allocated resources.
|
||||||
|
kl.recorder.Eventf(pod, v1.EventTypeWarning, events.ResizeInfeasible, msg)
|
||||||
|
kl.statusManager.SetPodResizeStatus(pod.UID, v1.PodResizeStatusInfeasible)
|
||||||
|
return allocatedPod, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
kl.podResizeMutex.Lock()
|
kl.podResizeMutex.Lock()
|
||||||
|
@ -1748,10 +1748,6 @@ func getPhase(pod *v1.Pod, info []v1.ContainerStatus, podIsTerminal bool) v1.Pod
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (kl *Kubelet) determinePodResizeStatus(allocatedPod *v1.Pod, podStatus *kubecontainer.PodStatus, podIsTerminal bool) v1.PodResizeStatus {
|
func (kl *Kubelet) determinePodResizeStatus(allocatedPod *v1.Pod, podStatus *kubecontainer.PodStatus, podIsTerminal bool) v1.PodResizeStatus {
|
||||||
if kubetypes.IsStaticPod(allocatedPod) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// If pod is terminal, clear the resize status.
|
// If pod is terminal, clear the resize status.
|
||||||
if podIsTerminal {
|
if podIsTerminal {
|
||||||
kl.statusManager.SetPodResizeStatus(allocatedPod.UID, "")
|
kl.statusManager.SetPodResizeStatus(allocatedPod.UID, "")
|
||||||
|
@ -2711,7 +2711,7 @@ func TestHandlePodResourcesResize(t *testing.T) {
|
|||||||
expectedAllocatedLims v1.ResourceList
|
expectedAllocatedLims v1.ResourceList
|
||||||
expectedResize v1.PodResizeStatus
|
expectedResize v1.PodResizeStatus
|
||||||
expectBackoffReset bool
|
expectBackoffReset bool
|
||||||
goos string
|
annotations map[string]string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Request CPU and memory decrease - expect InProgress",
|
name: "Request CPU and memory decrease - expect InProgress",
|
||||||
@ -2781,12 +2781,12 @@ func TestHandlePodResourcesResize(t *testing.T) {
|
|||||||
expectedResize: "",
|
expectedResize: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "windows node, expect Infeasible",
|
name: "static pod, expect Infeasible",
|
||||||
originalRequests: v1.ResourceList{v1.ResourceCPU: cpu1000m, v1.ResourceMemory: mem1000M},
|
originalRequests: v1.ResourceList{v1.ResourceCPU: cpu1000m, v1.ResourceMemory: mem1000M},
|
||||||
newRequests: v1.ResourceList{v1.ResourceCPU: cpu500m, v1.ResourceMemory: mem500M},
|
newRequests: v1.ResourceList{v1.ResourceCPU: cpu500m, v1.ResourceMemory: mem500M},
|
||||||
expectedAllocatedReqs: v1.ResourceList{v1.ResourceCPU: cpu1000m, v1.ResourceMemory: mem1000M},
|
expectedAllocatedReqs: v1.ResourceList{v1.ResourceCPU: cpu1000m, v1.ResourceMemory: mem1000M},
|
||||||
expectedResize: v1.PodResizeStatusInfeasible,
|
expectedResize: v1.PodResizeStatusInfeasible,
|
||||||
goos: "windows",
|
annotations: map[string]string{kubetypes.ConfigSourceAnnotationKey: kubetypes.FileSource},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Increase CPU from min shares",
|
name: "Increase CPU from min shares",
|
||||||
@ -2873,11 +2873,6 @@ func TestHandlePodResourcesResize(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
for _, isSidecarContainer := range []bool{false, true} {
|
for _, isSidecarContainer := range []bool{false, true} {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
oldGOOS := goos
|
|
||||||
defer func() { goos = oldGOOS }()
|
|
||||||
if tt.goos != "" {
|
|
||||||
goos = tt.goos
|
|
||||||
}
|
|
||||||
kubelet.statusManager = status.NewFakeManager()
|
kubelet.statusManager = status.NewFakeManager()
|
||||||
|
|
||||||
var originalPod *v1.Pod
|
var originalPod *v1.Pod
|
||||||
@ -2889,6 +2884,7 @@ func TestHandlePodResourcesResize(t *testing.T) {
|
|||||||
originalPod = testPod1.DeepCopy()
|
originalPod = testPod1.DeepCopy()
|
||||||
originalCtr = &originalPod.Spec.Containers[0]
|
originalCtr = &originalPod.Spec.Containers[0]
|
||||||
}
|
}
|
||||||
|
originalPod.Annotations = tt.annotations
|
||||||
originalCtr.Resources.Requests = tt.originalRequests
|
originalCtr.Resources.Requests = tt.originalRequests
|
||||||
originalCtr.Resources.Limits = tt.originalLimits
|
originalCtr.Resources.Limits = tt.originalLimits
|
||||||
|
|
||||||
|
37
pkg/kubelet/kuberuntime/features_linux.go
Normal file
37
pkg/kubelet/kuberuntime/features_linux.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2025 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kuberuntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsInPlacePodVerticalScalingAllowed(pod *v1.Pod) (allowed bool, msg string) {
|
||||||
|
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
||||||
|
return false, "InPlacePodVerticalScaling is disabled"
|
||||||
|
}
|
||||||
|
if kubetypes.IsStaticPod(pod) {
|
||||||
|
return false, "In-place resize of static-pods is not supported"
|
||||||
|
}
|
||||||
|
return true, ""
|
||||||
|
}
|
26
pkg/kubelet/kuberuntime/features_unsupported.go
Normal file
26
pkg/kubelet/kuberuntime/features_unsupported.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//go:build !linux && !windows
|
||||||
|
// +build !linux,!windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2025 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kuberuntime
|
||||||
|
|
||||||
|
import v1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
|
func IsInPlacePodVerticalScalingAllowed(_ *v1.Pod) (allowed bool, msg string) {
|
||||||
|
return false, "In-place pod resize is not supported on this node"
|
||||||
|
}
|
26
pkg/kubelet/kuberuntime/features_windows.go
Normal file
26
pkg/kubelet/kuberuntime/features_windows.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2025 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kuberuntime
|
||||||
|
|
||||||
|
import v1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
|
func IsInPlacePodVerticalScalingAllowed(_ *v1.Pod) (allowed bool, msg string) {
|
||||||
|
return false, "In-place pod resize is not supported on Windows"
|
||||||
|
}
|
@ -1164,7 +1164,7 @@ func (m *kubeGenericRuntimeManager) computeInitContainerActions(pod *v1.Pod, pod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if IsInPlacePodVerticalScalingAllowed(pod) && !m.computePodResizeAction(pod, i, true, status, changes) {
|
if !m.computePodResizeAction(pod, i, true, status, changes) {
|
||||||
// computePodResizeAction updates 'changes' if resize policy requires restarting this container
|
// computePodResizeAction updates 'changes' if resize policy requires restarting this container
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -550,20 +550,14 @@ func containerSucceeded(c *v1.Container, podStatus *kubecontainer.PodStatus) boo
|
|||||||
return cStatus.State == kubecontainer.ContainerStateExited && cStatus.ExitCode == 0
|
return cStatus.State == kubecontainer.ContainerStateExited && cStatus.ExitCode == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsInPlacePodVerticalScalingAllowed(pod *v1.Pod) bool {
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if types.IsStaticPod(pod) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// computePodResizeAction determines the actions required (if any) to resize the given container.
|
// computePodResizeAction determines the actions required (if any) to resize the given container.
|
||||||
// Returns whether to keep (true) or restart (false) the container.
|
// Returns whether to keep (true) or restart (false) the container.
|
||||||
// TODO(vibansal): Make this function to be agnostic to whether it is dealing with a restartable init container or not (i.e. remove the argument `isRestartableInitContainer`).
|
// TODO(vibansal): Make this function to be agnostic to whether it is dealing with a restartable init container or not (i.e. remove the argument `isRestartableInitContainer`).
|
||||||
func (m *kubeGenericRuntimeManager) computePodResizeAction(pod *v1.Pod, containerIdx int, isRestartableInitContainer bool, kubeContainerStatus *kubecontainer.Status, changes *podActions) (keepContainer bool) {
|
func (m *kubeGenericRuntimeManager) computePodResizeAction(pod *v1.Pod, containerIdx int, isRestartableInitContainer bool, kubeContainerStatus *kubecontainer.Status, changes *podActions) (keepContainer bool) {
|
||||||
|
if resizable, _ := IsInPlacePodVerticalScalingAllowed(pod); !resizable {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
var container v1.Container
|
var container v1.Container
|
||||||
if isRestartableInitContainer {
|
if isRestartableInitContainer {
|
||||||
container = pod.Spec.InitContainers[containerIdx]
|
container = pod.Spec.InitContainers[containerIdx]
|
||||||
@ -997,7 +991,7 @@ func (m *kubeGenericRuntimeManager) computePodActions(ctx context.Context, pod *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if IsInPlacePodVerticalScalingAllowed(pod) {
|
if resizable, _ := IsInPlacePodVerticalScalingAllowed(pod); resizable {
|
||||||
changes.ContainersToUpdate = make(map[v1.ResourceName][]containerToUpdateInfo)
|
changes.ContainersToUpdate = make(map[v1.ResourceName][]containerToUpdateInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1092,7 +1086,7 @@ func (m *kubeGenericRuntimeManager) computePodActions(ctx context.Context, pod *
|
|||||||
// If the container failed the startup probe, we should kill it.
|
// If the container failed the startup probe, we should kill it.
|
||||||
message = fmt.Sprintf("Container %s failed startup probe", container.Name)
|
message = fmt.Sprintf("Container %s failed startup probe", container.Name)
|
||||||
reason = reasonStartupProbe
|
reason = reasonStartupProbe
|
||||||
} else if IsInPlacePodVerticalScalingAllowed(pod) && !m.computePodResizeAction(pod, idx, false, containerStatus, &changes) {
|
} else if !m.computePodResizeAction(pod, idx, false, containerStatus, &changes) {
|
||||||
// computePodResizeAction updates 'changes' if resize policy requires restarting this container
|
// computePodResizeAction updates 'changes' if resize policy requires restarting this container
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
@ -1413,7 +1407,7 @@ func (m *kubeGenericRuntimeManager) SyncPod(ctx context.Context, pod *v1.Pod, po
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 7: For containers in podContainerChanges.ContainersToUpdate[CPU,Memory] list, invoke UpdateContainerResources
|
// Step 7: For containers in podContainerChanges.ContainersToUpdate[CPU,Memory] list, invoke UpdateContainerResources
|
||||||
if IsInPlacePodVerticalScalingAllowed(pod) {
|
if resizable, _ := IsInPlacePodVerticalScalingAllowed(pod); resizable {
|
||||||
if len(podContainerChanges.ContainersToUpdate) > 0 || podContainerChanges.UpdatePodResources {
|
if len(podContainerChanges.ContainersToUpdate) > 0 || podContainerChanges.UpdatePodResources {
|
||||||
m.doPodResizeAction(pod, podContainerChanges, &result)
|
m.doPodResizeAction(pod, podContainerChanges, &result)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user