我使用 Kubernetes 的第一天涉及到 Docker 化一个应用程序并将其部署到生产集群。我正在将 Buffer 的一个最高吞吐量(且低风险)端点从单体应用程序中迁移出来。这个特定的端点正在引起越来越多的问题,并且偶尔会影响其他更高优先级的流量。
在使用 curl 进行一些手动测试后,我们决定开始将流量推送到 Kubernetes 上的新服务。在 1% 的流量时,一切看起来都很棒——然后是 10%,仍然很棒——然后在 50% 时,服务突然开始进入崩溃循环。我的第一反应是将服务从 4 个副本扩展到 20 个。这有点帮助——服务正在处理流量,但 Pod 仍然进入崩溃循环。通过使用 kubectl describe 进行一些调查,我了解到 Kubelet 由于 OOMKilled(即内存不足)而终止了 Pod。深入研究后,我意识到当我从另一个部署中复制粘贴 YAML 时,我设置了一些过于严格的内存限制。这次经历让我开始思考如何有效地设置请求和限制。
请求 vs. 限制
Kubernetes 允许对 CPU、内存和本地临时存储(v1.12 中的 Beta 功能)等资源设置可配置的请求和限制。像 CPU 这样的资源是可压缩的,这意味着容器将使用 CPU 管理策略进行限制。其他资源(如内存)由 Kubelet 监控,如果它们超过限制,则会被终止。使用不同的请求和限制配置,可以为每个工作负载实现不同的服务质量。
限制
限制是工作负载允许消耗的上限。超过请求的限制阈值将触发 Kubelet 终止 Pod。如果未设置限制,则工作负载可以消耗给定节点上的所有资源。如果有多个未设置限制的工作负载正在运行,则将以尽力而为的方式分配资源。
请求
调度器使用请求来为工作负载分配资源。工作负载可以使用所有请求的资源,而无需 Kubernetes 的干预。如果未设置限制且超过了请求阈值,则容器将被限制回请求的资源。如果设置了限制但未设置请求,则请求的资源与请求的限制相匹配。
服务质量
可以通过资源和限制实现三种基本的服务质量 (QoS)——最佳 QoS 配置将取决于工作负载的需求。

保证 QoS
可以通过仅设置限制来实现保证 QoS。这意味着容器可以使用调度器为其配置的所有资源。对于 CPU 密集型且具有相对可预测工作负载的工作负载(例如,处理请求的 Web 服务器)来说,这是一个很好的 QoS。

可突发 QoS
通过设置请求和限制来配置可突发 QoS,其中请求低于限制。这意味着容器保证可以使用高达配置请求的资源,并且如果给定节点上可用,则可以使用整个配置的资源限制。这对于具有短暂资源利用期或需要密集初始化过程的工作负载非常有用。例如,构建 Docker 容器的 worker 或运行未优化 JVM 进程的容器。

尽力而为 QoS
尽力而为 QoS 是通过既不设置请求也不设置限制来配置的。这意味着容器可以占用机器上的任何可用资源。从调度器的角度来看,这是优先级最低的任务,并且将在可突发和保证 QoS 配置之前被终止。这对于可中断和低优先级的工作负载(例如,迭代运行的幂等优化过程)非常有用。

设置请求和限制
设置良好请求和限制的关键是找到单个 Pod 的崩溃点。通过使用几种不同的负载测试技术,可以在应用程序投入生产之前了解其不同的故障模式。几乎每个应用程序在达到极限时都会有自己的一组故障模式。
为了准备测试,请确保将副本计数设置为 1,并从保守的限制集开始,例如
# limits might look something like
replicas: 1
...
cpu: 100m # ~1/10th of a core
memory: 50Mi # 50 Mebibytes
请注意,在此过程中使用限制非常重要,以便清楚地看到效果(限制 CPU 和在内存过高时终止 Pod)。随着测试迭代完成,一次更改一个资源限制(CPU 或内存)。
爬坡测试
爬坡测试会随着时间的推移增加负载,直到负载下的服务突然失败或测试完成。

如果爬坡测试突然失败,则很好地表明资源限制过于严格。当观察到突然变化时,将资源限制加倍并重复,直到测试成功完成。

当资源限制接近最佳状态(至少对于 Web 风格的服务而言)时,性能应该随着时间的推移可预测地下降。

如果随着负载增加性能没有变化,则很可能为工作负载分配了过多的资源。
持续时间测试
在运行爬坡测试并调整限制后,就该运行持续时间测试了。持续时间测试在一段较长的时间(至少 10 分钟,但更长的时间更好)内应用一致的负载,该负载略低于崩溃点。

此测试的目的是识别内存泄漏和隐藏的排队机制,这些机制在短时间的爬坡测试中可能无法捕获。如果在此阶段进行调整,则调整幅度应较小(>10% 的变化)。一个好的结果是显示性能在整个测试期间保持稳定。

保留失败日志
在经历测试阶段时,记录服务在失败时的表现方式至关重要。可以将故障模式添加到运行手册和文档中,这在生产环境中诊断问题时非常有用。我们在测试中发现的一些观察到的故障模式
- 内存缓慢增加
- CPU 占用率达到 100%
- 500 错误
- 响应时间过长
- 请求丢弃
- 响应时间变化很大
保留这些信息以备不时之需,因为有一天它们会为您或您的队友节省漫长的故障排除时间。
有用的工具
虽然可以使用 Apache Bench 等工具来应用负载,并使用 cAdvisor 来可视化资源利用率,但有些工具更适合设置资源限制。
Loader.IO
Loader.io 是一项托管的负载测试服务。它允许您配置爬坡测试和持续时间测试,在测试运行时可视化应用程序性能和负载,并快速启动和停止测试。测试结果历史记录被存储,因此很容易比较资源限制更改后的结果。

Kubescope CLI
Kubescope CLI 是一个在 Kubernetes(或本地)中运行的工具,它直接从 Docker 收集和可视化容器指标(无耻的自我推销)。它每秒收集指标(而不是像 cAdvisor 或其他集群指标收集服务那样每 10-15 秒收集一次)。使用 10-15 秒的间隔,有足够的时间流逝,以至于您可能会在测试期间错过瓶颈。使用 cAdvisor,您必须为每次测试寻找新的 Pod,因为 Kubernetes 会在资源限制被超过时终止它。Kubescope CLI 通过直接从 Docker 收集指标(您可以设置自己的间隔)并使用正则表达式来选择和过滤您想要可视化的容器来解决这个问题。

结论
我痛苦地发现,在您知道服务何时以及如何崩溃之前,该服务尚未准备好投入生产。我希望您能从我的错误中吸取教训,并使用这些技术中的一些技术来设置部署的资源限制和请求。这将为您的系统增加弹性和可预测性,这将使您的客户满意,并有望帮助您获得更多睡眠。
Harrison Harnisch 将在 12 月 10 日至 13 日在西雅图举行的 KubeCon + CloudNativeCon 北美会议上展示 充分利用 Kubernetes 的资源限制和负载测试。
评论已关闭。