Kubernetes 从用户角度将 Pod 在节点上的执行方式进行大量抽象。这是一种有意的设计。
然而,一些工作负载为了能够正常运行,需要在延迟和/或性能方面获得更强的保障。
kubelet 提供了一些方法,在不引入显式调度指令的前提下,实现更复杂的工作负载调度策略。
你可以在节点内管理拓扑结构。这意味着帮助 kubelet 配置主机操作系统,使 Pod 和容器能够被调度在正确的内部边界(例如 NUMA 域)上。 (NUMA 是 non-uniform memory access 的缩写,指的是由于硬件组件的物理布局及其连接方式,CPU 在拓扑上可能更接近某些特定的内存区域。)
在拓扑管理的上下文中,你可以通过以下方式排查为什么 Pod 无法被部署或在节点上被拒绝的原因:
TopologyAffinityError此错误通常出现在以下情况下:
此错误显示在 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 相关的记录,例如找出与 cgroups 和 cpuset.mems 更新有关的信息。
首先,部署一个示例 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 节点,即索引为 0 和 1 的 NUMA 节点。
为了分析一个组中的可用内存资源,需要将该组内所有 NUMA 节点的对应条目进行累加。
例如,组中“常规”内存的总空闲量可以通过将组内每个 NUMA 节点的空闲内存相加得到,
即 NUMA 节点 0 的 "memory" 部分("free": 0)和 NUMA 节点 1 的 "memory" 部分("free": 103739236352)。
因此,此组中“常规”内存的总空闲量为 0 + 103739236352 字节。
"systemReserved": 3221225472 这一行表示节点管理员通过 --reserved-memory 参数,在
NUMA 节点 0 上为 kubelet 和系统进程预留了 3221225472 字节(即 3Gi)的内存。
kubelet 提供了一个名为 PodResourceLister 的 gRPC 服务,用于发现资源及其相关元数据。通过其
List gRPC 接口,
可以获取每个容器的预留内存信息,这些信息包含在 protobuf 的 ContainerMemory 消息中。
这些信息仅适用于 Guaranteed QoS 类的 Pod。