...
1
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
34
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
50
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
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
93
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
130 type CgroupNotifierFactory struct{}
131
132
133 func (n *CgroupNotifierFactory) NewCgroupNotifier(path, attribute string, threshold int64) (CgroupNotifier, error) {
134 return NewCgroupNotifier(path, attribute, threshold)
135 }
136
View as plain text