mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
Merge pull request #59681 from mtaufen/kc-empty-eviction-hard
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Ignore 0% and 100% eviction thresholds Primarily, this gives a way to explicitly disable eviction, which is necessary to use omitempty on EvictionHard. See: https://github.com/kubernetes/kubernetes/pull/53833#discussion_r166672137 As justification for this approach, neither 0% nor 100% make sense as eviction thresholds; in the "less-than" case, you can't have less than 0% of a resource and 100% perpetually evicts; in the "greater-than" case (assuming we ever add a resource with this semantic), the reasoning is the reverse (not more than 100%, 0% perpetually evicts). ```release-note Eviction thresholds set to 0% or 100% are now ignored. ```
This commit is contained in:
commit
9de5839944
@ -209,10 +209,10 @@ type KubeletConfiguration struct {
|
|||||||
SerializeImagePulls *bool `json:"serializeImagePulls"`
|
SerializeImagePulls *bool `json:"serializeImagePulls"`
|
||||||
// Map of signal names to quantities that defines hard eviction thresholds. For example: {"memory.available": "300Mi"}.
|
// Map of signal names to quantities that defines hard eviction thresholds. For example: {"memory.available": "300Mi"}.
|
||||||
// +optional
|
// +optional
|
||||||
EvictionHard map[string]string `json:"evictionHard"`
|
EvictionHard map[string]string `json:"evictionHard,omitempty"`
|
||||||
// Map of signal names to quantities that defines soft eviction thresholds. For example: {"memory.available": "300Mi"}.
|
// Map of signal names to quantities that defines soft eviction thresholds. For example: {"memory.available": "300Mi"}.
|
||||||
// +optional
|
// +optional
|
||||||
EvictionSoft map[string]string `json:"evictionSoft"`
|
EvictionSoft map[string]string `json:"evictionSoft,omitempty"`
|
||||||
// Map of signal names to quantities that defines grace periods for each soft eviction signal. For example: {"memory.available": "30s"}.
|
// Map of signal names to quantities that defines grace periods for each soft eviction signal. For example: {"memory.available": "30s"}.
|
||||||
// +optional
|
// +optional
|
||||||
EvictionSoftGracePeriod map[string]string `json:"evictionSoftGracePeriod"`
|
EvictionSoftGracePeriod map[string]string `json:"evictionSoftGracePeriod"`
|
||||||
|
@ -107,7 +107,6 @@ func ParseThresholdConfig(allocatableConfig []string, evictionHard, evictionSoft
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
results = append(results, hardThresholds...)
|
results = append(results, hardThresholds...)
|
||||||
|
|
||||||
softThresholds, err := parseThresholdStatements(evictionSoft)
|
softThresholds, err := parseThresholdStatements(evictionSoft)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -151,26 +150,36 @@ func parseThresholdStatements(statements map[string]string) ([]evictionapi.Thres
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
results = append(results, result)
|
if result != nil {
|
||||||
|
results = append(results, *result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseThresholdStatement parses a threshold statement.
|
// parseThresholdStatement parses a threshold statement and returns a threshold,
|
||||||
func parseThresholdStatement(signal evictionapi.Signal, val string) (evictionapi.Threshold, error) {
|
// or nil if the threshold should be ignored.
|
||||||
|
func parseThresholdStatement(signal evictionapi.Signal, val string) (*evictionapi.Threshold, error) {
|
||||||
if !validSignal(signal) {
|
if !validSignal(signal) {
|
||||||
return evictionapi.Threshold{}, fmt.Errorf(unsupportedEvictionSignal, signal)
|
return nil, fmt.Errorf(unsupportedEvictionSignal, signal)
|
||||||
}
|
}
|
||||||
operator := evictionapi.OpForSignal[signal]
|
operator := evictionapi.OpForSignal[signal]
|
||||||
if strings.HasSuffix(val, "%") {
|
if strings.HasSuffix(val, "%") {
|
||||||
|
// ignore 0% and 100%
|
||||||
|
if val == "0%" || val == "100%" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
percentage, err := parsePercentage(val)
|
percentage, err := parsePercentage(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return evictionapi.Threshold{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if percentage <= 0 {
|
if percentage < 0 {
|
||||||
return evictionapi.Threshold{}, fmt.Errorf("eviction percentage threshold %v must be positive: %s", signal, val)
|
return nil, fmt.Errorf("eviction percentage threshold %v must be >= 0%%: %s", signal, val)
|
||||||
}
|
}
|
||||||
return evictionapi.Threshold{
|
if percentage > 100 {
|
||||||
|
return nil, fmt.Errorf("eviction percentage threshold %v must be <= 100%%: %s", signal, val)
|
||||||
|
}
|
||||||
|
return &evictionapi.Threshold{
|
||||||
Signal: signal,
|
Signal: signal,
|
||||||
Operator: operator,
|
Operator: operator,
|
||||||
Value: evictionapi.ThresholdValue{
|
Value: evictionapi.ThresholdValue{
|
||||||
@ -180,12 +189,12 @@ func parseThresholdStatement(signal evictionapi.Signal, val string) (evictionapi
|
|||||||
}
|
}
|
||||||
quantity, err := resource.ParseQuantity(val)
|
quantity, err := resource.ParseQuantity(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return evictionapi.Threshold{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if quantity.Sign() < 0 || quantity.IsZero() {
|
if quantity.Sign() < 0 || quantity.IsZero() {
|
||||||
return evictionapi.Threshold{}, fmt.Errorf("eviction threshold %v must be positive: %s", signal, &quantity)
|
return nil, fmt.Errorf("eviction threshold %v must be positive: %s", signal, &quantity)
|
||||||
}
|
}
|
||||||
return evictionapi.Threshold{
|
return &evictionapi.Threshold{
|
||||||
Signal: signal,
|
Signal: signal,
|
||||||
Operator: operator,
|
Operator: operator,
|
||||||
Value: evictionapi.ThresholdValue{
|
Value: evictionapi.ThresholdValue{
|
||||||
|
@ -288,6 +288,20 @@ func TestParseThresholdConfig(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"disable via 0%": {
|
||||||
|
allocatableConfig: []string{},
|
||||||
|
evictionHard: map[string]string{"memory.available": "0%"},
|
||||||
|
evictionSoft: map[string]string{"memory.available": "0%"},
|
||||||
|
expectErr: false,
|
||||||
|
expectThresholds: []evictionapi.Threshold{},
|
||||||
|
},
|
||||||
|
"disable via 100%": {
|
||||||
|
allocatableConfig: []string{},
|
||||||
|
evictionHard: map[string]string{"memory.available": "100%"},
|
||||||
|
evictionSoft: map[string]string{"memory.available": "100%"},
|
||||||
|
expectErr: false,
|
||||||
|
expectThresholds: []evictionapi.Threshold{},
|
||||||
|
},
|
||||||
"invalid-signal": {
|
"invalid-signal": {
|
||||||
allocatableConfig: []string{},
|
allocatableConfig: []string{},
|
||||||
evictionHard: map[string]string{"mem.available": "150Mi"},
|
evictionHard: map[string]string{"mem.available": "150Mi"},
|
||||||
|
@ -170,7 +170,8 @@ var _ = framework.KubeDescribe("LocalStorageSoftEviction [Slow] [Serial] [Disrup
|
|||||||
initialConfig.EvictionMaxPodGracePeriod = 30
|
initialConfig.EvictionMaxPodGracePeriod = 30
|
||||||
initialConfig.EvictionMinimumReclaim = map[string]string{}
|
initialConfig.EvictionMinimumReclaim = map[string]string{}
|
||||||
// Ensure that pods are not evicted because of the eviction-hard threshold
|
// Ensure that pods are not evicted because of the eviction-hard threshold
|
||||||
initialConfig.EvictionHard = map[string]string{}
|
// setting a threshold to 0% disables; non-empty map overrides default value (necessary due to omitempty)
|
||||||
|
initialConfig.EvictionHard = map[string]string{"memory.available": "0%"}
|
||||||
})
|
})
|
||||||
runEvictionTest(f, pressureTimeout, expectedNodeCondition, logDiskMetrics, []podEvictSpec{
|
runEvictionTest(f, pressureTimeout, expectedNodeCondition, logDiskMetrics, []podEvictSpec{
|
||||||
{
|
{
|
||||||
@ -192,7 +193,8 @@ var _ = framework.KubeDescribe("LocalStorageCapacityIsolationEviction [Slow] [Se
|
|||||||
Context(fmt.Sprintf(testContextFmt, "evictions due to pod local storage violations"), func() {
|
Context(fmt.Sprintf(testContextFmt, "evictions due to pod local storage violations"), func() {
|
||||||
tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) {
|
tempSetCurrentKubeletConfig(f, func(initialConfig *kubeletconfig.KubeletConfiguration) {
|
||||||
initialConfig.FeatureGates[string(features.LocalStorageCapacityIsolation)] = true
|
initialConfig.FeatureGates[string(features.LocalStorageCapacityIsolation)] = true
|
||||||
initialConfig.EvictionHard = map[string]string{}
|
// setting a threshold to 0% disables; non-empty map overrides default value (necessary due to omitempty)
|
||||||
|
initialConfig.EvictionHard = map[string]string{"memory.available": "0%"}
|
||||||
})
|
})
|
||||||
sizeLimit := resource.MustParse("100Mi")
|
sizeLimit := resource.MustParse("100Mi")
|
||||||
useOverLimit := 101 /* Mb */
|
useOverLimit := 101 /* Mb */
|
||||||
|
Loading…
Reference in New Issue
Block a user