了解压力阻塞信息(PSI)指标

了解压力阻塞信息(PSI)指标

详细解释压力阻塞信息(PSI)指标,以及如何使用这些指标识别 Kubernetes 中的资源压力。
特性状态: GA since Kubernetes v1.36
More information about this feature

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 指标通过两种不同的来源公开:

  • kubelet 的 Summary API, 它在节点、Pod 和容器级别提供 PSI 数据。
  • kubelet 上的 /metrics/cadvisor 端点,它以 Prometheus 格式公开 PSI 指标。

要求

在你的 Linux 节点上启用压力阻塞信息需要满足以下条件:

  • Linux 内核版本必须为 4.20 或更高版本。
  • 内核必须使用 CONFIG_PSI=y 选项编译。大多数现代发行版默认启用该选项。 你可以运行 zgrep CONFIG_PSI /proc/config.gz 检查内核配置。
  • 有些 Linux 发行版可能已将 PSI 编译进内核,但默认将其禁用。 这种情况下,你需要在启动时向内核命令行添加 psi=1 参数来启用它。
  • 节点必须使用 cgroup v2。

理解 PSI 指标

压力阻塞信息(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 工具。

产生 CPU 压力

创建一个使用 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

观察 CPU 压力

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

产生 I/O 压力

此 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

观察 I/O 压力

使用 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

接下来

集群故障排查相关的任务页面讨论了如何使用依赖这些数据的指标管道。

最后修改 October 19, 2026 at 3:23 PM PST: [zh] Add understand-psi-metrics.md (c5786fd09f)