深色模式
性能问题排查 CPU/内存/IO
面向遇到"服务变慢/超时/Pod 被 OOMKill/节点 load 高"的工程师。给出分层定位路径与可执行命令,不依赖猜测。
适用版本与前提
- Kubernetes:v1.28+
- 前提:有节点 SSH 或
kubectl debug权限、metrics-server 已安装 - 工具:
kubectl top、crictl、kubectl debug、节点侧top/iostat/perf(可选)
背景与问题
性能问题的难点在于症状与根因不在同一层:
- 应用报超时,根因可能是节点 CPU steal、存储 IO 打满、或 CPU throttling。
- Pod 被 OOMKill,根因可能是内存 limit 设太小,也可能是节点整体内存压力触发驱逐。
因此必须按层排查,而不是"重启试试"。
核心概念
| 层 | 关键指标 | 典型症状 |
|---|---|---|
| 应用 | P99 时延、错误率、GC | 慢查询、线程打满 |
| 容器 | CPU throttling、内存 working set | 时延抖动、OOMKill |
| 节点 | load、CPU、内存、磁盘 IO、网络 | 大量 Pod 同时变慢 |
| 存储 | IOPS/吞吐/时延、PVC 用量 | 有状态服务整体变慢 |
| 集群 | apiserver 时延、etcd 写延迟、调度排队 | 全集群操作变慢 |
建议
先问一个问题:是一个 Pod 慢,还是所有 Pod 都慢? 前者通常是应用/容器层,后者通常是节点或集群层。这一步能省掉一半的排查时间。
架构与原理:CPU throttling
这是 K8s 最常被误判的性能问题。CFS(Completely Fair Scheduler)按周期分配 CPU:
- 周期默认 100ms。
- 若容器在一个周期内用完
cpu limit对应的配额,会被限流(throttle),直到下个周期。 - 结果是:CPU 使用率可能显示只有 60%,但 P99 时延已经很高——平均使用率掩盖了突发限流。
生产实践
第 1 步:确认影响面
bash
# 是单个 Pod 还是整体?
kubectl top pods -n prod --sort-by=cpu | head -20
kubectl top pods -n prod --sort-by=memory | head -20
kubectl top nodes1
2
3
4
2
3
4
第 2 步:容器层 —— 查 throttling 与 OOM
bash
# CPU 限流(需要 metrics)
kubectl -n prod exec <pod> -- cat /sys/fs/cgroup/cpu.stat 2>/dev/null | grep -E 'throttled|nr_'
# 关注 nr_throttled(限流次数)与 throttled_time(累计限流时长)
# OOM 相关
kubectl get pod <pod> -n prod -o jsonpath='{.status.containerStatuses[*].lastState.terminated}'
# exitCode 137 + reason OOMKilled = 内存超限被杀
kubectl describe pod <pod> -n prod | grep -A5 -i 'oom\|killed'1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
注意
kubectl top显示的内存是 working set,与 OOM 判定口径不完全一致,且有一定延迟。判断 OOM 请以容器实际退出原因和节点dmesg为准。
第 3 步:节点层
bash
# 有没有节点级资源压力
kubectl describe node <node> | grep -A10 'Conditions\|Allocated resources'
kubectl get events -A --sort-by=.lastTimestamp | grep -i 'evict\|pressure\|oom' | tail -20
# 哪些 Pod 吃得多(在该节点上)
kubectl describe node <node> | grep -B2 -A30 'Non-terminated Pods'1
2
3
4
5
6
2
3
4
5
6
节点侧(需 SSH 或 debug):
bash
top -H # 看 load 与 CPU 分布
vmstat 1 5 # 看 si/so(swap)、wa(IO 等待)
iostat -xmd 1 5 # 看磁盘 util、await
df -h /var/lib/containerd # 容器运行时盘是否满1
2
3
4
2
3
4
版本相关
cgroup v2 环境下
cpu.stat字段与路径(/sys/fs/cgroup/cpu.stat)与 cgroup v1 不同。请以目标节点为准。
第 4 步:存储 IO
bash
kubectl -n prod describe pvc <pvc> # 看事件、绑定、用量
kubectl -n prod exec <pod> -- df -h # 容器内挂载点
kubectl -n prod exec <pod> -- sh -c 'time dd if=/dev/zero of=/data/test bs=1M count=256 oflag=direct'1
2
3
2
3
生产危险
上面的 dd 会写真实数据到业务卷,仅用于临时诊断且必须随后删除
test文件。生产环境优先用只读指标(云盘监控)而非写入测试。
第 5 步:集群层(操作变慢时)
bash
# apiserver 与 etcd 延迟
kubectl get --raw /metrics | grep -E 'apiserver_request_duration|etcd_request_duration' | head
# 调度是否积压
kubectl get pods -A --field-selector=status.phase=Pending | wc -l
kubectl logs -n kube-system deployment/kube-scheduler | tail -501
2
3
4
5
6
2
3
4
5
6
验证
修复后必须验证,而不是"感觉快了":
bash
# 对比修复前后的 P99 与 throttling 计数
kubectl -n prod exec <pod> -- cat /sys/fs/cgroup/cpu.stat | grep throttled
# 以及你的应用监控面板上的 P99 / 错误率1
2
3
2
3
回滚与清理
bash
# 资源限制调整是可回滚的(改回原值即可)
kubectl set resources deployment/web -n prod --limits=cpu=2 --requests=cpu=1
kubectl rollout status deployment/web -n prod
# 清理诊断产生的临时文件
kubectl -n prod exec <pod> -- rm -f /data/test1
2
3
4
5
6
2
3
4
5
6
故障排查速查表
| 现象 | 优先怀疑 | 关键命令 |
|---|---|---|
| CPU 使用率不高但 P99 高 | CPU throttling | cpu.stat 的 nr_throttled |
| Pod 反复重启(137) | 内存 limit 过小 / 内存泄漏 | lastState.terminated、dmesg |
| 节点上所有 Pod 变慢 | 节点 CPU/IO/内存压力 | top、iostat、vmstat |
| 有状态服务慢 | 存储 IOPS/吞吐打满 | 云盘监控、iostat |
| 全集群 kubectl 变慢 | apiserver/etcd 压力 | /metrics 延迟指标 |
| 调度一直 Pending | 资源碎片 / 亲和性冲突 | kubectl describe pod Events |
常见坑
- 只看平均使用率:CPU throttling 在平均值上不可见,必须看
nr_throttled。 - 把 limit 当 request 用:只设 limit 不设 request 会让调度器无法正确装箱,也影响 QoS 等级。
- 用
kubectl top判断 OOM:口径与延迟都不适合,应看退出原因。 - 节点盘满:容器运行时盘满会导致镜像拉取失败、Pod 无法创建,常被误判为网络问题。
- 不做基准:任何"调优"前后都要有可对比的指标,否则无法证明有效。
安全与成本权衡
- 调大
requests会提高调度密度与成本,但能减少争抢与 throttling;调小省成本但增加抖动。 - 生产核心服务建议
requests贴近实际用量、limits适度放宽(或直接不设 CPU limit 以避免 throttling,需评估风险)。[未实测]
参考资料
- Kubernetes 官方文档:Pod 与容器的资源管理,访问日期:2026-10-09。
- Kubernetes 官方文档:节点压力驱逐,访问日期:2026-10-09。
- Kubernetes 官方文档:调试运行中的 Pod,访问日期:2026-10-09。
- Kubernetes 官方文档:资源指标流水线,访问日期:2026-10-09。