掌握 Kubernetes 资源请求与限制,实现卓越性能
Kubernetes 提供了强大的能力,可自动化容器化应用的部署、扩展和管理。然而,要实现卓越性能并保持集群稳定性,关键在于如何为工作负载定义资源需求。错误的资源配置是导致性能瓶颈、不可预测的调度和集群资源利用率低下的主要原因。
本指南将深入探讨 Kubernetes 的资源请求(Resource Requests)和资源限制(Limits)。理解它们之间的区别并正确应用,对于确保应用程序的服务质量(QoS)、防止“吵闹的邻居”问题以及优化基础设施开销至关重要。我们将探讨这些设置如何与 Kubernetes 调度器和底层操作系统进行交互。
核心概念:请求(Requests)与限制(Limits)
在 Kubernetes 中,Pod 中的每个容器规范都必须使用 resources.requests 和 resources.limits 定义其预期的资源消耗。这些设置管理着 CPU 和内存,它们是应用程序健康最重要的两种资源。
1. 资源请求(requests)
请求(Requests)表示容器在调度时保证获得的资源量。这是 kube-scheduler 在决定将 Pod 放置在哪个节点上时所使用的最小资源量。
- 调度: 一个节点必须拥有足够的可用可分配资源,能够满足所有 Pod 请求的总和,新的 Pod 才能被调度到该节点上。
- 保证: 如果节点稍后资源不足,容器仍然会至少获得所请求的资源量(除非它受到驱逐)。
2. 资源限制(limits)
限制(Limits)定义了容器允许消耗的最大资源量。超出这些限制会导致 CPU 和内存出现特定的行为。
- CPU 限制: 如果容器尝试使用的 CPU 超过其限制,Linux 内核的 cgroups 将限制其使用,阻止其进一步消耗 CPU 周期。
- 内存限制: 如果容器超出其内存限制,操作系统将立即终止进程(OOMKill:内存不足终止)。
CPU 与内存行为
理解 Kubernetes 如何强制执行 CPU 与内存边界的质的差异至关重要:
| 资源 | 超出限制时的行为 | 强制执行机制 |
|---|---|---|
| CPU | 被限制(变慢) | cgroups (CPU 带宽控制) |
| 内存 | 被终止(OOMKill) | 内核 OOM Killer |
提示: 由于 CPU 限制通常比 OOMKill 的破坏性小,因此最佳实践通常是设置略高于典型峰值使用量的 CPU 限制,同时设置严格的内存限制以防止节点不稳定。
在 Pod 规范中定义资源
资源在 spec.containers[*].resources 块中定义。数量使用标准的 Kubernetes 后缀指定(例如,m 代表毫核,Mi 代表兆字节)。
CPU 单位定义
1CPU 单位等于 1 个完整核心(或云提供商的 vCPU)。1000m(毫核)等于 1 CPU 单位。
内存单位定义
Mi(兆字节)或Gi(千兆字节)是常见的单位。1024Mi=1Gi。
YAML 配置示例
考虑一个容器,它需要保证最少 500m CPU 和 256Mi 内存,但永远不应超过 1 CPU 和 512Mi:
resources:
requests:
memory: "256Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "1"
服务质量(QoS)类别
请求与限制之间的关系决定了分配给 Pod 的服务质量(QoS)类别。此类别决定了当资源稀缺且节点需要回收内存(驱逐)时 Pod 的优先级。
Kubernetes 定义了三种 QoS 类别:
1. 保证型(Guaranteed)
定义: Pod 中所有容器的 CPU 和内存的请求与限制都必须相同且非零。
- 优点: 这些 Pod 在资源压力期间最后被驱逐,确保最大稳定性。
- 用例: 需要严格性能隔离的关键系统组件或数据库。
2. 突发型(Burstable)
定义: Pod 中至少有一个容器定义了请求,但要么所有容器的请求和限制不相等,要么某些资源没有设置限制(尽管强烈建议设置限制)。
- 优点: 允许容器突破其请求量,利用节点上未使用的容量,直至其定义的限制。
- 驱逐优先级: 在 BestEffort Pod 之前被驱逐,但在 Guaranteed Pod 之后。
- 用例: 大多数标准无状态应用程序,可接受轻微的延迟变化。
3. 尽力而为型(BestEffort)
定义: Pod 中没有为任何容器定义请求或限制。
- 优点: 除了简单之外,没有其他好处。
- 风险: 当节点遇到资源压力时,这些 Pod 是最先被驱逐的对象。如果超出节点容量,它们也会立即受到限制或 OOMKill。
- 用例: 可轻松重启的非关键批处理作业或日志代理。
实际优化策略
有效的资源管理需要测量、迭代和仔细规划。
策略 1:准确测量并设置请求
请求应反映应用程序所需的典型或最小可持续负载。如果将请求设置得过高,您将浪费集群容量,因为即使容器未使用该资源,调度器也会保留它。
最佳实践: 使用监控工具(如 Prometheus/Grafana)分析历史使用数据。将请求设置在正常运行期间观察到的使用量的 90th 百分位数附近。
策略 2:定义保守的限制
限制充当安全网。对于内存,始终将限制设置在略高于测量峰值的水平,以防止崩溃。对于 CPU,设置限制可防止一个失控进程“饿死”同一节点上的关键同级进程。
关于 CPU 限制的警告: 过度积极地设置 CPU 限制(例如,实际需求量的 50%)会导致严重的性能下降,因为会持续受到限制。除非您有对 Guaranteed 隔离的特定需求,否则请始终优先选择 Burstable QoS。
策略 3:利用垂直 Pod 自动扩缩器(VPA)
手动调整资源既困难又耗时。垂直 Pod 自动扩缩器(VPA)会监控运行时使用情况,并随着时间的推移自动调整 Pod 规范中定义的请求和限制。VPA 帮助工作负载从配置不佳的状态(或 BestEffort)转换为最佳的 Burstable 或 Guaranteed 配置。
策略 4:针对命名空间的资源配额
为了防止跨团队或环境的资源争用,管理员应在 Namespace 级别使用资源配额(Resource Quotas)。资源配额对该命名空间内所有 CPU/内存请求和限制的总量施加聚合限制,从而确保公平性。
命名空间配额示例
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: development
spec:
hard:
requests.cpu: "10"
limits.memory: "20Gi"
这确保了 development 命名空间中所有 Pod 的总请求 CPU 不能超过 10 核,总内存限制不能超过 20Gi。
总结
掌握 Kubernetes 资源请求与限制,不仅仅是为了防止崩溃;更是为了实现可预测的性能,并最大限度地提高基础设施投资回报。通过正确设置用于调度保证的请求(Requests)和用于防止失控消耗的安全限制(Limits),您可以将 Pod 提升到所需的 QoS 类别(最好是 Burstable 或 Guaranteed)。定期审查性能指标,并考虑利用 VPA 等工具,以随着应用程序的演进保持最佳的资源对齐。