kubectl top 的数字是怎么来的?为什么刚起的 Pod 看不到数据?CPU 用量是怎么算出来的?内存为什么和 free、cgroup 的 usage 都对不上?这篇从 kubectl top 一路追到 kubelet 的原始指标和 cgroup,把每一层的数字对上。
kubectl top nodes:
NAME CPU(cores) CPU(%) MEMORY(bytes) MEMORY(%)
kx-main-control-plane 125m 1% 1173Mi 14%
kx-main-worker 332m 3% 483Mi 6%
kx-main-worker2 24m 0% 386Mi 4%
APIService v1beta1.metrics.k8s.io:service=kube-system/metrics-server;Available=[('Available', 'True')](聚合 API:apiserver 把 /apis/metrics.k8s.io 的请求转发给 metrics-server)
GET /apis/metrics.k8s.io/v1beta1/nodes 的一项:name=control-plane;timestamp=18:44:06;window=20.031s;usage={'cpu': '124109031n', 'memory': '1202164Ki'}
对应 kubectl top 里的数字:CPU 124109031n = 124m;内存 1202164Ki = 1174Mikubectl top nodes 给出每个节点的 CPU 和内存。背后是聚合 API:APIService v1beta1.metrics.k8s.io 指向 kube-system/metrics-server,Available=True,apiserver 把 /apis/metrics.k8s.io 的请求转给它。GET /apis/metrics.k8s.io/v1beta1/nodes 的一项带 timestamp、window=20.031s 和 usage: {cpu: 124109031n, memory: 1202164Ki}——124109031n 就是 kubectl top 里的 124m,1202164Ki 就是 1174Mi。
GET /api/v1/nodes/worker/proxy/metrics/resource 里的指标名:['container_cpu_usage_seconds_total', 'container_memory_working_set_bytes', 'container_start_time_seconds', 'container_swap_limit_bytes', 'container_swap_usage_bytes', 'node_cpu_usage_seconds_total', 'node_memory_working_set_bytes', 'node_swap_usage_bytes', 'pod_cpu_usage_seconds_total', 'pod_memory_working_set_bytes', 'pod_swap_usage_bytes', 'resource_scrape_error'] burn 创建后 24 秒(从 Ready 起算),kubectl top pod burn 才第一次有数据 burn 容器的 container_cpu_usage_seconds_total 两次读数:6.18(采样时刻 …877s)和 17.24(…914s)——每个读数后面都带着 kubelet 采样的时间戳;累计秒数只增不减;用采样时间戳算间隔 36.9 秒 → 平均 CPU = (17.24-6.18)/36.9 = 300m(limit 是 300m) 同一时间 kubectl top pod burn:burn 301m 3Mi(与上面的速率相符) metrics-server 做的事:每 15 秒向每个 kubelet 取一次这个累计值,相邻两次相减再除以时间,得到 CPU 用量
metrics-server 的数据来自每个节点 kubelet 的 /metrics/resource(用 kubectl get --raw /api/v1/nodes/<n>/proxy/metrics/resource 能读到):container_cpu_usage_seconds_total、container_memory_working_set_bytes、pod_*、node_* 等。CPU 是累计计数器:burn 容器(limit 300m 的死循环)两次读数 6.18 和 17.24,读数后面带着 kubelet 的采样时间戳,时间戳间隔 36.9 秒,平均 CPU = (17.24−6.18)/36.9 = 300m;同一时间 kubectl top pod burn 是 301m。(必须用采样时间戳算间隔:kubelet 的读数有缓存,用本地时钟会算错。)
不存在的 Pod:Error from server (NotFound): pods "nope" not found fresh 刚 Ready(0.5 秒)时 kubectl top:退出码 1;Error from server (NotFound): podmetrics.metrics.k8s.io "k102/fresh" not found fresh 从创建到 top 里第一次出现:23 秒(metrics-server 每 15 秒采集一次,新容器要有两次样本才能算出速率)
新建的 fresh Pod 刚 Ready(0.5 秒)时 kubectl top 返回 podmetrics.metrics.k8s.io "k102/fresh" not found;直到创建后 23 秒才第一次出现——metrics-server 每 15 秒采集一次,新容器要有两次样本才能算出 CPU 速率。刚起的 Pod 看不到 top 数据是正常的,HPA 也因此会对刚启动的 Pod 保持谨慎(见 k50)。
mem 容器(进程 80 MiB 匿名内存 + 写了 150 MiB 文件的页缓存):cgroup memory.current=238 MiB;其中 anon=83 MiB,file(页缓存)=150 MiB,inactive_file=150 MiB working set = memory.current − inactive_file = 88 MiB kubectl top pod mem:mem 37m 87Mi(与 working set 一致,而不是 memory.current;页缓存里不活跃的部分被扣掉了,kubelet 的节点内存压力驱逐也看 working set,见 k31)
mem 容器有 80 MiB 匿名内存加 150 MiB 页缓存:cgroup memory.current 是 238 MiB,其中 anon 83 MiB、file 150 MiB、inactive_file 150 MiB;working set = memory.current − inactive_file = 88 MiB;kubectl top pod mem 显示 87Mi,与 working set 一致,而不是 238 MiB。不活跃的页缓存可回收,不算进去。kubelet 的节点内存压力驱逐也看 working set(见 k31)。