本文发布已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。

Kubernetes 性能测量和路线图

无论你的容器编排系统多么灵活和可靠,最终,你总有一些工作要做,并且希望它能快速完成。对于大型问题,一个常见的答案就是投入更多的机器。毕竟,更多的计算能力 = 更快,对吧?

有趣的是,添加更多节点有点像火箭方程的暴政- 在某些系统中,添加更多机器实际上可能会使你的处理速度变慢。然而,与火箭方程不同,我们可以做得更好。v1.0 版本的 Kubernetes 支持多达 100 个节点的集群。但是,我们的目标是到 2015 年底将我们支持的节点数量增加 10 倍。这篇博文将介绍我们目前的情况以及我们打算如何实现下一个性能水平。

我们衡量什么?

我们需要回答的第一个问题是:“Kubernetes 可以管理 N 节点集群意味着什么?” 用户期望它能“合理快速”地处理所有操作,但我们需要对此进行精确的定义。我们决定基于以下两个指标定义性能和可伸缩性目标

  1. 1. “API 响应速度”:99% 的 API 调用在 1 秒内返回

  2. 2. “Pod 启动时间”:99% 的 Pod(带有预先拉取的镜像)在 5 秒内启动

请注意,对于“Pod 启动时间”,我们明确假设运行 Pod 所需的所有镜像都已经预先拉取到它将运行的机器上。在我们的实验中,镜像之间存在高度的可变性(网络吞吐量、镜像大小等),这些变化与 Kubernetes 的整体性能无关。

选择这些指标的决定是基于我们在谷歌每周启动 20 亿个容器的经验。我们明确希望衡量面向用户的流程的延迟,因为这才是客户真正关心的。

我们如何衡量?

为了监控性能改进并检测回归,我们建立了一个持续测试基础设施。每隔 2-3 小时,我们都会从 HEAD 创建一个 100 节点集群,并在其上运行我们的可伸缩性测试。我们使用 GCE n1-standard-4(4 核,15GB 内存)机器作为主节点,并使用 n1-standard-1(1 核,3.75GB 内存)机器作为节点。

在可伸缩性测试中,我们明确只关注完整集群的情况(完整的 N 节点集群是其中运行 30 * N 个 Pod 的集群),这是从性能角度来看要求最高的场景。为了重现客户可能实际执行的操作,我们执行以下步骤

  • 填充 Pod 和复制控制器以填满集群

  • 生成一些负载(创建/删除额外的 Pod 和/或复制控制器,扩展现有的 Pod 和复制控制器等)并记录性能指标

  • 停止所有正在运行的 Pod 和复制控制器

  • 抓取指标并检查它们是否符合我们的期望

值得强调的是,测试的主要部分是在完整集群(每个节点 30 个 Pod,共 100 个节点)上完成的 - 在空集群中启动 Pod,即使它有 100 个节点也会快得多。

为了测量 Pod 启动延迟,我们使用非常简单的 Pod,只有一个容器运行“gcr.io/google_containers/pause:go”镜像,该镜像启动然后永远休眠。该容器保证已经预先拉取到节点上(我们将其用作所谓的 pod-infra-container)。

性能数据

下表包含 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 调用,而不仅仅是用户发出的 API 调用(在本例中,由测试本身发出)。

get.pngput.png

delete.pngpost.png

list.png

某些资源仅出现在某些图表上,这取决于该操作期间正在运行的内容(例如,当时没有放置命名空间)。

正如你在结果中看到的那样,对于我们的 100 节点集群,我们已经提前达到了目标,即使在完全打包的集群中,Pod 启动时间在第 99 百分位数时也比 5 秒快 14%。有趣的是,列出(LIST)Pod 的速度明显慢于任何其他操作。这是有道理的:在完整集群中,有 3000 个 Pod,每个 Pod 大约几 KB 的数据,这意味着每次列出都需要处理数 MB 的数据。

#####已完成的工作和一些未来计划

为了使 100 节点集群足够稳定以在其上运行任何测试,最初的性能工作涉及许多小修复和调整,包括增加 apiserver 中文件描述符的限制以及重用对 etcd 的不同请求之间的 TCP 连接。

然而,构建稳定的性能测试只是将我们的集群支持的节点数量增加十倍的第一步。作为这项工作的结果,我们已经投入了大量精力来消除未来的瓶颈,包括

  • 重写控制器以基于 watch:以前,它们每隔几秒钟重新列出给定类型的对象,这给 apiserver 带来了巨大的负载。

  • 使用代码生成器生成转换和深拷贝函数:尽管使用 Go 反射的默认实现非常方便,但事实证明它们非常慢,与生成的代码相比,慢了多达 10 倍。

  • 向 apiserver 添加缓存,以避免多次反序列化从 etcd 读取的相同数据

  • 降低更新状态的频率:考虑到状态变化的缓慢性质,仅在发生更改时更新 Pod 状态以及每 10 秒更新一次节点状态才有意义。

  • 在 apiserver 中实现 watch,而不是将请求重定向到 etcd:我们更希望避免多次从 etcd 监视相同的数据,因为在许多情况下,无论如何都会在 apiserver 中将其过滤掉。

展望我们 1000 节点集群的目标,拟议的改进包括

  • 将事件从 etcd 中移出:它们更像是系统日志,既不是系统状态的一部分,也不是 Kubernetes 正确运行的关键。

  • 使用更好的 JSON 解析器:Go 中实现的默认解析器非常慢,因为它基于反射。

  • 重写调度器,使其更高效和并发

  • 提高 apiserver 和 Kubelet 之间通信的效率:特别是,我们计划减少每次更新节点状态时发送的数据大小。

这绝不是一个详尽的列表。我们将根据在运行现有可伸缩性测试和新创建的测试时观察到的瓶颈添加新元素(或删除现有元素)。如果你有希望我们解决的特定用例或场景,请加入我们!

  • 我们每周四太平洋标准时间上午 11 点举行 Kubernetes 规模特别兴趣小组会议,在会上我们讨论正在进行的问题以及性能跟踪和改进计划。
  • 如果你在此之前有具体的性能或可伸缩性问题,请加入我们在 Slack 上的可伸缩性特别兴趣小组:https://kubernetes.slack.com/messages/sig-scale
  • 一般问题?请随时加入我们在 Slack 上的 Kubernetes 社区:https://kubernetes.slack.com/messages/kubernetes-users/
  • 提交拉取请求或创建 issue!你可以在我们的 GitHub 存储库中执行此操作。我们也热情地鼓励每个人贡献自己的实验(及其结果)或 PR 贡献来改进 Kubernetes。