Kubernetes 节点性能测试与剖析完整指南:集群搭建、E2E 测试、pprof 剖析与 Benchmark(SIG Node)
【免费下载链接】communityKubernetes Community Documentation项目地址: https://gitcode.com/GitHub_Trending/com/community
导读
本文是 Kubernetes SIG Node 官方《Measuring Node Performance》文档的深度展开版,系统讲解在真实 Kubernetes 集群中测量 Node 组件(尤其是 Kubelet)性能时的常见陷阱、集群搭建要点,以及四类可用的测量手段:性能仪表盘、集群 E2E 性能测试、Node E2E 性能测试、Go pprof 剖析与 Benchmark。读完本文,你将能够搭建可复现的基准环境,正确运行kubelet_perf.go与node_perf_test.go两类性能测试,并用go tool pprof定位 Kubelet 的 CPU 与堆内存热点,最终产出一份可信、可比较的节点性能数据。
为什么节点性能测量容易出错:测量前的核心认知
测量节点性能(Measuring Node Performance)并不只是"跑一个压测脚本"那么简单。文档开篇即指出:有大量因素会显著影响节点性能数据,因此在搭建集群时必须格外谨慎,确保你测量的是你想要测量的东西。
其中最容易被忽视的一点是:性能会随 commit 变化剧烈波动。Kubernetes 是高速迭代的项目,每次代码提交都可能引入性能回归或优化。因此文档强烈建议:
- 精确记录测量时使用的Kubernetes 版本或 commit;
- 记录容器运行时(container runtime)版本(如 containerd、CRI-O 等);
- 记录集群的完整搭建配置(节点规格、内核、cgroup 驱动等)。
只有把这些"测量元数据"写清楚,后续才能在不同 commit 之间、不同版本之间做有意义的性能对比。从本仓库的定位来看,该文档隶属于 SIG Node 开发文档,是社区贡献者在开发 Kubelet 相关功能时必须掌握的测量方法论。
影响测量结果的关键维度
从文档与 Kubernetes 实现来看,至少有以下维度必须在测量前明确:
| 维度 | 影响 | 应对建议 |
|---|---|---|
| Addon Pod 数量与分布 | 默认 8 个 addon pod + 每节点 2 个(fluentd-elasticsearch与kube-proxy)会消耗资源并周期性触发 Kubelet 工作 | 记录每个节点上运行的 addon;必要时禁用(见下文) |
| Pod 数量 | 0 个 pod 与 100 个 pod 的节点,Kubelet 开销完全不同 | 在多个 Pod 数量档位下分别测量,并等待系统进入稳态 |
| Pod 负载类型 | 空闲 pod 与有真实负载的 pod 结果不同 | 默认用 pause 镜像;特殊场景用轻量任务(如 ping) |
| Pod 功能特性 | 是否配置探针、卷、容器数量等直接影响 Kubelet 工作 | 按目标场景配置 liveness/readiness 探针、卷等 |
| 节点数量 | 单节点易管理,多节点可并行采样更稳健 | 按需选择并记录 |
集群搭建(Cluster Set-up):建立可信的基准环境
Addon Pod:测量中最大的隐藏变量
默认情况下,Kubernetes 会运行8 个 addon pod,并且在kube-system命名空间中,每个节点还会额外运行2 个 pod:fluentd-elasticsearch和kube-proxy。这些 addon 会持续产生 CPU 与内存开销,更重要的是它们会周期性唤醒 Kubelet 执行工作,污染你的测量结果。
一个典型例子是Heapster:它会定期轮询每个节点收集 stats 数据。如果不禁用 Heapster,Kubelet 就必须持续响应这些统计请求——这部分"服务统计数据的性能成本"会被计入你的测量结果,导致你测到的不是"节点自身开销"而是"节点 + 监控栈"的开销。禁用 Heapster 可以隐藏这部分成本,得到更纯粹的 Kubelet 数据。
禁用 Addon 的方法
禁用 addon 非常简单:SSH 登录 Kubernetes master 节点,将对应 addon 从/etc/kubernetes/addons/目录移动到备份位置即可。addon 清单的完整来源可参考 cluster/addons 目录。
不过文档也提醒:禁用 addon 虽然能获得更一致的测量结果,但禁用本身也可能带来性能影响——例如缺少 kube-proxy 时网络路径行为不同,缺少 fluentd 时日志采集路径不同。因此必须结合你的测量目标决定哪些 addon 需要禁用,并在报告中明确记录。
用多少个 Pod、什么样的 Pod?
性能数据会随节点上的 Pod 数量剧烈变化:0 个 pod 和 100 个 pod 的节点,Kubelet 的负载完全不是一个量级。因此文档建议在多个 Pod 数量档位下分别测量。
在单节点集群中,通过扩展 ReplicationController 可以轻松控制 Pod 数量,例如:
$ kubectl scale replicationcontroller pause --replicas=100关键细节:必须等待系统进入稳态(steady-state)再开始测量。Pod 创建、镜像拉取、调度绑定、PLEG 同步等过程都会带来瞬时峰值,只有稳态下的数据才具有可比性。
关于 Pod 类型,文档给出两条重要经验:
- 大多数情况下,pause pod 能产生最一致的测量结果——因为系统不会被 Pod 内应用的真实负载所影响。pause 镜像是一个极简的空容器,只负责保持 Pod 沙箱存在。
- 但存在例外:Kubernetes 在部分场景下专门对"空闲 Pod"做了优化(例如cAdvisor housekeeping,即 stats 收集逻辑,会针对无所事事的容器做特殊处理)。此时让 Pod 执行一个极轻量的任务(比如简单的网络 ping)反而更能反映真实成本。
最后,还应考虑目标 Pod 需要使用的特性:如果目的是测量带探针场景下的性能,就应该使用配置了liveness 或 readiness 探针的 Pod;同样,卷、容器数量、端口等特性都应按需配置。从 Kubelet 组件文档 可知,探针由ProbeManager驱动,稳态下每个探针都会周期性运行并触发结果更新,这一机制正是"带探针 Pod 开销更高"的源码级解释。
其他搭建建议
节点数量是一个权衡:
- 单节点:更容易管理日志、Pod、环境变量,适合快速迭代测量方案;
- 多节点:可以在并行收集更多数据,得到更稳健的采样结果(例如跨节点取均值、识别异常节点)。
建议在文档中记录最终采用的节点拓扑,以及每种配置下的样本量。
性能仪表盘(Performance Dashboard):持续追踪 Kubelet 资源占用
自Kubernetes 1.22起,Kubelet 的资源使用情况被纳入Kubernetes 性能仪表盘(perf-dash)进行跟踪,地址为 perf-dash.k8s.io。该仪表盘由kubernetes/perf-tests仓库支撑,用于持续监控上游 Kubelet 的 CPU / 内存资源占用趋势,便于社区及早发现资源利用率的回归(相关讨论可参见本仓库 sig-node/archive/ci-subgroup-notes-2021.md 中关于 perf 测试的会议记录)。
对于贡献者而言,这提供了一个"零成本"的持续观测入口:在自行搭建集群做单次测量之前,可以先查看仪表盘上的历史趋势,判断某个 commit 的改动是否已经引起资源占用变化。
E2E 性能测试:整体资源占用采集(kubelet_perf.go)
测试定位与入口
对于收集节点组件整体资源使用情况,Kubernetes 提供了端到端测试:test/e2e/node/kubelet_perf.go。该测试的定位是"集群级 e2e 性能测试",与仅测试 Kubelet 的 node e2e 测试不同,它需要一套完整集群来运行。
运行前置条件
- 确保有一个按 集群搭建 一节正确配置的e2e 集群在运行(文档使用
kubetest --up拉起); - 集群必须按上文要求做好 addon、Pod 数量等配置。
运行命令
$ kubetest --test --test_args="--ginkgo.focus=resource\susage\stracking"--ginkgo.focus使用正则匹配要运行的测试用例,这里的resource usage tracking即匹配 kubelet_perf 相关的性能测试规格。
如果需要自定义测试中的 Pod 数量或其他参数,请修改测试代码后重新编译测试二进制:
$ make WHAT=test/e2e/e2e.test这是文档特别强调的一步:e2e 测试二进制是预编译的,修改参数后不重新编译,改动不会生效。
已知限制
文档明确说明:由于这些测试非常耗时,目前并未在 CI 中运行(见 kubernetes/kubernetes issue #81490 的讨论)。这意味着:
- 该测试目前主要靠社区成员手动、按需运行;
- 运行结果不具备 CI 的持续覆盖,因此更需要测量者严格遵守可复现性要求(记录 commit、版本、配置)。
Node E2E 性能测试:在性能敏感负载下测 Kubelet
测试定位与入口
与集群级 e2e 性能测试不同,Node E2E 性能测试(test/e2e_node/node_perf_test.go)的目标是在部署了"性能敏感工作负载"之后,测量节点(Kubelet)的性能表现。它属于 node e2e 测试体系,只需单节点基础设施(Kubelet + kube-apiserver + etcd),无需完整控制平面。
相关资源:
- 测试源码:
test/e2e_node/node_perf_test.go - 结果看板:TestGrid 的
sig-node-kubelet页面下的node-performance-test通道
运行方法
先按照 Node e2e 测试指南 完成环境准备(本仓库中该指南详细覆盖了 etcd、containerd、CNI 插件的安装与配置),然后运行:
$ make test-e2e-node FOCUS="Node Performance Testing" SKIP="" PARALLELISM=1参数说明:
FOCUS="Node Performance Testing":只运行名称匹配该正则的性能测试;SKIP="":不跳过任何用例(node e2e 默认会跳过[Flaky]、[Slow]、[Serial]类用例,此处显式清空);PARALLELISM=1:串行执行,保证性能测试之间互不干扰。
Node E2E 测试机制的源码视角
从本仓库 node e2e 代码文档 可以深入理解这套测试的底层机制:
- node e2e 与常规 e2e 的本质区别在于只测节点组件 Kubelet,基础设施只需要 Kubelet、kube-apiserver 与 etcd;
- 测试套件提供**本地运行器(local runner)与远程运行器(remote runner,目前仅集成 GCP)**两个入口;
- 本地运行时,
make test-e2e-node会依次:请求 sudo 权限 → 构建 Kubernetes 源码 → 启动本地 etcd、kube-apiserver、kubelet → 运行测试 → 输出结果到 STDOUT → 停止各进程; - 启动顺序由
test/e2e_node/services管理:startEtcd→startAPIServer→startNamespaceController,Kubelet 则在测试框架中以子进程方式在后台拉起; ginkgo.SynchronizedBeforeSuite阶段还会执行系统校验(--system-validate-mode,基于k8s.io/system-validators校验 docker/OS 等环境)、按NodePrePullImageList预拉取测试镜像,然后启动服务并waitForNodeReady()。
这解释了为什么性能测试能拿到"干净"的数据:整套基础设施由测试框架独立拉起,与宿主环境隔离,测量对象就是单节点上的 Kubelet 本身。
更多运行控制参数
e2e-node-tests.md 还提供了与性能测量强相关的控制参数:
- 本地有 swap 的机器需传
TEST_ARGS='--kubelet-flags="--fail-swap-on=false"',否则 Kubelet 默认拒绝启动; - 远程运行时可用
REMOTE=true,并配合IMAGES、HOSTS、IMAGE_PROJECT、INSTANCE_PREFIX等控制目标主机; RUN_UNTIL_FAILURE=true可让测试持续运行直到失败,适合排查偶发性性能异常;CLEANUP=false保留 Kubelet 进程与二进制,便于手工调试;- 查看全部可用参数:
make test-e2e-node PRINT_HELP=y。
Profiling:用 pprof 剖析 Kubelet 的 CPU 与内存
启用 pprof 端点
Kubelet 内置了Go pprof handlers(来自net/http/pprof标准库),可以通过 HTTP 端点直接拉取性能剖析数据。启用方式有两种,任选其一:
- 以命令行 flag 方式启动 Kubelet:
--enable-debugging-handlers=true - 在 Kubelet 配置文件中设置:
EnableDebuggingHandlers=true
采集 CPU profile
启用后,通过kubectl proxy建立本地代理,即可用 curl 拉取 profile:
$ kubectl proxy & Starting to serve on 127.0.0.1:8001 $ curl -G "http://localhost:8001/api/v1/proxy/nodes/${NODE}:10250/debug/pprof/profile?seconds=${DURATION_SECONDS}" > $OUTPUT $ KUBELET_BIN=_output/dockerized/bin/linux/amd64/kubelet $ go tool pprof -web $KUBELET_BIN $OUTPUT命令逐段说明:
kubectl proxy &:在本地127.0.0.1:8001建立到 API server 的代理;- curl 通过代理路径访问
nodes/${NODE}:10250上的 Kubelet 调试端点,${NODE}替换为目标节点名,${DURATION_SECONDS}为采样时长(例如 30),${OUTPUT}为保存 profile 的文件路径; KUBELET_BIN指向与运行中 Kubelet同版本的 kubelet 二进制(符号表需要与之匹配,pprof 才能正确解析调用栈);go tool pprof -web在浏览器中打开调用关系图,直观展示热点函数。
提示:
--enable-debugging-handlers=true会暴露调试端点,请仅在你有权访问的测试/开发集群上开启,并在测量完成后关闭。
采集堆内存(heap)profile
pprof 同样可以提供堆使用情况,来自/debug/pprof/heap端点:
$ curl -G "http://localhost:8001/api/v1/proxy/nodes/${NODE}:10250/debug/pprof/heap" > $OUTPUT_HEAP $ go tool pprof -web $KUBELET_BIN $OUTPUT_HEAP堆 profile 可用于定位内存泄漏或高频分配热点。关于 Go profiling 的更多细节,可参考 Go 官方的《Profiling Go Programs》。
pprof 与 Kubelet 内部机制的对应
结合 Kubelet 组件文档,你可以把 profile 中的热点函数映射到实际工作机制上:
- SyncLoop / SyncPod:Kubelet 的核心工作循环,每个 Pod 都有独立的 worker goroutine(
pod_workers.go),Pod 数量越多,这些 goroutine 的开销越明显——这正是"Pod 数量影响性能"的源码依据; - PLEG(Pod Lifecycle Event Generator):默认每 ~2 秒轮询一次运行时以检测状态变化,是稳态下周期性 CPU 开销的重要来源之一;
- cAdvisor housekeeping(stats 收集):周期性采集容器统计信息,即文档提到的"针对空闲 Pod 做优化"的机制所在;
- 探针(Probes):
ProbeManager为每个配置了探针的 Pod 运行独立 worker 周期性探测。
如果在 profile 中看到这些函数占据显著比例,就能反向指导测量设计——例如区分"稳态同步开销"与"Pod 创建峰值开销"。
Benchmarks:在写代码前先考虑 Go Benchmark
为什么先考虑 Benchmark?
文档给出了一条非常务实的工作流建议:在费尽周折搭建真实集群测量之前,先问自己——我需要的数据能否通过一个 Benchmark 测试获得?
Go 提供了极其简单的基准测试机制:只需在_test.go文件中编写形如BenchmarkXxx(b *testing.B)的函数即可。对于纯逻辑、数据结构、算法层面的性能问题(例如某种计算是否值得优化),Benchmark 远比"起一个真实集群"快速、便宜、可复现。只有那些依赖真实运行时、真实调度、真实网络栈的问题,才值得动用前文的集群测量手段。
编写 Benchmark 的标准模式
// In foo_test.go func BenchmarkFoo(b *testing.B) { b.StopTimer() setupFoo() // Perform any global setup b.StartTimer() for i := 0; i < b.N; i++ { foo() // Functionality to measure } }要点:
b.StopTimer()/b.StartTimer():把全局初始化开销排除在计时之外——setupFoo()这类一次性准备工作不应计入被测函数的耗时;- 循环体
for i := 0; i < b.N; i++:b.N由 testing 框架自动调整(从 1 开始倍增,直到运行时间稳定在基准时长阈值附近),被测函数必须放在这个循环内执行; - 被测对象
foo():只放需要测量的函数,不要混入无关工作。
运行 Benchmark
$ go test -bench=. -benchtime=${SECONDS}s foo_test.go参数说明:
-bench=.:正则匹配所有 Benchmark 函数(.匹配全部;也可写-bench=BenchmarkFoo只跑特定函数);-benchtime=${SECONDS}s:每个 Benchmark 至少运行指定的秒数(例如-benchtime=10s),比默认的固定迭代次数更能反映稳态性能;foo_test.go:被测测试文件路径。
现状与后续方向(原文档 TODO 的延续)
原文档列出的 TODO 反映了该领域仍在演进:
- 测量 docker 性能(taotao 认领);
- 扩充集群搭建章节;
- 测量磁盘使用情况(vishh 认领);
- 测量内存使用情况(yujuhong 认领);
- 增加监控 Kubelet 指标(例如使用 Prometheus)的章节。
结合本仓库 sig-node/archive/ci-subgroup-notes-2021.md 中的记录可以看到后续进展:社区曾讨论 node performance 测试文档的现状与重构(指向本文所依据的 node-performance-testing.md)、kubelet_perf.go为何未进入kubelet-serial测试、以及资源利用回归被纳入kubernetes/perf-tests仓库跟踪等议题。这说明节点性能测量是一个持续演化的领域,社区正在逐步把"手动测量"沉淀为"持续跟踪"。
总结:一份可信的节点性能测量清单
把全文要点浓缩为可直接执行的检查清单:
- 记录测量元数据:Kubernetes commit/版本、容器运行时版本、集群拓扑与配置;
- 控制变量:明确 addon pod(必要时按需禁用)、Pod 数量(多档位)、Pod 类型(默认 pause,特殊场景用轻量任务)、Pod 特性(探针、卷等)、节点数量;
- 等待稳态:任何测量开始前,确保系统已进入稳态;
- 选择测量工具:
- 持续趋势 → Kubernetes 性能仪表盘(1.22+);
- 集群整体资源占用 →
kubelet_perf.go(kubetest --test --test_args="--ginkgo.focus=resource\susage\stracking",改参数后记得make WHAT=test/e2e/e2e.test); - 单节点 Kubelet 性能 →
node_perf_test.go(make test-e2e-node FOCUS="Node Performance Testing" SKIP="" PARALLELISM=1); - 定位热点 → Kubelet pprof(
--enable-debugging-handlers=true+go tool pprof -web); - 纯逻辑性能 → 先写 Go Benchmark(
go test -bench=. -benchtime=...);
- 如实记录:测量结果必须与第 1 步的元数据绑定,才能跨版本、跨 commit 比较。
参考文档(仓库内)
- 节点性能测量(原文档)
- Node e2e 测试运行指南
- Node e2e 测试代码机制说明
- Kubelet 组件结构与同步循环解析
- SIG Node 开发文档总览
- SIG Node 职责与范围
【免费下载链接】communityKubernetes Community Documentation项目地址: https://gitcode.com/GitHub_Trending/com/community
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考