mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
Add NUMA Node awareness to the TopologyManager
This commit is contained in:
parent
d7ecc85239
commit
5660cd3cfb
@ -289,6 +289,7 @@ func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.I
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.TopologyManager) {
|
||||
cm.topologyManager, err = topologymanager.NewManager(
|
||||
numaNodeInfo,
|
||||
nodeConfig.ExperimentalTopologyManagerPolicy,
|
||||
)
|
||||
|
||||
|
@ -13,6 +13,7 @@ go_library(
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/kubelet/cm/cpumanager/topology:go_default_library",
|
||||
"//pkg/kubelet/cm/topologymanager/socketmask:go_default_library",
|
||||
"//pkg/kubelet/lifecycle:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/klog"
|
||||
cputopology "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/socketmask"
|
||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||
)
|
||||
@ -50,6 +51,8 @@ type manager struct {
|
||||
podMap map[string]string
|
||||
//Topology Manager Policy
|
||||
policy Policy
|
||||
//List of NUMA Nodes available on the underlying machine
|
||||
numaNodes []int
|
||||
}
|
||||
|
||||
//HintProvider interface is to be implemented by Hint Providers
|
||||
@ -73,7 +76,7 @@ type TopologyHint struct {
|
||||
var _ Manager = &manager{}
|
||||
|
||||
//NewManager creates a new TopologyManager based on provided policy
|
||||
func NewManager(topologyPolicyName string) (Manager, error) {
|
||||
func NewManager(numaNodeInfo cputopology.NUMANodeInfo, topologyPolicyName string) (Manager, error) {
|
||||
klog.Infof("[topologymanager] Creating topology manager with %s policy", topologyPolicyName)
|
||||
var policy Policy
|
||||
|
||||
@ -92,6 +95,11 @@ func NewManager(topologyPolicyName string) (Manager, error) {
|
||||
return nil, fmt.Errorf("unknown policy: \"%s\"", topologyPolicyName)
|
||||
}
|
||||
|
||||
var numaNodes []int
|
||||
for node := range numaNodeInfo {
|
||||
numaNodes = append(numaNodes, node)
|
||||
}
|
||||
|
||||
var hp []HintProvider
|
||||
pth := make(map[string]map[string]TopologyHint)
|
||||
pm := make(map[string]string)
|
||||
@ -100,6 +108,7 @@ func NewManager(topologyPolicyName string) (Manager, error) {
|
||||
podTopologyHints: pth,
|
||||
podMap: pm,
|
||||
policy: policy,
|
||||
numaNodes: numaNodes,
|
||||
}
|
||||
|
||||
return manager, nil
|
||||
@ -149,12 +158,9 @@ func (m *manager) iterateAllProviderTopologyHints(allProviderHints [][]TopologyH
|
||||
|
||||
// Merge the hints from all hint providers to find the best one.
|
||||
func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) TopologyHint {
|
||||
// Set the default hint to return from this function as an any-numa
|
||||
// affinity with an unpreferred allocation. This will only be returned if
|
||||
// no better hint can be found when merging hints from each hint provider.
|
||||
defaultAffinity, _ := socketmask.NewSocketMask()
|
||||
defaultAffinity.Fill()
|
||||
defaultHint := TopologyHint{defaultAffinity, false}
|
||||
// Set the default affinity as an any-numa affinity containing the list
|
||||
// of NUMA Nodes available on this machine.
|
||||
defaultAffinity, _ := socketmask.NewSocketMask(m.numaNodes...)
|
||||
|
||||
// Loop through all hint providers and save an accumulated list of the
|
||||
// hints returned by each hint provider. If no hints are provided, assume
|
||||
@ -167,9 +173,7 @@ func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) Topology
|
||||
// If hints is nil, insert a single, preferred any-numa hint into allProviderHints.
|
||||
if len(hints) == 0 {
|
||||
klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with any resource")
|
||||
affinity, _ := socketmask.NewSocketMask()
|
||||
affinity.Fill()
|
||||
allProviderHints = append(allProviderHints, []TopologyHint{{affinity, true}})
|
||||
allProviderHints = append(allProviderHints, []TopologyHint{{defaultAffinity, true}})
|
||||
continue
|
||||
}
|
||||
|
||||
@ -177,17 +181,13 @@ func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) Topology
|
||||
for resource := range hints {
|
||||
if hints[resource] == nil {
|
||||
klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with resource '%s'", resource)
|
||||
affinity, _ := socketmask.NewSocketMask()
|
||||
affinity.Fill()
|
||||
allProviderHints = append(allProviderHints, []TopologyHint{{affinity, true}})
|
||||
allProviderHints = append(allProviderHints, []TopologyHint{{defaultAffinity, true}})
|
||||
continue
|
||||
}
|
||||
|
||||
if len(hints[resource]) == 0 {
|
||||
klog.Infof("[topologymanager] Hint Provider has no possible NUMA affinities for resource '%s'", resource)
|
||||
affinity, _ := socketmask.NewSocketMask()
|
||||
affinity.Fill()
|
||||
allProviderHints = append(allProviderHints, []TopologyHint{{affinity, false}})
|
||||
allProviderHints = append(allProviderHints, []TopologyHint{{defaultAffinity, false}})
|
||||
continue
|
||||
}
|
||||
|
||||
@ -199,8 +199,8 @@ func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) Topology
|
||||
// hints in each permutation by taking the bitwise-and of their affinity masks.
|
||||
// Return the hint with the narrowest NUMANodeAffinity of all merged
|
||||
// permutations that have at least one NUMA ID set. If no merged mask can be
|
||||
// found that has at least one NUMA ID set, return the 'defaultHint'.
|
||||
bestHint := defaultHint
|
||||
// found that has at least one NUMA ID set, return the 'defaultAffinity'.
|
||||
bestHint := TopologyHint{defaultAffinity, false}
|
||||
m.iterateAllProviderTopologyHints(allProviderHints, func(permutation []TopologyHint) {
|
||||
// Get the NUMANodeAffinity from each hint in the permutation and see if any
|
||||
// of them encode unpreferred allocations.
|
||||
@ -217,8 +217,7 @@ func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) Topology
|
||||
}
|
||||
|
||||
// Merge the affinities using a bitwise-and operation.
|
||||
mergedAffinity, _ := socketmask.NewSocketMask()
|
||||
mergedAffinity.Fill()
|
||||
mergedAffinity, _ := socketmask.NewSocketMask(m.numaNodes...)
|
||||
mergedAffinity.And(numaAffinities...)
|
||||
|
||||
// Build a mergedHintfrom the merged affinity mask, indicating if an
|
||||
|
@ -33,12 +33,6 @@ func NewTestSocketMask(sockets ...int) socketmask.SocketMask {
|
||||
return s
|
||||
}
|
||||
|
||||
func NewTestSocketMaskFull() socketmask.SocketMask {
|
||||
s, _ := socketmask.NewSocketMask()
|
||||
s.Fill()
|
||||
return s
|
||||
}
|
||||
|
||||
func TestNewManager(t *testing.T) {
|
||||
tcases := []struct {
|
||||
description string
|
||||
@ -64,7 +58,7 @@ func TestNewManager(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range tcases {
|
||||
mngr, err := NewManager(tc.policyName)
|
||||
mngr, err := NewManager(nil, tc.policyName)
|
||||
|
||||
if tc.expectedError != nil {
|
||||
if !strings.Contains(err.Error(), tc.expectedError.Error()) {
|
||||
@ -111,6 +105,8 @@ func TestGetAffinity(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCalculateAffinity(t *testing.T) {
|
||||
numaNodes := []int{0, 1}
|
||||
|
||||
tcases := []struct {
|
||||
name string
|
||||
hp []HintProvider
|
||||
@ -120,7 +116,7 @@ func TestCalculateAffinity(t *testing.T) {
|
||||
name: "TopologyHint not set",
|
||||
hp: []HintProvider{},
|
||||
expected: TopologyHint{
|
||||
NUMANodeAffinity: NewTestSocketMaskFull(),
|
||||
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
|
||||
Preferred: true,
|
||||
},
|
||||
},
|
||||
@ -132,7 +128,7 @@ func TestCalculateAffinity(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: TopologyHint{
|
||||
NUMANodeAffinity: NewTestSocketMaskFull(),
|
||||
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
|
||||
Preferred: true,
|
||||
},
|
||||
},
|
||||
@ -146,7 +142,7 @@ func TestCalculateAffinity(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: TopologyHint{
|
||||
NUMANodeAffinity: NewTestSocketMaskFull(),
|
||||
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
|
||||
Preferred: true,
|
||||
},
|
||||
},
|
||||
@ -160,7 +156,7 @@ func TestCalculateAffinity(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: TopologyHint{
|
||||
NUMANodeAffinity: NewTestSocketMaskFull(),
|
||||
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
|
||||
Preferred: false,
|
||||
},
|
||||
},
|
||||
@ -179,7 +175,7 @@ func TestCalculateAffinity(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: TopologyHint{
|
||||
NUMANodeAffinity: NewTestSocketMaskFull(),
|
||||
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
|
||||
Preferred: true,
|
||||
},
|
||||
},
|
||||
@ -198,7 +194,7 @@ func TestCalculateAffinity(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: TopologyHint{
|
||||
NUMANodeAffinity: NewTestSocketMaskFull(),
|
||||
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
|
||||
Preferred: true,
|
||||
},
|
||||
},
|
||||
@ -343,7 +339,7 @@ func TestCalculateAffinity(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: TopologyHint{
|
||||
NUMANodeAffinity: NewTestSocketMaskFull(),
|
||||
NUMANodeAffinity: NewTestSocketMask(numaNodes...),
|
||||
Preferred: false,
|
||||
},
|
||||
},
|
||||
@ -662,8 +658,10 @@ func TestCalculateAffinity(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range tcases {
|
||||
mngr := manager{}
|
||||
mngr.hintProviders = tc.hp
|
||||
mngr := manager{
|
||||
hintProviders: tc.hp,
|
||||
numaNodes: numaNodes,
|
||||
}
|
||||
actual := mngr.calculateAffinity(v1.Pod{}, v1.Container{})
|
||||
if !actual.NUMANodeAffinity.IsEqual(tc.expected.NUMANodeAffinity) {
|
||||
t.Errorf("Expected NUMANodeAffinity in result to be %v, got %v", tc.expected.NUMANodeAffinity, actual.NUMANodeAffinity)
|
||||
@ -926,10 +924,13 @@ func TestAdmit(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range tcases {
|
||||
man := manager{}
|
||||
man.policy = tc.policy
|
||||
man.podTopologyHints = make(map[string]map[string]TopologyHint)
|
||||
man.hintProviders = tc.hp
|
||||
man := manager{
|
||||
policy: tc.policy,
|
||||
podTopologyHints: make(map[string]map[string]TopologyHint),
|
||||
hintProviders: tc.hp,
|
||||
numaNodes: []int{0, 1},
|
||||
}
|
||||
|
||||
pod := &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
@ -938,10 +939,15 @@ func TestAdmit(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
QOSClass: tc.qosClass,
|
||||
},
|
||||
}
|
||||
podAttr := lifecycle.PodAdmitAttributes{}
|
||||
pod.Status.QOSClass = tc.qosClass
|
||||
podAttr.Pod = pod
|
||||
|
||||
podAttr := lifecycle.PodAdmitAttributes{
|
||||
Pod: pod,
|
||||
}
|
||||
|
||||
actual := man.Admit(&podAttr)
|
||||
if actual.Admit != tc.expected {
|
||||
t.Errorf("Error occurred, expected Admit in result to be %v got %v", tc.expected, actual.Admit)
|
||||
|
Loading…
Reference in New Issue
Block a user