...

Source file src/k8s.io/kubernetes/pkg/kubelet/eviction/memory_threshold_notifier.go

Documentation: k8s.io/kubernetes/pkg/kubelet/eviction

     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  }
   136  

View as plain text