写点什么

Kubelet 从人门到放弃:拓扑管理(下)

用户头像
DCOS
关注
发布于: 2021 年 03 月 03 日
Kubelet从人门到放弃:拓扑管理(下)

<Kubelet 从入门到放弃>系列将对 Kubelet 组件由基础知识到源码进行深入梳理。上一篇 zouyee 带各位看了 CPU 管理的相关内容,其中提及拓扑管理,本文将对此进行详细剖析,拓扑管理在 Kubernetes 1.18 时提升为 Beta。 TopologyManager 功能可实现 CPU、内存和外围设备(例如 SR-IOV VF 和 GPU)的 NUMA 对齐,从而使集群满足低延迟需求。


三、源码分析


对于拓扑管理器代码分析,我们从两个方面进行:


1)Kubelet 初始化时,涉及拓扑管理的相关操作


2)Kubelet 运行时,涉及拓扑管理的相关操作,深入分析拓扑管理结构逻辑


3.1 Kubelet 初始化


关于 Kubelet 初始化,我们在以 CPU manager 结合拓扑管理器的启动图(当前为 CPU manager、memory manager、device manager 构成资源分配管理器,其属于 Container Manager 模块的子系统)进行说明。


对于上图的内容,zouyee 总结流程如下:


1、在命令行启动部分,Kubelet 中调用 NewContainerManager 构建 ContainerManager


2、NewContainerManager 函数调用 topologymanager.NewManager 构建拓扑管理器,否则未启用拓扑管理器,则构建 fake


3、NewContainerManager 函数分别调用 cpu、memory 及 device 提供的 NewManager 构建相关管理器


4、若拓扑管理特性开启,则拓扑管理器使用 AddHintPriovider 方法将 CPU、memory 及 device 管理器加入管理,上述三种资源分配器,需要实现 HintPriovider 接口。


5、回到命令行启动部分,调用 NewMainKubelet(),构建 Kubelet 结构体


6、构建 Kubelet 结构体时,将 CPU、memory 管理器(没有 device)跟拓扑管理器封装为 InternalContainerLifecycle 接口,其实现 Pod 相关的生命周期资源管理操作,涉及资源分配回收相关的是 PreStartContainer、PostStopContainer 方法,可参看具体实现。


7、构建 Kubelet 结构体时,调用 AddPodmitHandler 将 GetAllocateResourcesPodAdmitHandler 方法加入到 Pod 准入插件中,在 Pod 创建时,资源预分配检查,其中 GetAllocateResourcesPodAdmitHandler 根据是否开启拓扑管理,决定是返回拓扑管理 Admit 接口,还是使用 cpu、memory 及 device 构成资源分配器,实现 Admit 接口。


8、构建 Kubelet 结构体后,调用 ContainerManager 的 Start 方法,ContainerManager 在 Start 方法中调用 CPU、memeory 及 device 管理器的 Start 方法,其做一些处理工作并孵化一个 goroutine,执行 reconcileState()


注:关于上述启动流程的代码解释,可以返回识透 CPU 一文。


3.2 Kubelet 运行时


Kubelet 运行时,涉及到拓扑管理、资源分配的就是对于 Pod 处理流程,zouyee 总结如下:


1、PodConfig 从 apiserver、file 及 http 三处接受 Pod,调用 Updates()返回 channel,内容为 Pod 列表及类型。


2、Kubelet 调用 Run 方法,处理 PodConfig 的 Updates()返回的 channel


3、在 Run 方法内部,Kubelet 调用 syncLoop,而在 syncLoop 内部,调用 syncLoopIteration


4、在 syncLoopIteration 中,当 configCh(即 PodConfig 调用的 Updates())返回的 pod 类型为 ADD 时,执行 handler.HandlePodAdditions,在 HandlePodAdditions 中,处理流程如下:当 pod 状态为非 Termination 时,Kubelet 遍历 admitHandlers,调用 Admit 方法。


注:syncLoopIteration 中除了 configCh,还有其他 channel(plegCh、syncCh、housekeepingCh 及 livenessManager)其中 plegCh、syncCh 及 livenessManager 三类 channel 中调用的 HandlePodAddtion、HandlePodReconcile、HandlePodSyncs 及 HandlePodUpdates 都涉及 dispatch 方法调用,还记得 Kubelet 流程中,将 CPU 管理器、内存管理器跟拓扑管理器封装为 InternalContainerLifecycle 接口,其实现 Pod 相关的生命周期资源管理操作,涉及 CPU、内存相关的是 PreStartContainer 方法,其调用 AddContainer 方法,后续统一介绍。


5、在介绍 Kubelet 启动时,调用 AddPodmitHandler 将 GetAllocateResourcesPodAdmitHandler 方法加入到 admitHandlers 中,因此在调用 Admit 方法的操作,涉及到拓扑管理的也就是 GetAllocateResourcesPodAdmitHandler,那么接下来就接受一下该方法。


6、在 Kublet 的 GetAllocateResourcesPodAdmitHandler 方法的处理逻辑为:当启用拓扑特性时,资源分配由拓扑管理器统一接管,如果未启用,则为 cpu 管理器、内存管理器及设备管理器分别管理,本文只介绍启用拓扑管理器的情况。


7、启用拓扑管理器后,Kublet 的 GetAllocateResourcesPodAdmitHandler 返回的 Admit 接口类型,由拓扑管理器实现,后续统一介绍。


上述流程即为 Pod 大致的处理流程,下面介绍拓扑结构初始化、AddContainer 及 Admit 方法。


1)拓扑结构初始化


拓扑结构初始化函数为 pkg/kubelet/cm/topologymanager/topology_manager.go:119


// NewManager creates a new TopologyManager based on provided policy and scope

func NewManager(topology []cadvisorapi.Node, topologyPolicyName string, topologyScopeName string) (Manager, error) {

// a. 根据 cadvisor 数据初始化 numa 信息

var numaNodes []int

for _, node := range topology {

numaNodes = append(numaNodes, node.Id)

}

// b. 判断策略为非 none 时,numa 节点数量是否超过 8,若超过,则返回错误

if topologyPolicyName != PolicyNone && len(numaNodes) > maxAllowableNUMANodes {

return nil, fmt.Errorf("unsupported on machines with more than %v NUMA Nodes", maxAllowableNUMANodes)

}

// c. 根据传入 policy 名称,进行初始化 policy

var policy Policy

switch topologyPolicyName {

case PolicyNone:

policy = NewNonePolicy()

case PolicyBestEffort:

policy = NewBestEffortPolicy(numaNodes)

case PolicyRestricted:

policy = NewRestrictedPolicy(numaNodes)

case PolicySingleNumaNode:

policy = NewSingleNumaNodePolicy(numaNodes)

default:

return nil, fmt.Errorf("unknown policy: \"%s\"", topologyPolicyName)

}

// d. 根据传入 scope 名称,以初始化 policy 结构体初始化 scope

var scope Scope

switch topologyScopeName {

case containerTopologyScope:

scope = NewContainerScope(policy)

case podTopologyScope:

scope = NewPodScope(policy)

default:

return nil, fmt.Errorf("unknown scope: \"%s\"", topologyScopeName)

}

// e. 封装 scope,返回 manager 结构体

manager := &manager{

scope: scope,

}


a. 根据 cadvisor 数据初始化 numa 信息


b. 判断策略为非 none 时,numa 节点数量是否超过 8,若超过,则返回错误


c. 根据传入 policy 名称,进行初始化 policy


d. 根据传入 scope 名称,以初始化 policy 结构体初始化 scope


e. 封装 scope,返回 manager 结构体


2) AddContainer


AddContainer 实际调用 scope 的方法:pkg/kubelet/cm/topologymanager/scope.go:97


func (s scope) AddContainer(pod v1.Pod, containerID string) error {

s.mutex.Lock()

defer s.mutex.Unlock()

s.podMap[containerID] = string(pod.UID)

return nil

}


该处只做简单字典加入操作。


3)Admit


Admit 函数调用:pkg/kubelet/cm/topologymanager/topology_manager.go:186,根据 scope 类型分别调用不同的实现:


a、container


pkg/kubelet/cm/topologymanager/scope_container.go:45


func (s containerScope) Admit(pod v1.Pod) lifecycle.PodAdmitResult {

// Exception - Policy : none

// 1. 策略为 none,则跳过

if s.policy.Name() == PolicyNone {

return s.admitPolicyNone(pod)

}

// 2. 遍历 init 及常规容器

for _, container := range append(pod.Spec.InitContainers, pod.Spec.Containers...) {

// 2.1 计算亲和性,判断是否准入

bestHint, admit := s.calculateAffinity(pod, &container)

if !admit {

return topologyAffinityError()

}

// 2.2 记录分配结果

s.setTopologyHints(string(pod.UID), container.Name, bestHint)

// 2.3 调用 hint provider 分配资源

err := s.allocateAlignedResources(pod, &container)

if err != nil {

return unexpectedAdmissionError(err)

}

}

return admitPod()

}


b、pod


pkg/kubelet/cm/topologymanager/scope_pod.go:45


func (s podScope) Admit(pod v1.Pod) lifecycle.PodAdmitResult {

// Exception - Policy : none

// 1. 策略为 none,则跳过

if s.policy.Name() == PolicyNone {

return s.admitPolicyNone(pod)

}

// 2 计算亲和性,判断是否准入

bestHint, admit := s.calculateAffinity(pod)

if !admit {

return topologyAffinityError()

}

// 3. 遍历 init 及常规容器

for _, container := range append(pod.Spec.InitContainers, pod.Spec.Containers...) {

// 3.1 记录分配结果

s.setTopologyHints(string(pod.UID), container.Name, bestHint)

// 3.2 调用 hint provider 分配资源

err := s.allocateAlignedResources(pod, &container)

if err != nil {

return unexpectedAdmissionError(err)

}

}

return admitPod()

}


具体说明见代码注释,需要说明的是 scope 为 container 与 pod 的区别主要在计算亲和性,判断是否准入的阶段,同样也反应了 scope 与 container 的粒度,后续重点介绍 calculateAffinity 方法。


下面 zouyee 带各位总结一下拓扑管理器的 Admit 逻辑。


拓扑管理器为组件定义 Hint Providers 的接口,以发送和接收拓扑信息,CPU、memory 及 device 都实现该接口,拓扑管理器调用 AddHintPriovider 加入到管理器,其中拓扑信息表示可用的 NUMA 节点和首选分配指示的位掩码。 拓扑管理器策略对所提供的 hint 执行一组操作,并根据策略获取最优解;如果存储了与预期不符的 hint,则该建议的优选字段设置为 false。所选建议可用来决定节点接受或拒绝 Pod 。 之后,hint 结果存储在拓扑管理器中,供 Hint Providers 进行资源分配决策时使用。


对于上述两种作用域(container 及 pod)的 calculateAffinity 通用流程,汇总如下(忽略计算亲和性的差异):


对于上图的内容,zouyee 总结流程如下:


  1. 遍历容器中的所有容器(scope 为 pod 跟 container 的差别,上面已经说明)

  2. 对于每个容器,针对容器请求的每种拓扑感知资源类型(例如 gpu-vendor.com/gpu、nic-vendor.com/nic、cpu 等),从一组 HintProviders 中获取 TopologyHints。

  3. 使用选定的策略,合并收集到的 TopologyHints 以找到最佳 hint,该 hint 可以在所有资源类型之间对齐资源分配。

  4. 循环返回 hintHintProviders 集合,指示他们使用合并的 hint 来分配他们管理的资源。

  5. 如果上述步骤中的任一个失败或根据所选策略无法满足对齐要求,Kubelet 将不会准入该 pod。


下面 zouyee 根据下图依次介绍拓扑管理器涉及的结构体。


a. TopologyHints


拓扑 hint 对一组约束进行编码,记录可以满足给定的资源请求。 目前,我们唯一考虑的约束是 NUMA 对齐。 定义如下:


type TopologyHint struct {

NUMANodeAffinity bitmask.BitMask

Preferred bool

}


NUMANodeAffinity 字段表示可以满足资源请求的 NUMA 节点个数的位掩码,是 bitmask 类型。 例如,在 2 个 NUMA 节点的系统上,可能的掩码包括:


{00}, {01}, {10}, {11}


Preferred 是用来管理 NUMANodeAffinity 是否生效的布尔类型,如果 Preferred 为 true 那么当前的亲和度有效,如果为 false 那么当前的亲和度无效。 使用 best-effort 策略时,在生成最佳 hint 时,优先 hint 将优先于非优先 hint。 使用 restricted 和 single-numa-node 策略时,将拒绝非优先 hint。


HintProvider 为每个可以满足该资源请求的 NUMA 节点的掩码生成一个 TopologyHint。 如果掩码不能满足要求,则将其省略。 例如,当被要求分配 2 个资源时,HintProvider 可能在具有 2 个 NUMA 节点的系统上提供以下 hint。 这些 hint 编码代表的两种资源可以都来自单个 NUMA 节点(0 或 1),也可以各自来自不同的 NUMA 节点。


{01: True}, {10: True}, {11: False}


当且仅当 NUMANodeAffinity 代表的信息可以满足资源请求的最小 NUMA 节点集时,所有 HintProvider 才会将 Preferred 字段设置为 True。


{0011: True}, {0111: False}, {1011: False}, {1111: False}


如果在其他容器释放资源之前无法满足实际的首选分配,则 HintProvider 返回所有 Preferred 字段设置为 False 的 hint 列表。考虑以下场景:


  1. 当前,除 2 个 CPU 外的所有 CPU 均已分配给容器

  2. 剩余的 2 个 CPU 在不同的 NUMA 节点上

  3. 一个新的容器请求 2 个 CPU


在上述情况下,生成的唯一 hint 是{11:False}而不是{11:True}。因为可以从该系统上的同一 NUMA 节点分配 2 个 CPU(虽然当前的分配状态,还不能立即分配),在可以满足最小对齐方式时,使 pod 进入失败并重试部署总比选择以次优对齐方式调度 pod 更好。


b. HintProviders


目前,Kubernetes 中仅有的 HintProviders 是 CPUManager、MemoryManager 及 DeviceManager。 拓扑管理器既从 HintProviders 收集 TopologyHint,又使用合并的最佳 hint 调用资源分配。 HintProviders 实现以下接口:


type HintProvider interface {

GetTopologyHints(v1.Pod, v1.Container) map[string][]TopologyHint

Allocate(v1.Pod, v1.Container) error

}


注意:GetTopologyHints 返回一个 map [string] [] TopologyHint。 这使单个 HintProvider 可以提供多种资源类型的 hint。 例如,DeviceManager 可以返回插件注册的多种资源类型。


当 HintProvider 生成 hint 时,仅考虑如何满足系统上当前可用资源的对齐方式。 不考虑已经分配给其他容器的任何资源。


例如,考虑图 1 中的系统,以下两个容器请求资源:


# Container0

spec:

containers:

- name: numa-aligned-container0

image: alpine

resources:

limits:

cpu: 2

memory: 200Mi

gpu-vendor.com/gpu: 1

nic-vendor.com/nic: 1


# Container1

spec:

containers:

- name: numa-aligned-container1

image: alpine

resources:

limits:

cpu: 2

memory: 200Mi

gpu-vendor.com/gpu: 1

nic-vendor.com/nic: 1


如果 Container0 是要在系统上分配的第一个容器,则当前三种拓扑感知资源类型生成以下 hint 集:


cpu: {{01: True}, {10: True}, {11: False}}

gpu-vendor.com/gpu: {{01: True}, {10: True}}

nic-vendor.com/nic: {{01: True}, {10: True}}


已经对齐的资源分配:


{cpu: {0, 1}, gpu: 0, nic: 0}


在考虑 Container1 时,上述资源假定为不可用,因此将生成以下 hint 集:


cpu: {{01: True}, {10: True}, {11: False}}

gpu-vendor.com/gpu: {{10: True}}

nic-vendor.com/nic: {{10: True}}


分配的对齐资源:


{cpu: {4, 5}, gpu: 1, nic: 1}


注意:HintProviders 调用 Allocate 的时,并未采用合并的最佳 hint, 而是通过 TopologyManager 实现的 Store 接口,HintProviders 通过该接口,获取生成的 hint:


type Store interface {

GetAffinity(podUID string, containerName string) TopologyHint

}


c. Policy.Merge


每个策略都实现了合并方法,各自实现如何将所有 HintProviders 生成的 TopologyHint 集合合并到单个 TopologyHint 中,该 TopologyHint 用于提供已对齐的资源分配信息。


// 1. bestEffort

func (p *bestEffortPolicy) Merge(providersHints []map[string][]TopologyHint) (TopologyHint, bool) {

filteredProvidersHints := filterProvidersHints(providersHints)

bestHint := mergeFilteredHints(p.numaNodes, filteredProvidersHints)

admit := p.canAdmitPodResult(&bestHint)

return bestHint, admit

}

// 2. restrict

func (p *restrictedPolicy) Merge(providersHints []map[string][]TopologyHint) (TopologyHint, bool) {

filteredHints := filterProvidersHints(providersHints)

hint := mergeFilteredHints(p.numaNodes, filteredHints)

admit := p.canAdmitPodResult(&hint)

return hint, admit

}

// 3. sigle-numa-node

func (p *singleNumaNodePolicy) Merge(providersHints []map[string][]TopologyHint) (TopologyHint, bool) {

filteredHints := filterProvidersHints(providersHints)

// Filter to only include don't cares and hints with a single NUMA node.

singleNumaHints := filterSingleNumaHints(filteredHints)

bestHint := mergeFilteredHints(p.numaNodes, singleNumaHints)

defaultAffinity, _ := bitmask.NewBitMask(p.numaNodes...)

if bestHint.NUMANodeAffinity.IsEqual(defaultAffinity) {

bestHint = TopologyHint{nil, bestHint.Preferred}

}

admit := p.canAdmitPodResult(&bestHint)

return bestHint, admit

}


从上述三种分配策略,可以发现 Merge 方法的一些类似流程:


1. filterProvidersHints

2. mergeFilteredHints

3. canAdmitPodResult


其中 filterProvidersHints 位于 pkg/kubelet/cm/topologymanager/policy.go:62


func filterProvidersHints(providersHints []map[string][]TopologyHint) [][]TopologyHint {

// Loop through all hint providers and save an accumulated list of the

// hints returned by each hint provider. If no hints are provided, assume

// that provider has no preference for topology-aware allocation.

var allProviderHints [][]TopologyHint

for _, hints := range providersHints {

// 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")

allProviderHints = append(allProviderHints, []TopologyHint{{nil, true}})

continue

}

// Otherwise, accumulate the hints for each resource type into allProviderHints.

for resource := range hints {

if hints[resource] == nil {

klog.Infof("[topologymanager] Hint Provider has no preference for NUMA affinity with resource '%s'", resource)

allProviderHints = append(allProviderHints, []TopologyHint{{nil, true}})

continue

}

if len(hints[resource]) == 0 {

klog.Infof("[topologymanager] Hint Provider has no possible NUMA affinities for resource '%s'", resource)

allProviderHints = append(allProviderHints, []TopologyHint{{nil, false}})

continue

}

allProviderHints = append(allProviderHints, hints[resource])

}

}

return allProviderHints

}


遍历所有的 HintProviders,收集并存储 hint。如果 HintProviders 没有提供任何 hint,那么就默认为该 provider 没有任何资源分配。最终返回 allProviderHints.


其中 mergeFilteredHints 位于 pkg/kubelet/cm/topologymanager/policy.go:95


// Merge a TopologyHints permutation to a single hint by performing a bitwise-AND

// of their affinity masks. The hint shall be preferred if all hits in the permutation

// are preferred.

func mergePermutation(numaNodes []int, permutation []TopologyHint) TopologyHint {

// Get the NUMANodeAffinity from each hint in the permutation and see if any

// of them encode unpreferred allocations.

preferred := true

defaultAffinity, _ := bitmask.NewBitMask(numaNodes...)

var numaAffinities []bitmask.BitMask

for _, hint := range permutation {

// Only consider hints that have an actual NUMANodeAffinity set.

if hint.NUMANodeAffinity == nil {

numaAffinities = append(numaAffinities, defaultAffinity)

} else {

numaAffinities = append(numaAffinities, hint.NUMANodeAffinity)

}

if !hint.Preferred {

preferred = false

}

}

// Merge the affinities using a bitwise-and operation.

mergedAffinity := bitmask.And(defaultAffinity, numaAffinities...)

// Build a mergedHint from the merged affinity mask, indicating if an

// preferred allocation was used to generate the affinity mask or not.

return TopologyHint{mergedAffinity, preferred}

}

func mergeFilteredHints(numaNodes []int, filteredHints [][]TopologyHint) TopologyHint {

// Set the default affinity as an any-numa affinity containing the list

// of NUMA Nodes available on this machine.

defaultAffinity, _ := bitmask.NewBitMask(numaNodes...)

// Set the bestHint to return from this function as {nil false}.

// This will only be returned if no better hint can be found when

// merging hints from each hint provider.

bestHint := TopologyHint{defaultAffinity, false}

iterateAllProviderTopologyHints(filteredHints, func(permutation []TopologyHint) {

// Get the NUMANodeAffinity from each hint in the permutation and see if any

// of them encode unpreferred allocations.

mergedHint := mergePermutation(numaNodes, permutation)

// Only consider mergedHints that result in a NUMANodeAffinity > 0 to

// replace the current bestHint.

if mergedHint.NUMANodeAffinity.Count() == 0 {

return

}

// If the current bestHint is non-preferred and the new mergedHint is

// preferred, always choose the preferred hint over the non-preferred one.

if mergedHint.Preferred && !bestHint.Preferred {

bestHint = mergedHint

return

}

// If the current bestHint is preferred and the new mergedHint is

// non-preferred, never update bestHint, regardless of mergedHint's

// narowness.

if !mergedHint.Preferred && bestHint.Preferred {

return

}

// If mergedHint and bestHint has the same preference, only consider

// mergedHints that have a narrower NUMANodeAffinity than the

// NUMANodeAffinity in the current bestHint.

if !mergedHint.NUMANodeAffinity.IsNarrowerThan(bestHint.NUMANodeAffinity) {

return

}

// In all other cases, update bestHint to the current mergedHint

bestHint = mergedHint

})

return bestHint

}


mergeFilteredHints 函数处理流程如下所示:


  1. 通过 cadvisor 传递的 NUMA 节点数生成 bitmask

  2. 设置 bestHint := TopologyHint{defaultAffinity, false}如果没有符合条件的 hint,返回该 hint

  3. 取每种资源类型生成的 TopologyHints 的交叉积

  4. 对于交叉中的每个条目,每个 TopologyHint 的 NUMA 亲和力执行位计算。 在合并 hint 中将此设置为 NUMA 亲和性。

  5. 如果条目中的所有 hint 都将 Preferred 设置为 True,则在合并 hint 中的 Preferred 设置为 True。

  6. 如果条目中存在 Preferred 设置为 False 的 hint,则在合并 hint 中的 Preferred 设置为 False。 如果其 NUMA 亲和性节点数量全为 0,则在合并 hint 中的 Preferred 设置为 False。

接上文的分配说明,Container0 的 hint 为:


cpu: {{01: True}, {10: True}, {11: False}}

gpu-vendor.com/gpu: {{01: True}, {10: True}}

nic-vendor.com/nic: {{01: True}, {10: True}}


上面的算法将产生的交叉积及合并后的 hint:


cross-product entry{cpu, gpu-vendor.com/gpu, nic-vendor.com/nic} "merged" hint

{{01: True}, {01: True}, {01: True}} {01: True}

{{01: True}, {01: True}, {10: True}} {00: False}

{{01: True}, {10: True}, {01: True}} {00: False}

{{01: True}, {10: True}, {10: True}} {00: False}

{{10: True}, {01: True}, {01: True}} {00: False}

{{10: True}, {01: True}, {10: True}} {00: False}

{{10: True}, {10: True}, {01: True}} {00: False}

{{10: True}, {10: True}, {10: True}} {01: True}

{{11: False}, {01: True}, {01: True}} {01: False}

{{11: False}, {01: True}, {10: True}} {00: False}

{{11: False}, {10: True}, {01: True}} {00: False}

{{11: False}, {10: True}, {10: True}} {10: False}


生成合并的 hint 列表之后,将根据 Kubelet 配置的拓扑管理器分配策略来确定哪个为最佳 hint。


一般流程如下所示:


  1. 根据合并 hint 的“狭窄度”进行排序。狭窄度定义为 hint 的 NUMA 相似性掩码中设置的位数。设置的位数越少,hint 越窄。对于在 NUMA 关联掩码中设置了相同位数的 hint,设置为最低位的 hint 被认为是较窄的。

  2. 根据合并 hint 的 Preferred 字段排序。Preferred 为 true 的 hint 优于 Preferred 为 true 的 hint。

  3. 为 Preferred 选择具有最佳设置的最窄 hint。


在上面的示例中,当前支持的所有策略都将使用 hint{01:True}以准入该 Pod。




四、后续发展


4.1 已知问题


  1. 拓扑管理器所能处理的最大 NUMA 节点个数是 8。若 NUMA 节点数超过 8, 枚举可能的 NUMA 亲和性而生成 hint 时会导致数据爆炸式增长。

  2. 调度器不支持资源拓扑功能,当调度至该节点,但因为拓扑管理器的原因导致在该节点上调度失败。


4.2 功能特性


a. hugepage 的 numa 应用


如前所述,当前仅可用于 TopologyManager 的三个 HintProvider 是 CPUManager、MemoryManager 及 DeviceManager。 但是,目前也正在努力增加对 hugepage 的支持,TopologyManager 最终将能够在同一 NUMA 节点上分配内存,大页,CPU 和 PCI 设备。


b. 调度


当前,TopologyManager 不参与 Pod 调度决策,仅充当 Pod Admission 控制器,当调度器将 Pod 调度到某节点后,TopologyManager 才判定应该接受还是拒绝该 pod。但是可能会因为节点可用的 NUMA 对齐资源而拒绝 pod,这跟调度系统的决定相悖。


那么我们如何解决这个问题呢?当前 Kubernetes 调度框架提供实现 framework 架构,调度算法插件化,可以实现诸如 NUMA 对齐之类的调度插件。


d. Pod 对齐策略


如前所述,单个策略通过 Kubelet 命令行应用于节点上的所有 Pod,而不是根据 Pod 进行自定义配置。


当前实现该特性最大的问题是,此功能需要更改 API 才能在 Pod 结构或其关联的 RuntimeClass 中表达所需的对齐策略。


后续相关内容,请查看公众号:DCOS


https://mp.weixin.qq.com/s/mAOPWiyV2V8T1K8YZZdCgw





五、参考资料


1、kubernetes-1-18-feature-topoloy-manager-beta


2、topology manager


3、cpu manager policy


4、设计文档


发布于: 2021 年 03 月 03 日阅读数: 10
用户头像

DCOS

关注

还未添加个人签名 2020.09.22 加入

公众号:DCOS

评论

发布
暂无评论
Kubelet从人门到放弃:拓扑管理(下)