这篇文章已超过一年。较旧的文章可能包含过时内容。请检查页面中的信息自发布以来是否仍然正确。
Kubernetes 性能测量和路线图
无论你的容器编排系统多么灵活可靠,最终,你总有一些工作要做,并且希望它能快速完成。对于大问题,一个常见的答案是投入更多机器来解决。毕竟,更多的计算能力意味着更快,对吗?
有趣的是,增加更多节点有点像火箭方程的暴政 - 在某些系统中,增加更多机器实际上会使处理速度变慢。然而,与火箭方程不同的是,我们可以做得更好。Kubernetes v1.0 版本支持多达 100 个节点的集群。不过,我们的目标是在 2015 年底之前将支持的节点数量提高 10 倍。这篇博文将介绍我们目前的进展以及我们打算如何达到下一个性能水平。
我们衡量什么?
我们首先需要回答的问题是:“Kubernetes 管理 N 节点集群意味着什么?” 用户期望它能“合理快速”地处理所有操作,但我们需要一个精确的定义。我们决定基于以下两个指标来定义性能和可扩展性目标:
1.“API 响应速度”:所有 API 调用中 99% 在不到 1 秒内返回
2.“Pod 启动时间”:99% 的 Pod(镜像已预拉取)在 5 秒内启动
请注意,“Pod 启动时间”明确假定运行 Pod 所需的所有镜像已在运行机器上预先拉取。在我们的实验中,镜像之间存在高度的可变性(网络吞吐量、镜像大小等),这些变化与 Kubernetes 的整体性能关系不大。
选择这些指标的决定是基于我们在 Google 每周启动 20 亿个容器的经验。我们明确希望衡量面向用户流程的延迟,因为这是客户真正关心的。
我们如何衡量?
为了监控性能改进和检测回归,我们建立了持续测试基础设施。每隔 2-3 小时,我们从 HEAD 创建一个 100 节点的集群,并在其上运行我们的可扩展性测试。我们使用一台 GCE n1-standard-4(4 核,15GB 内存)机器作为主节点,使用 n1-standard-1(1 核,3.75GB 内存)机器作为节点。
在可扩展性测试中,我们明确只关注满载集群的情况(满载 N 节点集群是指运行 30 * N 个 Pod 的集群),这是从性能角度来看要求最高的场景。为了重现客户实际可能执行的操作,我们执行以下步骤:
填充 Pod 和 Replication Controller 使集群满载
产生一些负载(创建/删除额外的 Pod 和/或 Replication Controller,扩展现有的,等等)并记录性能指标
停止所有正在运行的 Pod 和 Replication Controller
抓取指标并检查它们是否符合我们的预期
值得强调的是,测试的主要部分是在满载集群(每节点 30 个 Pod,100 个节点)上进行的 - 在一个空集群中启动 Pod,即使它有 100 个节点,也会快得多。
为了衡量 Pod 启动延迟,我们使用非常简单的 Pod,其中只有一个容器运行“gcr.io/google_containers/pause:go”镜像,该镜像启动后就会永远休眠。该容器保证已在节点上预先拉取(我们将其用作所谓的 Pod 基础设施容器)。
性能数据
下表包含 100 节点集群中 Pod 启动时间的百分位数(50、90 和 99),集群负载分别为 10%、25%、50% 和 100%。
10% 满载 | 25% 满载 | 50% 满载 | 100% 满载 | |
---|---|---|---|---|
第 50 百分位数 | 0.90 秒 | 1.08 秒 | 1.33 秒 | 1.94 秒 |
第 90 百分位数 | 1.29 秒 | 1.49 秒 | 1.72 秒 | 2.50 秒 |
第 99 百分位数 | 1.59 秒 | 1.86 秒 | 2.56 秒 | 4.32 秒 |
至于 API 响应速度,以下图表展示了按操作类型和资源类型分组的 API 调用延迟的第 50、90 和 99 百分位数。但是请注意,这还包括内部系统 API 调用,而不仅仅是用户发出的调用(在本例中由测试本身发出)。
某些资源仅出现在特定图表中,这取决于该操作期间正在运行的内容(例如,当时没有创建命名空间)。
正如结果所示,我们在 100 节点集群上超出了目标,即使在满载集群中,Pod 启动时间在第 99 百分位也比 5 秒快 14%。有趣的是,列出 (LIST) Pod 的操作显著慢于其他任何操作。这是有道理的:在满载集群中,有 3000 个 Pod,每个 Pod 大约几千字节数据,这意味着每次 LIST 操作都需要处理几兆字节的数据。
#####已完成工作和一些未来计划
最初为了使 100 节点集群足够稳定以在其上运行任何测试而进行的性能工作涉及许多小的修复和调优,包括增加 apiserver 中文件描述符的限制以及复用不同请求到 etcd 的 tcp 连接。
然而,构建稳定的性能测试只是将我们集群支持的节点数量提高十倍的第一步。这项工作的结果是,我们已经投入大量精力来消除未来的瓶颈,包括:
将控制器重写为基于 watch:以前它们每隔几秒就会重新列出给定类型的对象,这给 apiserver 带来了巨大负载。
使用代码生成器生成转换和深拷贝函数:尽管使用 Go 反射的默认实现非常方便,但它们被证明极其缓慢,与生成代码相比慢了多达 10 倍。
在 apiserver 中添加缓存以避免多次反序列化从 etcd 读取的相同数据
降低状态更新频率:考虑到状态变化缓慢的特性,仅在状态改变时更新 Pod 状态,每 10 秒更新一次节点状态是合理的。
在 apiserver 实现 watch,而不是将请求重定向到 etcd:我们希望避免多次从 etcd watch 相同的数据,因为在许多情况下,这些数据无论如何都会在 apiserver 中被过滤掉。
着眼于我们 1000 节点集群的目标,拟议的改进包括:
将事件移出 etcd:它们更像是系统日志,既不是系统状态的一部分,也不是 Kubernetes 正常工作所必需的。
使用更好的 JSON 解析器:Go 中实现的默认解析器非常慢,因为它基于反射。
重写调度器,使其更高效和并发
提高 apiserver 和 Kubelet 之间的通信效率:特别是,我们计划减少每次节点状态更新时发送的数据大小。
这绝不是一个详尽的列表。我们将根据运行现有可扩展性测试和新创建测试时观察到的瓶颈添加新元素(或删除现有元素)。如果你有特定的用例或场景希望我们解决,请加入我们!
- 我们在太平洋标准时间 (PST) 每周四上午 11 点举行 Kubernetes Scale 特别兴趣小组周会,讨论正在进行的问题以及性能跟踪和改进计划。
- 如果你在此之前有特定的性能或可扩展性问题,请在 Slack 上加入我们的可扩展性特别兴趣小组:https://kubernetes.slack.com/messages/sig-scale
- 一般问题?欢迎在 Slack 上加入我们的 Kubernetes 社区:https://kubernetes.slack.com/messages/kubernetes-users/
- 提交 Pull Request 或提出 Issue!你可以在我们的 GitHub 仓库中这样做。也热烈鼓励所有人贡献自己的实验(及其结果)或通过 PR 贡献改进 Kubernetes。