This is a stable feature in Kubernetes, and has been since version v1.36. It was first available in the v1.33 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate KubeletPSI, Kubernetes ignores it but does not report any error.
作为一项稳定特性,Kubernetes 允许你配置 kubelet 收集 Linux 内核针对 CPU、内存和 I/O
使用情况的压力阻塞信息(PSI)。
这些信息在节点、Pod 和容器级别收集。从 Kubernetes v1.36 开始,KubeletPSI
特性门控被锁定为 true,无法禁用。
PSI 指标通过两种不同的来源公开:
/metrics/cadvisor 端点,它以
Prometheus 格式公开 PSI 指标。在你的 Linux 节点上启用压力阻塞信息需要满足以下条件:
CONFIG_PSI=y 选项编译。大多数现代发行版默认启用该选项。
你可以运行 zgrep CONFIG_PSI /proc/config.gz 检查内核配置。psi=1 参数来启用它。压力阻塞信息(PSI)指标针对三种资源提供:CPU、内存和 I/O。这些指标分为两类主要压力:some 和 full。
some:此值表示有部分任务(一个或多个)在某个资源上受阻。
例如,如果有部分任务在等待 I/O,该指标就会上升。这可以作为资源争用的早期信号。full:此值表示所有非空闲任务同时受阻于某个资源。
这标志着更严重的资源短缺,整个系统无法取得进展。每种压力类型提供四个指标:avg10、avg60、avg300 和 total。
avg 值表示在 10 秒、60 秒和 5 分钟滑动平均窗口内,任务受阻时间占实际耗时的百分比。
total 值是以微秒为单位的累计计数器,显示任务受阻的总时长。
以 Summary API 中的以下查询为例:
kubectl get --raw "/api/v1/nodes/$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')/proxy/stats/summary" | jq '.pods[].containers[] | select(.name=="<CONTAINER_NAME>") | {name, cpu: .cpu.psi, memory: .memory.psi, io: .io.psi}'
返回值采用如下 JSON 格式。
{
"name": "<CONTAINER_NAME>",
"cpu": {
"full": {
"total": 0,
"avg10": 0,
"avg60": 0,
"avg300": 0
},
"some": {
"total": 35232438,
"avg10": 0.74,
"avg60": 0.52,
"avg300": 0.21,
},
},
"memory": {
"full": {
"total": 539105,
"avg10": 0,
"avg60": 0,
"avg300": 0
},
"some": {
"total": 658164,
"avg10": 0.01,
"avg60": 0.01,
"avg300": 0.00,
},
}
},
"io": {
"full": {
"total": 33190987,
"avg10": 0.31,
"avg60": 0.22,
"avg300": 0.05,
},
"some": {
"total": 40809937,
"avg10": 0.52,
"avg60": 0.45,
"avg300": 0.12,
}
}
}
这里是一个简单的尖峰场景。cpu.some 的 avg10 值为 0.74,表示在过去 10 秒内,
该容器中至少有一个任务有 0.74% 的时间(0.0074 秒或 74 毫秒)受阻于 CPU。
由于同一资源上的 avg10(0.74)明显高于 avg300(0.21),
这表明资源争用是近期激增的,而不是长期持续的瓶颈。
如果持续监控并发现 avg300 也随之上升,就可以诊断出更严重、更持久的问题!
此外,注意在这个例子中 cpu.some 显示出压力,而 cpu.full 保持为 0.00。
这说明虽然有一些进程在等待 CPU 时间而被延迟,但容器整体仍在取得进展。
full 值非零则表示所有非空闲任务同时受阻,这是严重得多的问题。
尽管不如其他值那么直观,total 值 35232438 表示以微秒为单位的累计受阻时长,
借助它可以发现那些不会出现在平均值中的延迟尖峰。
最后说明一点:当你观察到 I/O 压力高而内存压力低时, 这可能说明应用是在等待磁盘吞吐,而不是因为可用内存不足而失败。 节点的内存并没有被超额分配,此时可以针对磁盘消耗另作诊断。
你可以使用一个带有压力测试工具的简单 Pod 来制造资源压力并观察 PSI 指标。
以下示例使用 agnhost 容器镜像,其中包含 stress 工具。
创建一个使用 stress 工具产生 CPU 压力的 Pod。该工作负载会给一个 CPU 核带来很高的负载。
创建名为 cpu-pressure-pod.yaml 的文件:
apiVersion: v1
kind: Pod
metadata:
name: cpu-pressure-pod
spec:
restartPolicy: Never
containers:
- name: cpu-stress
image: registry.k8s.io/e2e-test-images/agnhost:2.47
args:
- "stress"
- "--cpus"
- "1"
resources:
limits:
cpu: "500m"
requests:
cpu: "500m"
将其应用到集群:kubectl apply -f cpu-pressure-pod.yaml
Pod 运行后,你可以通过 Summary API 或 Prometheus 指标端点观察 CPU 压力。
使用 Summary API:
观察你所在节点的摘要统计信息。在另一个终端中运行:
# 将 <node-name> 替换为集群中某个节点的名称
kubectl get --raw "/api/v1/nodes/<node-name>/proxy/stats/summary" | jq '.pods[] | select(.podRef.name | contains("cpu-pressure-pod"))'
你会在 summary API 输出中看到 CPU 的 some PSI 指标上升。
some 压力的 avg10 值应升至零以上,表示有任务在 CPU 上受阻。
使用 Prometheus 指标端点:
查询 /metrics/cadvisor 端点以查看 container_pressure_cpu_waiting_seconds_total 指标。
# 将 <node-name> 替换为 Pod 所在节点的名称
kubectl get --raw "/api/v1/nodes/<node-name>/proxy/metrics/cadvisor" | \
grep 'container_pressure_cpu_waiting_seconds_total{container="cpu-stress"'
输出应显示该值不断增长,表示容器正阻塞等待 CPU 资源。
完成后清理该 Pod:
kubectl delete pod cpu-pressure-pod
此示例创建一个持续写入容器可写层文件的 Pod,导致内核页缓存增长并触发内存回收,从而产生压力。
创建名为 memory-pressure-pod.yaml 的文件:
apiVersion: v1
kind: Pod
metadata:
name: memory-pressure-pod
spec:
restartPolicy: Never
containers:
- name: memory-stress
image: registry.k8s.io/e2e-test-images/agnhost:2.47
command: ["/bin/sh", "-c"]
args:
- "i=0; while true; do dd if=/dev/zero of=testfile.$i bs=1M count=50 &>/dev/null; i=$(((i+1)%5)); sleep 0.1; done"
resources:
limits:
memory: "200M"
requests:
memory: "200M"
将其应用到集群:kubectl apply -f memory-pressure-pod.yaml
使用 Summary API:
在摘要输出中,你会观察到内存的 full PSI 指标上升,表示系统正承受显著的内存压力。
# 将 <node-name> 替换为集群中某个节点的名称
kubectl get --raw "/api/v1/nodes/<node-name>/proxy/stats/summary" | jq '.pods[] | select(.podRef.name | contains("memory-pressure-pod"))'
使用 Prometheus 指标端点:
查询 /metrics/cadvisor 端点以查看 container_pressure_memory_waiting_seconds_total 指标。
# 将 <node-name> 替换为 Pod 所在节点的名称
kubectl get --raw "/api/v1/nodes/<node-name>/proxy/metrics/cadvisor" | \
grep 'container_pressure_memory_waiting_seconds_total{container="memory-stress"'
在输出中,你会看到该指标值不断增长,表示系统正承受显著的内存压力。
完成后清理该 Pod:
kubectl delete pod memory-pressure-pod
此 Pod 通过反复向磁盘写入文件并使用 sync 将数据从内存刷出,制造 I/O 阻塞,从而产生 I/O 压力。
创建名为 io-pressure-pod.yaml 的文件:
apiVersion: v1
kind: Pod
metadata:
name: io-pressure-pod
spec:
restartPolicy: Never
containers:
- name: io-stress
image: registry.k8s.io/e2e-test-images/agnhost:2.47
command: ["/bin/sh", "-c"]
args:
- "while true; do dd if=/dev/zero of=testfile bs=1M count=128 &>/dev/null; sync; rm testfile &>/dev/null; done"
将其应用到集群:kubectl apply -f io-pressure-pod.yaml
使用 Summary API:
随着 Pod 持续写入磁盘,你会看到 I/O 的 some PSI 指标上升。
# 将 <node-name> 替换为集群中某个节点的名称
kubectl get --raw "/api/v1/nodes/<node-name>/proxy/stats/summary" | jq '.pods[] | select(.podRef.name | contains("io-pressure-pod"))'
使用 Prometheus 指标端点:
查询 /metrics/cadvisor 端点以查看 container_pressure_io_waiting_seconds_total 指标。
# 将 <node-name> 替换为 Pod 所在节点的名称
kubectl get --raw "/api/v1/nodes/<node-name>/proxy/metrics/cadvisor" | \
grep 'container_pressure_io_waiting_seconds_total{container="io-stress"'
随着 Pod 持续写入磁盘,你会看到该指标值上升。
完成后清理该 Pod:
kubectl delete pod io-pressure-pod
集群故障排查相关的任务页面讨论了如何使用依赖这些数据的指标管道。