拓扑管理故障排查

Kubernetes 从用户角度将 Pod 在节点上的执行方式进行大量抽象。这是一种有意的设计。 然而,一些工作负载为了能够正常运行,需要在延迟和/或性能方面获得更强的保障。 kubelet 提供了一些方法,在不引入显式调度指令的前提下,实现更复杂的工作负载调度策略。

你可以在节点内管理拓扑结构。这意味着帮助 kubelet 配置主机操作系统,使 Pod 和容器能够被调度在正确的内部边界(例如 NUMA 域)上。 (NUMA 是 non-uniform memory access 的缩写,指的是由于硬件组件的物理布局及其连接方式,CPU 在拓扑上可能更接近某些特定的内存区域。)

故障排查信息来源

在拓扑管理的上下文中,你可以通过以下方式排查为什么 Pod 无法被部署或在节点上被拒绝的原因:

  • Pod 状态 —— 指示拓扑亲和性相关错误
  • 系统日志 —— 包含用于调试的重要信息,例如生成的提示
  • kubelet 状态文件 —— Memory Manager 的内部状态转储(包括 node mapmemory maps
  • 你可以使用设备插件资源 API 获取容器预留内存的信息

排查 TopologyAffinityError

此错误通常出现在以下情况下:

  • 节点可用资源不足,无法满足 Pod 的请求
  • Pod 的请求由于 Topology Manager 策略约束而被拒绝

此错误显示在 Pod 的状态中:

kubectl get pods
NAME         READY   STATUS                  RESTARTS   AGE
guaranteed   0/1     TopologyAffinityError   0          113s

使用 kubectl describe pod <id>kubectl events 获取详细错误消息:

Warning  TopologyAffinityError  10m   kubelet, dell8  Resources cannot be allocated with Topology locality

检查系统日志

搜索有关特定 Pod 的系统日志。

CPU Manager 生成的提示集合应出现在日志中。 另外,Memory Manager 为 Pod 生成的提示也可以在日志中找到。

Topology Manager 合并这些提示,计算出一个最佳提示。这个最佳提示也应出现在日志中。

最佳提示指示所有资源应如何分配。Topology Manager 根据其当前策略来测试此提示, 并基于结果决定是允许 Pod 调度到节点,或是拒绝将 Pod 调度到节点。

此外,还可以在日志中查找与 Memory Manager 相关的记录,例如找出与 cgroupscpuset.mems 更新有关的信息。

示例

检查节点上的 Memory Manager 状态

首先,部署一个示例 Guaranteed Pod,其规约如下:

apiVersion: v1
kind: Pod
metadata:
  name: guaranteed
spec:
  containers:
  - name: guaranteed
    image: consumer
    imagePullPolicy: Never
    resources:
      limits:
        cpu: "2"
        memory: 150Gi
      requests:
        cpu: "2"
        memory: 150Gi
    command: ["sleep","infinity"]

接下来,登录到部署 Pod 的节点,并检查 /var/lib/kubelet/memory_manager_state 中的状态文件:

{
   "policyName":"Static",
   "machineState":{
      "0":{
         "numberOfAssignments":1,
         "memoryMap":{
            "hugepages-1Gi":{
               "total":0,
               "systemReserved":0,
               "allocatable":0,
               "reserved":0,
               "free":0
            },
            "memory":{
               "total":134987354112,
               "systemReserved":3221225472,
               "allocatable":131766128640,
               "reserved":131766128640,
               "free":0
            }
         },
         "nodes":[
            0,
            1
         ]
      },
      "1":{
         "numberOfAssignments":1,
         "memoryMap":{
            "hugepages-1Gi":{
               "total":0,
               "systemReserved":0,
               "allocatable":0,
               "reserved":0,
               "free":0
            },
            "memory":{
               "total":135286722560,
               "systemReserved":2252341248,
               "allocatable":133034381312,
               "reserved":29295144960,
               "free":103739236352
            }
         },
         "nodes":[
            0,
            1
         ]
      }
   },
   "entries":{
      "fa9bdd38-6df9-4cf9-aa67-8c4814da37a8":{
         "guaranteed":[
            {
               "numaAffinity":[
                  0,
                  1
               ],
               "type":"memory",
               "size":161061273600
            }
         ]
      }
   },
   "checksum":4142013182
}

可以从状态文件中推断,此 Pod 被固定到两个 NUMA 节点上,即:

"numaAffinity":[
   0,
   1
],

“Pinned(固定)”一词表示,Pod 的内存消耗被约束(通过 cgroups 配置)在这些 NUMA 节点上。

这也意味着 Memory Manager 自动创建了一个新的组,该组包含这两个 NUMA 节点,即索引为 01 的 NUMA 节点。

为了分析一个组中的可用内存资源,需要将该组内所有 NUMA 节点的对应条目进行累加。

例如,组中“常规”内存的总空闲量可以通过将组内每个 NUMA 节点的空闲内存相加得到, 即 NUMA 节点 0"memory" 部分("free": 0)和 NUMA 节点 1"memory" 部分("free": 103739236352)。 因此,此组中“常规”内存的总空闲量为 0 + 103739236352 字节。

"systemReserved": 3221225472 这一行表示节点管理员通过 --reserved-memory 参数,在 NUMA 节点 0 上为 kubelet 和系统进程预留了 3221225472 字节(即 3Gi)的内存。

检查设备插件资源 API

kubelet 提供了一个名为 PodResourceLister 的 gRPC 服务,用于发现资源及其相关元数据。通过其 List gRPC 接口, 可以获取每个容器的预留内存信息,这些信息包含在 protobuf 的 ContainerMemory 消息中。

这些信息仅适用于 Guaranteed QoS 类的 Pod。


最后修改 April 22, 2026 at 11:01 AM PST: [zh] Add debug-cluster/topology.md (944a1d2c3a)