memory_threshold_notifier.go 4.6 KB

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