k8s.io/kubernetes@v1.29.3/pkg/kubelet/eviction/memory_threshold_notifier.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package eviction 18 19 import ( 20 "fmt" 21 "time" 22 23 "k8s.io/klog/v2" 24 25 "k8s.io/apimachinery/pkg/api/resource" 26 statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1" 27 "k8s.io/kubernetes/pkg/kubelet/cm" 28 evictionapi "k8s.io/kubernetes/pkg/kubelet/eviction/api" 29 ) 30 31 const ( 32 memoryUsageAttribute = "memory.usage_in_bytes" 33 // this prevents constantly updating the memcg notifier if synchronize 34 // is run frequently. 35 notifierRefreshInterval = 10 * time.Second 36 ) 37 38 type memoryThresholdNotifier struct { 39 threshold evictionapi.Threshold 40 cgroupPath string 41 events chan struct{} 42 factory NotifierFactory 43 handler func(string) 44 notifier CgroupNotifier 45 } 46 47 var _ ThresholdNotifier = &memoryThresholdNotifier{} 48 49 // NewMemoryThresholdNotifier creates a ThresholdNotifier which is designed to respond to the given threshold. 50 // UpdateThreshold must be called once before the threshold will be active. 51 func NewMemoryThresholdNotifier(threshold evictionapi.Threshold, cgroupRoot string, factory NotifierFactory, handler func(string)) (ThresholdNotifier, error) { 52 cgroups, err := cm.GetCgroupSubsystems() 53 if err != nil { 54 return nil, err 55 } 56 cgpath, found := cgroups.MountPoints["memory"] 57 if !found || len(cgpath) == 0 { 58 return nil, fmt.Errorf("memory cgroup mount point not found") 59 } 60 if isAllocatableEvictionThreshold(threshold) { 61 // for allocatable thresholds, point the cgroup notifier at the allocatable cgroup 62 cgpath += cgroupRoot 63 } 64 return &memoryThresholdNotifier{ 65 threshold: threshold, 66 cgroupPath: cgpath, 67 events: make(chan struct{}), 68 handler: handler, 69 factory: factory, 70 }, nil 71 } 72 73 func (m *memoryThresholdNotifier) Start() { 74 klog.InfoS("Eviction manager: created memoryThresholdNotifier", "notifier", m.Description()) 75 for range m.events { 76 m.handler(fmt.Sprintf("eviction manager: %s crossed", m.Description())) 77 } 78 } 79 80 func (m *memoryThresholdNotifier) UpdateThreshold(summary *statsapi.Summary) error { 81 memoryStats := summary.Node.Memory 82 if isAllocatableEvictionThreshold(m.threshold) { 83 allocatableContainer, err := getSysContainer(summary.Node.SystemContainers, statsapi.SystemContainerPods) 84 if err != nil { 85 return err 86 } 87 memoryStats = allocatableContainer.Memory 88 } 89 if memoryStats == nil || memoryStats.UsageBytes == nil || memoryStats.WorkingSetBytes == nil || memoryStats.AvailableBytes == nil { 90 return fmt.Errorf("summary was incomplete. Expected MemoryStats and all subfields to be non-nil, but got %+v", memoryStats) 91 } 92 // Set threshold on usage to capacity - eviction_hard + inactive_file, 93 // since we want to be notified when working_set = capacity - eviction_hard 94 inactiveFile := resource.NewQuantity(int64(*memoryStats.UsageBytes-*memoryStats.WorkingSetBytes), resource.BinarySI) 95 capacity := resource.NewQuantity(int64(*memoryStats.AvailableBytes+*memoryStats.WorkingSetBytes), resource.BinarySI) 96 evictionThresholdQuantity := evictionapi.GetThresholdQuantity(m.threshold.Value, capacity) 97 memcgThreshold := capacity.DeepCopy() 98 memcgThreshold.Sub(*evictionThresholdQuantity) 99 memcgThreshold.Add(*inactiveFile) 100 101 klog.V(3).InfoS("Eviction manager: setting notifier to capacity", "notifier", m.Description(), "capacity", memcgThreshold.String()) 102 if m.notifier != nil { 103 m.notifier.Stop() 104 } 105 newNotifier, err := m.factory.NewCgroupNotifier(m.cgroupPath, memoryUsageAttribute, memcgThreshold.Value()) 106 if err != nil { 107 return err 108 } 109 m.notifier = newNotifier 110 go m.notifier.Start(m.events) 111 return nil 112 } 113 114 func (m *memoryThresholdNotifier) Description() string { 115 var hard, allocatable string 116 if isHardEvictionThreshold(m.threshold) { 117 hard = "hard " 118 } else { 119 hard = "soft " 120 } 121 if isAllocatableEvictionThreshold(m.threshold) { 122 allocatable = "allocatable " 123 } 124 return fmt.Sprintf("%s%smemory eviction threshold", hard, allocatable) 125 } 126 127 var _ NotifierFactory = &CgroupNotifierFactory{} 128 129 // CgroupNotifierFactory knows how to make CgroupNotifiers which integrate with the kernel 130 type CgroupNotifierFactory struct{} 131 132 // NewCgroupNotifier implements the NotifierFactory interface 133 func (n *CgroupNotifierFactory) NewCgroupNotifier(path, attribute string, threshold int64) (CgroupNotifier, error) { 134 return NewCgroupNotifier(path, attribute, threshold) 135 }