关于linux:Kubelet从人门到放弃拓扑管理下

41次阅读

共计 15806 个字符,预计需要花费 40 分钟才能阅读完成。

<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/mA…


五、参考资料

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

2、topology manager

3、cpu manager policy

4、设计文档

正文完
 0