Add NUMA Node awareness to the TopologyManager

This commit is contained in:
Kevin Klues 2019-08-21 15:14:35 +02:00
parent d7ecc85239
commit 5660cd3cfb
4 changed files with 50 additions and 43 deletions

View File

@ -289,6 +289,7 @@ func NewContainerManager(mountUtil mount.Interface, cadvisorInterface cadvisor.I
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.TopologyManager) { if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.TopologyManager) {
cm.topologyManager, err = topologymanager.NewManager( cm.topologyManager, err = topologymanager.NewManager(
numaNodeInfo,
nodeConfig.ExperimentalTopologyManagerPolicy, nodeConfig.ExperimentalTopologyManagerPolicy,
) )

View File

@ -13,6 +13,7 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager", importpath = "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//pkg/kubelet/cm/cpumanager/topology:go_default_library",
"//pkg/kubelet/cm/topologymanager/socketmask:go_default_library", "//pkg/kubelet/cm/topologymanager/socketmask:go_default_library",
"//pkg/kubelet/lifecycle:go_default_library", "//pkg/kubelet/lifecycle:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",

View File

@ -21,6 +21,7 @@ import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/klog" "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/cm/topologymanager/socketmask"
"k8s.io/kubernetes/pkg/kubelet/lifecycle" "k8s.io/kubernetes/pkg/kubelet/lifecycle"
) )
@ -50,6 +51,8 @@ type manager struct {
podMap map[string]string podMap map[string]string
//Topology Manager Policy //Topology Manager Policy
policy Policy policy Policy
//List of NUMA Nodes available on the underlying machine
numaNodes []int
} }
//HintProvider interface is to be implemented by Hint Providers //HintProvider interface is to be implemented by Hint Providers
@ -73,7 +76,7 @@ type TopologyHint struct {
var _ Manager = &manager{} var _ Manager = &manager{}
//NewManager creates a new TopologyManager based on provided policy //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) klog.Infof("[topologymanager] Creating topology manager with %s policy", topologyPolicyName)
var policy Policy var policy Policy
@ -92,6 +95,11 @@ func NewManager(topologyPolicyName string) (Manager, error) {
return nil, fmt.Errorf("unknown policy: \"%s\"", topologyPolicyName) return nil, fmt.Errorf("unknown policy: \"%s\"", topologyPolicyName)
} }
var numaNodes []int
for node := range numaNodeInfo {
numaNodes = append(numaNodes, node)
}
var hp []HintProvider var hp []HintProvider
pth := make(map[string]map[string]TopologyHint) pth := make(map[string]map[string]TopologyHint)
pm := make(map[string]string) pm := make(map[string]string)
@ -100,6 +108,7 @@ func NewManager(topologyPolicyName string) (Manager, error) {
podTopologyHints: pth, podTopologyHints: pth,
podMap: pm, podMap: pm,
policy: policy, policy: policy,
numaNodes: numaNodes,
} }
return manager, nil 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. // Merge the hints from all hint providers to find the best one.
func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) TopologyHint { func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) TopologyHint {
// Set the default hint to return from this function as an any-numa // Set the default affinity as an any-numa affinity containing the list
// affinity with an unpreferred allocation. This will only be returned if // of NUMA Nodes available on this machine.
// no better hint can be found when merging hints from each hint provider. defaultAffinity, _ := socketmask.NewSocketMask(m.numaNodes...)
defaultAffinity, _ := socketmask.NewSocketMask()
defaultAffinity.Fill()
defaultHint := TopologyHint{defaultAffinity, false}
// Loop through all hint providers and save an accumulated list of the // Loop through all hint providers and save an accumulated list of the
// hints returned by each hint provider. If no hints are provided, assume // 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 hints is nil, insert a single, preferred any-numa hint into allProviderHints.
if len(hints) == 0 { if len(hints) == 0 {
klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with any resource") klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with any resource")
affinity, _ := socketmask.NewSocketMask() allProviderHints = append(allProviderHints, []TopologyHint{{defaultAffinity, true}})
affinity.Fill()
allProviderHints = append(allProviderHints, []TopologyHint{{affinity, true}})
continue continue
} }
@ -177,17 +181,13 @@ func (m *manager) calculateAffinity(pod v1.Pod, container v1.Container) Topology
for resource := range hints { for resource := range hints {
if hints[resource] == nil { if hints[resource] == nil {
klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with resource '%s'", resource) klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with resource '%s'", resource)
affinity, _ := socketmask.NewSocketMask() allProviderHints = append(allProviderHints, []TopologyHint{{defaultAffinity, true}})
affinity.Fill()
allProviderHints = append(allProviderHints, []TopologyHint{{affinity, true}})
continue continue
} }
if len(hints[resource]) == 0 { if len(hints[resource]) == 0 {
klog.Infof("[topologymanager] Hint Provider has no possible NUMA affinities for resource '%s'", resource) klog.Infof("[topologymanager] Hint Provider has no possible NUMA affinities for resource '%s'", resource)
affinity, _ := socketmask.NewSocketMask() allProviderHints = append(allProviderHints, []TopologyHint{{defaultAffinity, false}})
affinity.Fill()
allProviderHints = append(allProviderHints, []TopologyHint{{affinity, false}})
continue 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. // hints in each permutation by taking the bitwise-and of their affinity masks.
// Return the hint with the narrowest NUMANodeAffinity of all merged // 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 // 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'. // found that has at least one NUMA ID set, return the 'defaultAffinity'.
bestHint := defaultHint bestHint := TopologyHint{defaultAffinity, false}
m.iterateAllProviderTopologyHints(allProviderHints, func(permutation []TopologyHint) { m.iterateAllProviderTopologyHints(allProviderHints, func(permutation []TopologyHint) {
// Get the NUMANodeAffinity from each hint in the permutation and see if any // Get the NUMANodeAffinity from each hint in the permutation and see if any
// of them encode unpreferred allocations. // 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. // Merge the affinities using a bitwise-and operation.
mergedAffinity, _ := socketmask.NewSocketMask() mergedAffinity, _ := socketmask.NewSocketMask(m.numaNodes...)
mergedAffinity.Fill()
mergedAffinity.And(numaAffinities...) mergedAffinity.And(numaAffinities...)
// Build a mergedHintfrom the merged affinity mask, indicating if an // Build a mergedHintfrom the merged affinity mask, indicating if an

View File

@ -33,12 +33,6 @@ func NewTestSocketMask(sockets ...int) socketmask.SocketMask {
return s return s
} }
func NewTestSocketMaskFull() socketmask.SocketMask {
s, _ := socketmask.NewSocketMask()
s.Fill()
return s
}
func TestNewManager(t *testing.T) { func TestNewManager(t *testing.T) {
tcases := []struct { tcases := []struct {
description string description string
@ -64,7 +58,7 @@ func TestNewManager(t *testing.T) {
} }
for _, tc := range tcases { for _, tc := range tcases {
mngr, err := NewManager(tc.policyName) mngr, err := NewManager(nil, tc.policyName)
if tc.expectedError != nil { if tc.expectedError != nil {
if !strings.Contains(err.Error(), tc.expectedError.Error()) { if !strings.Contains(err.Error(), tc.expectedError.Error()) {
@ -111,6 +105,8 @@ func TestGetAffinity(t *testing.T) {
} }
func TestCalculateAffinity(t *testing.T) { func TestCalculateAffinity(t *testing.T) {
numaNodes := []int{0, 1}
tcases := []struct { tcases := []struct {
name string name string
hp []HintProvider hp []HintProvider
@ -120,7 +116,7 @@ func TestCalculateAffinity(t *testing.T) {
name: "TopologyHint not set", name: "TopologyHint not set",
hp: []HintProvider{}, hp: []HintProvider{},
expected: TopologyHint{ expected: TopologyHint{
NUMANodeAffinity: NewTestSocketMaskFull(), NUMANodeAffinity: NewTestSocketMask(numaNodes...),
Preferred: true, Preferred: true,
}, },
}, },
@ -132,7 +128,7 @@ func TestCalculateAffinity(t *testing.T) {
}, },
}, },
expected: TopologyHint{ expected: TopologyHint{
NUMANodeAffinity: NewTestSocketMaskFull(), NUMANodeAffinity: NewTestSocketMask(numaNodes...),
Preferred: true, Preferred: true,
}, },
}, },
@ -146,7 +142,7 @@ func TestCalculateAffinity(t *testing.T) {
}, },
}, },
expected: TopologyHint{ expected: TopologyHint{
NUMANodeAffinity: NewTestSocketMaskFull(), NUMANodeAffinity: NewTestSocketMask(numaNodes...),
Preferred: true, Preferred: true,
}, },
}, },
@ -160,7 +156,7 @@ func TestCalculateAffinity(t *testing.T) {
}, },
}, },
expected: TopologyHint{ expected: TopologyHint{
NUMANodeAffinity: NewTestSocketMaskFull(), NUMANodeAffinity: NewTestSocketMask(numaNodes...),
Preferred: false, Preferred: false,
}, },
}, },
@ -179,7 +175,7 @@ func TestCalculateAffinity(t *testing.T) {
}, },
}, },
expected: TopologyHint{ expected: TopologyHint{
NUMANodeAffinity: NewTestSocketMaskFull(), NUMANodeAffinity: NewTestSocketMask(numaNodes...),
Preferred: true, Preferred: true,
}, },
}, },
@ -198,7 +194,7 @@ func TestCalculateAffinity(t *testing.T) {
}, },
}, },
expected: TopologyHint{ expected: TopologyHint{
NUMANodeAffinity: NewTestSocketMaskFull(), NUMANodeAffinity: NewTestSocketMask(numaNodes...),
Preferred: true, Preferred: true,
}, },
}, },
@ -343,7 +339,7 @@ func TestCalculateAffinity(t *testing.T) {
}, },
}, },
expected: TopologyHint{ expected: TopologyHint{
NUMANodeAffinity: NewTestSocketMaskFull(), NUMANodeAffinity: NewTestSocketMask(numaNodes...),
Preferred: false, Preferred: false,
}, },
}, },
@ -662,8 +658,10 @@ func TestCalculateAffinity(t *testing.T) {
} }
for _, tc := range tcases { for _, tc := range tcases {
mngr := manager{} mngr := manager{
mngr.hintProviders = tc.hp hintProviders: tc.hp,
numaNodes: numaNodes,
}
actual := mngr.calculateAffinity(v1.Pod{}, v1.Container{}) actual := mngr.calculateAffinity(v1.Pod{}, v1.Container{})
if !actual.NUMANodeAffinity.IsEqual(tc.expected.NUMANodeAffinity) { if !actual.NUMANodeAffinity.IsEqual(tc.expected.NUMANodeAffinity) {
t.Errorf("Expected NUMANodeAffinity in result to be %v, got %v", tc.expected.NUMANodeAffinity, actual.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 { for _, tc := range tcases {
man := manager{} man := manager{
man.policy = tc.policy policy: tc.policy,
man.podTopologyHints = make(map[string]map[string]TopologyHint) podTopologyHints: make(map[string]map[string]TopologyHint),
man.hintProviders = tc.hp hintProviders: tc.hp,
numaNodes: []int{0, 1},
}
pod := &v1.Pod{ pod := &v1.Pod{
Spec: v1.PodSpec{ Spec: v1.PodSpec{
Containers: []v1.Container{ 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 := lifecycle.PodAdmitAttributes{
podAttr.Pod = pod Pod: pod,
}
actual := man.Admit(&podAttr) actual := man.Admit(&podAttr)
if actual.Admit != tc.expected { if actual.Admit != tc.expected {
t.Errorf("Error occurred, expected Admit in result to be %v got %v", tc.expected, actual.Admit) t.Errorf("Error occurred, expected Admit in result to be %v got %v", tc.expected, actual.Admit)