k8s.io/kubernetes@v1.29.3/pkg/kubelet/cm/topologymanager/topology_manager.go (about) 1 /* 2 Copyright 2019 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 topologymanager 18 19 import ( 20 "fmt" 21 "time" 22 23 cadvisorapi "github.com/google/cadvisor/info/v1" 24 v1 "k8s.io/api/core/v1" 25 "k8s.io/klog/v2" 26 "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask" 27 "k8s.io/kubernetes/pkg/kubelet/lifecycle" 28 "k8s.io/kubernetes/pkg/kubelet/metrics" 29 ) 30 31 const ( 32 // maxAllowableNUMANodes specifies the maximum number of NUMA Nodes that 33 // the TopologyManager supports on the underlying machine. 34 // 35 // At present, having more than this number of NUMA Nodes will result in a 36 // state explosion when trying to enumerate possible NUMAAffinity masks and 37 // generate hints for them. As such, if more NUMA Nodes than this are 38 // present on a machine and the TopologyManager is enabled, an error will 39 // be returned and the TopologyManager will not be loaded. 40 maxAllowableNUMANodes = 8 41 // ErrorTopologyAffinity represents the type for a TopologyAffinityError 42 ErrorTopologyAffinity = "TopologyAffinityError" 43 ) 44 45 // TopologyAffinityError represents an resource alignment error 46 type TopologyAffinityError struct{} 47 48 func (e TopologyAffinityError) Error() string { 49 return "Resources cannot be allocated with Topology locality" 50 } 51 52 func (e TopologyAffinityError) Type() string { 53 return ErrorTopologyAffinity 54 } 55 56 // Manager interface provides methods for Kubelet to manage pod topology hints 57 type Manager interface { 58 // PodAdmitHandler is implemented by Manager 59 lifecycle.PodAdmitHandler 60 // AddHintProvider adds a hint provider to manager to indicate the hint provider 61 // wants to be consulted with when making topology hints 62 AddHintProvider(HintProvider) 63 // AddContainer adds pod to Manager for tracking 64 AddContainer(pod *v1.Pod, container *v1.Container, containerID string) 65 // RemoveContainer removes pod from Manager tracking 66 RemoveContainer(containerID string) error 67 // Store is the interface for storing pod topology hints 68 Store 69 } 70 71 type manager struct { 72 //Topology Manager Scope 73 scope Scope 74 } 75 76 // HintProvider is an interface for components that want to collaborate to 77 // achieve globally optimal concrete resource alignment with respect to 78 // NUMA locality. 79 type HintProvider interface { 80 // GetTopologyHints returns a map of resource names to a list of possible 81 // concrete resource allocations in terms of NUMA locality hints. Each hint 82 // is optionally marked "preferred" and indicates the set of NUMA nodes 83 // involved in the hypothetical allocation. The topology manager calls 84 // this function for each hint provider, and merges the hints to produce 85 // a consensus "best" hint. The hint providers may subsequently query the 86 // topology manager to influence actual resource assignment. 87 GetTopologyHints(pod *v1.Pod, container *v1.Container) map[string][]TopologyHint 88 // GetPodTopologyHints returns a map of resource names to a list of possible 89 // concrete resource allocations per Pod in terms of NUMA locality hints. 90 GetPodTopologyHints(pod *v1.Pod) map[string][]TopologyHint 91 // Allocate triggers resource allocation to occur on the HintProvider after 92 // all hints have been gathered and the aggregated Hint is available via a 93 // call to Store.GetAffinity(). 94 Allocate(pod *v1.Pod, container *v1.Container) error 95 } 96 97 // Store interface is to allow Hint Providers to retrieve pod affinity 98 type Store interface { 99 GetAffinity(podUID string, containerName string) TopologyHint 100 GetPolicy() Policy 101 } 102 103 // TopologyHint is a struct containing the NUMANodeAffinity for a Container 104 type TopologyHint struct { 105 NUMANodeAffinity bitmask.BitMask 106 // Preferred is set to true when the NUMANodeAffinity encodes a preferred 107 // allocation for the Container. It is set to false otherwise. 108 Preferred bool 109 } 110 111 // IsEqual checks if TopologyHint are equal 112 func (th *TopologyHint) IsEqual(topologyHint TopologyHint) bool { 113 if th.Preferred == topologyHint.Preferred { 114 if th.NUMANodeAffinity == nil || topologyHint.NUMANodeAffinity == nil { 115 return th.NUMANodeAffinity == topologyHint.NUMANodeAffinity 116 } 117 return th.NUMANodeAffinity.IsEqual(topologyHint.NUMANodeAffinity) 118 } 119 return false 120 } 121 122 // LessThan checks if TopologyHint `a` is less than TopologyHint `b` 123 // this means that either `a` is a preferred hint and `b` is not 124 // or `a` NUMANodeAffinity attribute is narrower than `b` NUMANodeAffinity attribute. 125 func (th *TopologyHint) LessThan(other TopologyHint) bool { 126 if th.Preferred != other.Preferred { 127 return th.Preferred 128 } 129 return th.NUMANodeAffinity.IsNarrowerThan(other.NUMANodeAffinity) 130 } 131 132 var _ Manager = &manager{} 133 134 // NewManager creates a new TopologyManager based on provided policy and scope 135 func NewManager(topology []cadvisorapi.Node, topologyPolicyName string, topologyScopeName string, topologyPolicyOptions map[string]string) (Manager, error) { 136 // When policy is none, the scope is not relevant, so we can short circuit here. 137 if topologyPolicyName == PolicyNone { 138 klog.InfoS("Creating topology manager with none policy") 139 return &manager{scope: NewNoneScope()}, nil 140 } 141 142 opts, err := NewPolicyOptions(topologyPolicyOptions) 143 if err != nil { 144 return nil, err 145 } 146 147 klog.InfoS("Creating topology manager with policy per scope", "topologyPolicyName", topologyPolicyName, "topologyScopeName", topologyScopeName, "topologyPolicyOptions", opts) 148 149 numaInfo, err := NewNUMAInfo(topology, opts) 150 if err != nil { 151 return nil, fmt.Errorf("cannot discover NUMA topology: %w", err) 152 } 153 154 if topologyPolicyName != PolicyNone && len(numaInfo.Nodes) > maxAllowableNUMANodes { 155 return nil, fmt.Errorf("unsupported on machines with more than %v NUMA Nodes", maxAllowableNUMANodes) 156 } 157 158 var policy Policy 159 switch topologyPolicyName { 160 161 case PolicyBestEffort: 162 policy = NewBestEffortPolicy(numaInfo, opts) 163 164 case PolicyRestricted: 165 policy = NewRestrictedPolicy(numaInfo, opts) 166 167 case PolicySingleNumaNode: 168 policy = NewSingleNumaNodePolicy(numaInfo, opts) 169 170 default: 171 return nil, fmt.Errorf("unknown policy: \"%s\"", topologyPolicyName) 172 } 173 174 var scope Scope 175 switch topologyScopeName { 176 177 case containerTopologyScope: 178 scope = NewContainerScope(policy) 179 180 case podTopologyScope: 181 scope = NewPodScope(policy) 182 183 default: 184 return nil, fmt.Errorf("unknown scope: \"%s\"", topologyScopeName) 185 } 186 187 manager := &manager{ 188 scope: scope, 189 } 190 191 return manager, nil 192 } 193 194 func (m *manager) GetAffinity(podUID string, containerName string) TopologyHint { 195 return m.scope.GetAffinity(podUID, containerName) 196 } 197 198 func (m *manager) GetPolicy() Policy { 199 return m.scope.GetPolicy() 200 } 201 202 func (m *manager) AddHintProvider(h HintProvider) { 203 m.scope.AddHintProvider(h) 204 } 205 206 func (m *manager) AddContainer(pod *v1.Pod, container *v1.Container, containerID string) { 207 m.scope.AddContainer(pod, container, containerID) 208 } 209 210 func (m *manager) RemoveContainer(containerID string) error { 211 return m.scope.RemoveContainer(containerID) 212 } 213 214 func (m *manager) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult { 215 klog.InfoS("Topology Admit Handler", "podUID", attrs.Pod.UID, "podNamespace", attrs.Pod.Namespace, "podName", attrs.Pod.Name) 216 metrics.TopologyManagerAdmissionRequestsTotal.Inc() 217 218 startTime := time.Now() 219 podAdmitResult := m.scope.Admit(attrs.Pod) 220 metrics.TopologyManagerAdmissionDuration.Observe(float64(time.Since(startTime).Milliseconds())) 221 222 return podAdmitResult 223 }