ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

kubectl top失效怎么办?K8s资源监控全链路排查指南

kubectl top失效怎么办?K8s资源监控全链路排查指南 1. 为什么“kubectl top”不是万能钥匙从一个被反复问爆的运维现场说起上周三凌晨两点我正盯着屏幕等一个灰度发布完成手机突然弹出告警某核心服务 Pod 的 CPU 使用率持续飙到 98%但kubectl get pods显示状态全是 Runningkubectl describe pod里 Events 也干干净净。我下意识敲出kubectl top pod pod-name结果返回一行冰冷的报错error: Metrics API not available。那一刻办公室里只有键盘敲击声和我内心无声的叹息——这已经是本周第三次有同事在 Slack 里发问“kubectl top不工作到底怎么查 Pod 真实用了多少内存和 CPU”这个问题背后藏着一个被大量新手甚至部分中级运维人长期忽略的事实kubectl top命令本身不采集数据它只是个“取数接口”真正的资源使用数据必须由集群中独立部署的 metrics-server 组件提供支撑。这就像你家的电表读数kubectl top只是墙上那个数字显示屏而 metrics-server 才是埋在墙里的电表本体。没有它再熟练的kubectl命令也调不出真实用量。更麻烦的是这个“电表”本身还有自己的安装门槛和运行边界。它默认只采集最近 5 分钟的数据不存历史它依赖 kubelet 的 cAdvisor 接口而 cAdvisor 在某些精简版容器运行时如 containerd 的极简配置里可能被默认关闭它对节点资源的统计是基于 kubelet 上报的“容器级”指标而非操作系统内核级的top或htop结果——这意味着如果某个 Pod 里跑着一个疯狂 fork 子进程的恶意程序kubectl top pod可能完全看不到它的存在因为子进程没被容器 runtime 纳入统计维度。所以当你在搜索框里输入“kubectl 查看 K8s 内节点、Pod 资源使用情况”你真正要解决的从来不是“怎么敲命令”而是“如何构建一条从操作系统内核 → 容器运行时 → kubelet → metrics-server → kubectl 的完整可观测链路”。这条链路上任何一个环节缺失或配置错误都会导致你看到的是一片空白或者是一份严重失真的“假报告”。这也是为什么我在给新入职的 SRE 做培训时第一课永远不是教他们背命令而是带他们一起用curl直接调用 kubelet 的/metrics/cadvisor端点看原始的 Prometheus 格式指标。当他们亲眼看到container_cpu_usage_seconds_total这个指标是如何从一个数字变成kubectl top屏幕上那个百分比时那种“原来如此”的顿悟感远比记住十个命令来得深刻。今天这篇内容就从这条链路的每一个关键节点出发手把手带你把“查看资源使用情况”这件事从一句模糊的提问变成一套可验证、可调试、可落地的完整能力。2. metrics-server那个沉默却至关重要的“数据搬运工”如果你的kubectl top nodes和kubectl top pods命令始终报错 “Metrics API not available”那么第一步你必须直面这个组件——metrics-server。它不是 Kubernetes 的核心组件不像 api-server 或 scheduler 那样随集群启动而是一个可选的、需要你手动部署的附加组件。它的唯一使命就是定期从所有节点的 kubelet 上拉取 cAdvisor 暴露的容器指标并将这些原始数据聚合、转换后通过 Kubernetes 的 Metrics APImetrics.k8s.io/v1beta1暴露给kubectl top等工具消费。2.1 它到底长什么样一次真实的部署复盘去年我在一个客户现场部署 metrics-server 时踩了一个典型的“版本陷阱”。客户集群是 v1.24我习惯性地用了社区文档里推荐的 v0.6.3 版本结果部署后kubectl top nodes依然报错。排查过程如下先确认 Deployment 是否正常运行kubectl get deploy -n kube-system | grep metrics-server # 输出metrics-server 1/1 1 1 2m看起来没问题副本数是 1且已就绪。检查 Pod 日志这是最直接的线索kubectl logs -n kube-system deploy/metrics-server # 输出关键错误行 # E0315 08:22:17.345123 1 scraper.go:137] Failed to scrape node errGet \https://10.0.1.10:10250/metrics/resource\: x509: certificate signed by unknown authority nodenode-01错误非常清晰metrics-server 尝试用 HTTPS 访问 kubelet 的10250端口这是 kubelet 的安全端口用于接收 API 请求时发现 kubelet 的证书是自签名的且 metrics-server 的信任库里没有这个 CA。这在绝大多数自建集群中是常态。解决方案绕过证书校验仅限测试环境或注入 CA 证书生产环境对于快速验证我们采用前者在 metrics-server 的 Deployment 启动参数里增加--kubelet-insecure-tls# 在 metrics-server 的 deployment.yaml 中containers.args 下添加 - --kubelet-insecure-tls重新 apply 后kubectl top nodes终于返回了数据。但这只是权宜之计。在生产环境我们必须让 metrics-server 信任 kubelet 的证书。方法是将集群的 CA 证书通常是/etc/kubernetes/pki/ca.crt以 Secret 方式挂载进 metrics-server 的 Pod并通过--kubelet-certificate-authority参数指定其路径。这个过程看似繁琐但它确保了整个链路的安全性和可审计性。提示不要在生产环境长期使用--kubelet-insecure-tls。它相当于给 metrics-server 开了一扇不设防的门任何能访问该 Pod 的攻击者都可以轻易伪造请求去探测 kubelet 的敏感端点。2.2 它的“视力范围”能看什么不能看什么metrics-server 的设计哲学是“轻量、实时、够用”。它采集的核心指标只有两类CPU 和内存。具体来说它会拉取并聚合以下关键指标指标名 (Prometheus格式)含义kubectl top中对应字段container_cpu_usage_seconds_total容器累计 CPU 使用时间秒CPU(cores)container_memory_usage_bytes容器当前内存使用量字节MEMORY(bytes)它不会采集以下信息这是你必须建立的认知边界磁盘 I/Okubectl top无法告诉你某个 Pod 正在疯狂读写磁盘。你需要kubectl exec进入 Pod用iostat或iotop。网络流量kubectl top不显示 Pod 的入站/出站带宽。你需要kubectl execiftop或依赖专门的网络监控方案如 eBPF 工具。进程级详情它告诉你“这个 Pod 用了 1.2 cores”但不会告诉你“是里面的 nginx 进程占了 90%还是一个后台日志轮转脚本占了 10%”。要看到这个你得kubectl exec -it pod-name -- ps aux。历史趋势metrics-server 是一个“内存数据库”只保留最近几分钟的数据。它不存储历史因此无法回答“过去一小时的 CPU 峰值是多少”这类问题。这正是 Prometheus 这类长期存储方案存在的意义。理解这个边界至关重要。很多团队在遇到性能问题时第一反应是猛敲kubectl top发现一切“正常”就以为问题不在资源层面。殊不知真正的瓶颈可能是一次慢 SQL 导致的磁盘 IO 阻塞而kubectl top对此完全“视而不见”。所以kubectl top应该是你排查资源问题的起点而不是终点。3. 超越kubectl top当标准命令失效时的四条“逃生通道”在真实的生产环境中“标准流程”往往是最先失效的那个。metrics-server 可能因资源不足而 OOMkubelet 的 cAdvisor 端口可能被防火墙策略意外阻断甚至你的kubectl配置文件里指向的集群上下文可能已经过期。当kubectl top报错时下面这四条路径是我压箱底的“逃生通道”每一条都经过数十次线上故障的千锤百炼。3.1 通道一直连 kubelet获取最原始的 cAdvisor 数据这是最底层、最可靠的路径。cAdvisor 是 Google 开发的容器监控代理它作为 kubelet 的一部分直接嵌入在每个节点的操作系统内核中负责收集容器的实时资源指标。它的数据是 metrics-server 的唯一上游来源。操作步骤找到目标节点的 IP 和端口kubectl get nodes -o wide记下你要查的节点的INTERNAL-IP。构造 curl 请求kubelet 的安全端口是10250cAdvisor 的指标端点是/metrics/cadvisor。假设节点 IP 是10.0.1.10则命令为curl -k https://10.0.1.10:10250/metrics/cadvisor-k参数用于跳过 SSL 证书校验同 metrics-server 的--kubelet-insecure-tls。你会得到一份长达数千行的纯文本格式是 Prometheus 的 key-value 形式。精准提取你需要的指标这份原始数据太庞大我们需要过滤。例如要查名为nginx-deployment-5c7b4f8d9c-abcde的 Pod 的内存使用量可以这样 grepcurl -k https://10.0.1.10:10250/metrics/cadvisor 2/dev/null | \ grep container_memory_usage_bytes{.*podnginx-deployment-5c7b4f8d9c-abcde.*} | \ head -1 # 输出类似container_memory_usage_bytes{container,id/kubepods/burstable/pod12345678-9abc-def0-1234-567890abcdef,image,name,namespacedefault,podnginx-deployment-5c7b4f8d9c-abcde} 123456789最后的数字123456789就是当前内存使用量单位是字节Bytes。注意curl -k在生产环境有安全风险仅限紧急排障时使用。更安全的做法是先用kubectl get secret -n kube-system $(kubectl get sa default -o jsonpath{.secrets[0].name}) -o jsonpath{.data.token} | base64 -d获取一个有效的 bearer token然后在 curl 中加上-H Authorization: Bearer token。3.2 通道二进入容器内部用“老派”工具做诊断当你要诊断一个“看起来很安静但实际在拖垮整个节点”的 Pod 时kubectl top和 cAdvisor 都可能给你一个“虚假的平静”。因为它们统计的是容器 runtime 管理下的进程。而一个失控的 Pod可能通过exec或initContainer启动了大量脱离容器管控的孤儿进程。操作步骤进入容器kubectl exec -it pod-name -c container-name -- /bin/sh提示如果容器镜像里没有/bin/sh比如一些极简的 distroless 镜像你可以用kubectl debug创建一个临时的、带有调试工具的容器来“附身”到目标 Pod 上。这是 Kubernetes v1.20 引入的强大功能。执行经典诊断命令top -H按线程Thread排序找出 CPU 占用最高的线程 IDTID。ps aux --sort-%cpu | head -10列出 CPU 占用前 10 的进程。cat /sys/fs/cgroup/memory/memory.usage_in_bytes直接读取 cgroup 的内存使用量这个值与kubectl top的结果应该高度一致是交叉验证的好方法。df -h检查磁盘空间一个填满的/tmp目录足以让任何应用崩溃。我曾在一个电商大促期间用这个方法揪出一个“幽灵进程”一个 Java 应用的logrotate脚本配置错误导致它每分钟都在创建一个 2GB 的空日志文件但这些文件被创建后立刻被另一个清理脚本删除。kubectl top看不到任何异常因为内存和 CPU 都很低df -h却显示根分区使用率在 1 秒内从 40% 跳到 99%。这就是为什么永远不要放弃df和ps这些“古董级”命令。3.3 通道三解析 kubelet 的健康端点窥探节点“心跳”kubelet 不仅是资源采集者它还是节点健康的“守门人”。它暴露了一系列/healthz,/metrics等端点其中/metrics/probes端点会告诉你 kubelet 自身对节点上所有容器的存活探针liveness probe和就绪探针readiness probe的执行结果和耗时。操作步骤# 获取节点 IP 后直接 curl curl -k https://10.0.1.10:10250/metrics/probes你会看到类似这样的输出# HELP kubelet_prober_probe_duration_seconds Probe duration in seconds. # TYPE kubelet_prober_probe_duration_seconds histogram kubelet_prober_probe_duration_seconds_bucket{probeliveness,le0.1} 123 kubelet_prober_probe_duration_seconds_bucket{probeliveness,le0.2} 123 ... kubelet_prober_probe_duration_seconds_sum{probeliveness} 12.345 kubelet_prober_probe_duration_seconds_count{probeliveness} 123如果kubelet_prober_probe_duration_seconds_sum{probeliveness}这个值异常高比如超过 5 秒并且count在短时间内激增那几乎可以断定你的 Pod 里配置的 liveness probe 脚本或 HTTP 接口正在变得极其缓慢。这会导致 kubelet 频繁重启容器而kubectl top显示的只是一个刚刚被拉起、还没来得及“热身”的、低负载的 Pod。这才是问题的根源而不是资源本身。3.4 通道四利用kubectl describe挖掘被忽略的“事件”金矿kubectl describe命令的输出常常被当作一个“查看配置”的辅助工具但它真正的价值在于其末尾的Events事件部分。这里记录了 Kubernetes 控制平面针对该资源所做出的所有决策和观察到的异常。一个真实案例一个 Pod 的 CPU 使用率一直稳定在 80%但业务方反馈响应延迟极高。kubectl top pod显示一切正常。我执行kubectl describe pod pod-name在 Events 区域发现了这样几行Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning Evicted 15m kubelet The node was low on resource: memory. Container nginx was using 1234Mi, which exceeds its request of 512Mi. Normal Killing 15m kubelet Container nginx was killed due to OOM Killer.原来这个 Pod 的内存请求request只有512Mi但实际使用了1234Mi触发了 Linux 的 OOM Killer强制杀死了其主进程。随后 kubelet 又将其拉起形成了一个“启动-吃内存-被杀-重启”的死亡循环。kubectl top显示的永远是它被拉起后、OOM 发生前那短暂的“健康”瞬间。而describe的 Events则像一个忠实的事故记录仪把整个过程完整地呈现了出来。所以养成一个习惯每次kubectl top的结果让你感到困惑时立刻、马上执行kubectl describe把 Events 部分从头到尾读一遍。那里往往藏着你苦苦寻找的答案。4. 从“看得到”到“看得懂”解读资源指标背后的业务真相拿到一堆数字只是开始真正的挑战在于如何将这些冰冷的指标翻译成业务可理解的语言。一个CPU(cores)值为1.2的 Pod到底是“很忙”还是“很闲”这取决于你为它设定的“标尺”。这个标尺就是 Kubernetes 的Resource Requests 和 Limits。4.1 Requests 和 LimitsKubernetes 的“资源契约”在 Pod 的 YAML 定义中你一定会看到类似这样的片段spec: containers: - name: nginx image: nginx:alpine resources: requests: memory: 64Mi cpu: 250m limits: memory: 128Mi cpu: 500m这里的requests和limits构成了 Kubernetes 调度器和 kubelet 之间的一份“资源契约”。Requests请求这是你向集群“申请”的最低保障资源。调度器在决定将 Pod 调度到哪个节点时只会选择那些剩余可用资源 Pod requests的节点。它决定了 Pod 的“准入门槛”。Limits限制这是你为 Pod 设定的“天花板”。kubelet 会使用 cgroup 机制严格限制容器的资源使用不能超过这个值。一旦超过后果是CPU: 被“节流”throttled即被内核限制其 CPU 时间片的分配表现为进程变慢。Memory: 触发 OOM Killer直接杀死容器内的进程通常是 PID 1 的主进程。因此kubectl top返回的CPU(cores)和MEMORY(bytes)其意义必须结合requests和limits来解读。我们来看几个典型场景场景kubectl top结果requestslimits解读与行动建议健康运行CPU:200m, MEM:45MiCPU:250m, MEM:64MiCPU:500m, MEM:128Mi当前使用量远低于 requests说明资源非常充裕可以考虑适当降低 requests 以提高集群资源利用率。CPU 瓶颈CPU:480mCPU:250mCPU:500m使用量已逼近 limits且远超 requests。CPU 节流很可能已经发生业务延迟升高。应优化代码或增加 CPU requests/limits。内存危机MEM:130MiMEM:64MiMEM:128Mi使用量已超过 limitsOOM Killer 极可能已介入。kubectl describe的 Events 会证实这一点。必须立即增加 memory limits或优化内存泄漏。资源浪费CPU:10m, MEM:15MiCPU:500m, MEM:1GiCPU:500m, MEM:1Girequests 和 limits 都被严重高估占用了大量本可用于其他 Pod 的宝贵资源。应大幅下调释放集群压力。提示kubectl top默认显示的是绝对数值如123m表示 0.123 个 CPU core。如果你想直接看到使用率百分比如45%可以使用第三方插件kubectl-view-allocations或者自己写一个简单的awk脚本来计算kubectl top pods | awk {if(NR1) print $1, $2, $3, ($2*100/500), %}这里假设 CPU limit 是 500m。4.2 “2c4g 的 Pod 支持的并发量”一个伪命题的破译网络热词里频繁出现的“2c4g的pod支持的并发量”本质上是一个没有答案的问题。并发量concurrency不是一个由 CPU 和内存直接决定的静态数字它是一个受应用架构、编程语言、IO 模型、外部依赖等多重因素影响的动态变量。一个用 Go 编写的、基于 goroutine 的高并发 HTTP 服务2 个 CPU 核心可能轻松支撑上万 QPS。而一个用 PythonCPython编写的、基于同步阻塞 IO 的 Web 应用同样的 2c4g可能在几百 QPS 时CPU 就已打满因为 GIL全局解释器锁让它无法真正并行。所以与其问“2c4g 能支持多少并发”不如问我的应用在 1000 QPS 时CPU 使用率是多少内存增长曲线是怎样的当并发从 1000 提升到 2000 时P95 延迟从 100ms 涨到了 500ms瓶颈在哪里是 CPU、内存、网络还是下游数据库要回答这些问题你需要的不是kubectl top的快照而是一套完整的、带时间维度的监控体系如 Prometheus Grafana它能帮你绘制出“并发量”、“CPU 使用率”、“内存使用量”、“P95 延迟”之间的关联曲线。kubectl top只是这个体系中最前端、最轻量的一个“探针”。5. 实战避坑指南那些让资深工程师也皱眉的“小细节”在无数次的集群巡检和故障排查中我发现导致kubectl top失效或结果失真的往往不是什么高深莫测的原理而是一些极易被忽视的“小细节”。我把它们总结为“五大坑”每一个都附带了我亲测有效的解决方案。5.1 坑一kubectl top的默认时间窗口是“此刻”而非“过去一分钟”这是一个认知偏差。很多人以为kubectl top显示的是过去一分钟的平均值就像top命令的默认行为一样。但事实并非如此。kubectl top调用的是 metrics-server 的 API而 metrics-server 从 kubelet 拉取数据的频率默认是60 秒一次。这意味着你看到的是 kubelet 在上一个 60 秒周期内上报的、一个瞬时快照值。后果如果你在一个 CPU 使用率剧烈波动的应用上每隔 5 秒执行一次kubectl top你可能会看到100m,450m,200m,0m这样毫无规律的数字。这不是命令有问题而是数据本身的采样粒度太粗。解决方案接受现实对于需要秒级精度的场景kubectl top不是合适的工具。你应该使用kubectl exec进入容器用vmstat 1或pidstat -u 1这类原生命令。调整 metrics-server 采样频率高级修改 metrics-server 的启动参数--metric-resolution15s可以将采样间隔缩短到 15 秒。但这会显著增加 kubelet 和 metrics-server 的负载需谨慎评估。5.2 坑二kubectl top nodes显示的“CPU”是“可分配 CPU”而非“总 CPU”kubectl top nodes的输出中CPU(cores)列的值代表的是该节点上所有 Pod 当前使用的 CPU 总和。但它有一个前提这个值只统计了那些被 Kubernetes 管理的、设置了resources.requests的 Pod。那些没有设置 requests 的 Pod即“BestEffort” QoS 类型其 CPU 使用量不会被计入kubectl top nodes的总数。后果你可能会看到kubectl top nodes显示节点 CPU 使用率只有 30%但kubectl describe node node-name的Allocatable字段却显示cpu: 3900m而Capacity是4000m。这 100m 的差额很可能就是被那些“无证经营”的 BestEffort Pod 吃掉了。解决方案强制要求所有 Pod 设置 requests在 CI/CD 流水线中加入策略检查如使用 OPA Gatekeeper拒绝部署任何未设置resources.requests的 Pod。用kubectl describe node交叉验证Allocatable是节点真正能分配给 Pod 的资源上限Capacity是物理机的总资源。两者的差值就是被系统组件kubelet、dockerd、systemd 等占用的资源。如果Allocatable和Capacity差距过大说明你的节点上可能运行了大量非 Kubernetes 管理的进程。5.3 坑三kubectl top pods的命名空间陷阱kubectl top pods默认只查询default命名空间。这是一个极其危险的默认行为。在多租户、多环境的集群中你的核心服务可能部署在prod命名空间而你却在default里徒劳地寻找。后果kubectl top pods返回No resources found in default namespace.你误以为集群里根本没有 Pod进而怀疑 metrics-server 是否部署成功。解决方案永远显式指定命名空间kubectl top pods -n prod。设置别名在你的.bashrc或.zshrc中添加alias ktpkubectl top pods -n然后就可以用ktp prod快速切换。5.4 坑四kubectl top的权限迷宫kubectl top命令最终调用的是metrics.k8s.io/v1beta1这个 API 组。如果你的kubectl用户ServiceAccount没有被授予对该 API 组的get权限命令就会失败报错Error from server (Forbidden): ...。后果即使 metrics-server 运行完美kubectl top依然无法工作让人摸不着头脑。解决方案检查 RBAC运行kubectl auth can-i --list查看当前用户是否有getnodes和pods的权限。授予必要权限创建一个 ClusterRoleBinding将system:aggregated-metrics-readerClusterRole 绑定到你的 ServiceAccount。这是 Kubernetes 官方为 metrics API 预定义的角色。5.5 坑五kubectl top的“缓存”幻觉kubectl top的结果有时会给人一种“滞后”的感觉。比如你刚刚手动kubectl scale将一个 Deployment 的副本数从 1 扩容到 10但kubectl top pods里只看到了 2 个新 Pod 的资源使用其余 8 个显示Unknown。原因metrics-server 并不会为每一个新创建的 Pod 立即开始采集数据。它需要等待 kubelet 成功上报一次指标后才会将其纳入top的查询范围。而 kubelet 的上报又依赖于 Pod 的容器是否已成功启动并进入Running状态。解决方案耐心等待通常 30-60 秒后所有 Pod 都会出现在kubectl top的列表中。用kubectl get pods确认状态确保所有 Pod 的STATUS都是Running且READY列为1/1。只有这时kubelet 才会开始向 metrics-server 暴露其指标。这些“小细节”单独拿出来都不值一提但它们组合在一起就足以让一个经验丰富的工程师在深夜的告警电话里花费数小时去兜圈子。把它们记下来贴在你的终端旁边或者写进团队的 Wiki是避免重复踩坑最经济、最有效的方式。
返回列表