概述

Kubernetes CPU 限制看似能保护集群稳定性,实则可能因 Linux 内核 Bug 导致不必要的 CPU 流控,严重影响服务性能。Buffer 团队通过移除 CPU 限制,实现了服务响应速度最高 22 倍的提升。

核心发现:

  • ⚠️ CPU 限制触发不必要流控
  • 🐛 Linux 内核 < 4.19 存在 Bug
  • 🚀 移除限制后性能大幅提升
  • ⚖️ 需要权衡稳定性和性能

适用场景:

  • 服务响应延迟高
  • CPU 未满但仍被流控
  • 追求极致性能
  • 内核版本 < 4.19

原文链接: https://blog.fleeto.us/post/k8s-faster-services-no-cpu-limits/
推荐配合阅读: 站内《最大最小内存设置为一致》文章

背景

Buffer 的 Kubernetes 实践

基本情况:

项目数据
开始时间2016 年
管理工具kops
节点数量~60 个(AWS)
容器数量~1500 个
服务架构微服务

参考: Kubernetes 官方案例研究

CPU 限制机制

工作原理

CPU 限制(CPU Limit):

1
2
3
4
5
resources:
limits:
cpu: "800m" # 最多使用 0.8 核
requests:
cpu: "200m" # 请求 0.2 核

限制机制:

1
2
3
4
CFS 配额周期(默认): 100ms
CPU Limit: 800m = 80ms/100ms

实际执行时间超过 80ms → 触发流控 → 强制等待

官方建议:

Google 等公司强烈建议设置 CPU 限制,防止容器耗尽节点资源,导致 kubelet 等关键进程停止响应。

CPU 限制与流控

CFS(Completely Fair Scheduler)配额:

参数说明默认值
cpu.cfs_period_us配额周期100,000 微秒(100ms)
cpu.cfs_quota_us配额时间limit * period
throttled流控次数监控指标

流控效果:

1
2
正常运行:[████████████████████] 100%
触发流控:[████████░░░░░░░░░░] 等待...

影响:

  • ⏱️ 增加响应延迟
  • 📉 降低吞吐量
  • 🔄 即使 CPU 未满也会触发

问题发现

不设置 CPU 限制的风险

可能导致的问题:

1
2
3
4
5
6
7
8
9
容器占用过多 CPU

kubelet 进程停止响应

节点进入 NotReady 状态

Pod 被重新调度到其他节点

引发级联故障

奇怪的流控现象

Buffer 的发现:

数据对比:

指标设置值实际值结论
CPU Limit800m-限制
实际最大使用-200m远低于限制
预期-无流控
实际-频繁流控⚠️

流控可视化:

关键问题:

为什么 CPU 消耗仅 25% 时(200m/800m),还会频繁触发流控?

原因分析

根本原因:Linux 内核 Bug

相关资源:

资源链接/说明
GitHub Issuekubernetes#67577
Zalando 分享YouTube 视频
omio 文章Medium 帖子
Dave Chiluk 演讲Unthrottled
详细文字稿Indeed 博客

Bug 本质:

1
2
3
4
5
6
Linux 内核 < 4.19:
CFS 配额计算错误

即使 CPU 空闲也触发流控

容器被不必要地限速

解决方案

决策:移除 CPU 限制

团队决定:

  • ✅ 删除所有关键服务的 CPU 限制
  • ⚠️ 承担集群稳定性风险
  • 📊 通过监控和隔离降低风险

配套措施

1. 服务隔离

隔离策略(Taint & Toleration):

节点分类:

节点类型CPU 限制用途隔离方式
受限节点✅ 设置常规服务默认调度
不受限节点❌ 无限制关键服务Taint 隔离

配置示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 节点打 Taint
kubectl taint nodes node-1 no-cpu-limit=true:NoSchedule

# Pod 配置 Toleration
spec:
tolerations:
- key: "no-cpu-limit"
operator: "Equal"
value: "true"
effect: "NoSchedule"
resources:
requests:
cpu: "300m" # 设置合理的 requests
# limits: # 删除 CPU limits
# cpu: "800m"

优势:

  • ✅ 防止不受限服务影响其他服务
  • ✅ 故障范围可控
  • ✅ 便于排查问题

2. 设置合理的 CPU Requests

方法论:

1
2
3
1. 使用 Datadog 等工具监控数天/数周
2. 记录 CPU 使用峰值
3. CPU Request = 峰值 × 120%

示例:

计算过程:

1
2
3
观测到的 CPU 峰值: 242m
安全系数: 120%
CPU Request: 242m × 1.2 = 290m ≈ 300m

配置示例:

1
2
3
4
5
resources:
requests:
cpu: "300m" # 基于峰值设置
memory: "512Mi" # 同样基于峰值
# 删除 limits

考虑因素:

因素说明建议
流量波动面向用户的服务更高的安全系数
资源密集型计算/数据处理观察更长时间
突发流量营销活动等配合 HPA 使用

3. 增强弹性和监控

HPA(Horizontal Pod Autoscaler):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-service
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU 70% 时扩容

监控告警:

1
2
3
4
5
6
7
8
# 节点资源不足告警
- alert: NodeMemoryPressure
expr: kube_node_status_condition{condition="MemoryPressure",status="true"} == 1
for: 5m

- alert: NodeCPUHigh
expr: node_cpu_utilization > 85
for: 10m

集群自动扩容:

1
2
# AWS Cluster Autoscaler
kubectl apply -f https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml

代价:容器密度降低

资源规划变化:

1
2
3
4
5
6
7
8
9
优化前:
节点 CPU: 16 cores
容器 Requests: 200m × 80 = 16000m(密度高)

优化后:
节点 CPU: 16 cores
容器 Requests: 300m × 53 = 15900m(密度降低)

密度下降:34%

权衡:

  • ❌ 需要更多节点资源
  • ✅ 性能大幅提升
  • ✅ 服务稳定性提高

效果

性能提升

整体结果:

具体数据:

服务优化前延迟优化后延迟提升倍数
API 服务 1~200ms~50ms4x
API 服务 2~150ms~30ms5x
数据处理~500ms~100ms5x
着陆页~440ms~20ms22x

典型案例:着陆页

Buffer.com 着陆页:

改进详情:

1
2
3
响应时间:440ms → 20ms
提升:22 倍
用户体验:显著改善

业务影响:

  • ✅ 首页加载速度快 22 倍
  • ✅ 降低跳出率
  • ✅ 提升转化率
  • ✅ 改善 SEO 评分

内核修复状态

Linux 内核补丁

修复版本: Linux 4.19+

修复提交: kernel.org commit

感谢: Dave Chiluk 发现并修复

各发行版修复状态

Linux 发行版:

发行版修复版本状态说明
DebianBuster (最新)✅ 已修复更新详情
Ubuntu20.04 Focal✅ 已修复Releases
CentOS8+✅ 已修复需验证具体版本
RHEL8+✅ 已修复需验证具体版本

托管 Kubernetes:

平台修复时间状态说明
EKS2019 年✅ 已修复Roadmap Issue
GKE2020.01✅ 已修复Release Notes
kops1.18 (2020.06)✅ 已修复PR#9283
AKS2020 年✅ 已修复需验证

检查内核版本:

1
2
3
4
5
6
7
8
# 检查当前内核版本
uname -r

# 输出示例
5.4.0-42-generic # ✅ > 4.19,已修复

# 检查节点内核
kubectl get nodes -o wide

仍需注意

注意事项:

  1. 即使修复,也建议测试

  2. 监控流控指标

    1
    2
    # 查看容器流控次数
    cat /sys/fs/cgroup/cpu/cpu.stat | grep throttled
  3. 逐步推广

    • 先在非关键服务测试
    • 观察一段时间
    • 再应用到生产环境

实施指南

决策流程

是否移除 CPU 限制?

graph TD
    A[服务有延迟问题?] -->|是| B[检查流控指标]
    A -->|否| Z[保持现状]
    B --> C{CPU 使用 < 80% 但频繁流控?}
    C -->|是| D[检查内核版本]
    C -->|否| Z
    D --> E{内核 < 4.19?}
    E -->|是| F[升级内核或移除限制]
    E -->|否| G[考虑移除限制测试]
    F --> H[实施隔离策略]
    G --> H
    H --> I[设置合理 Requests]
    I --> J[配置 HPA]
    J --> K[监控观察]

实施步骤

Step 1:评估和准备

1
2
3
4
5
6
# 1. 检查当前流控情况
kubectl top pods
kubectl get pods -o json | jq '.items[] | select(.status.containerStatuses[0].state.running != null) | .metadata.name'

# 2. 收集监控数据(至少1周)
# 使用 Prometheus/Datadog 等工具

Step 2:节点隔离

1
2
3
# 为节点打标签和 Taint
kubectl label nodes node-1 cpu-unlimited=true
kubectl taint nodes node-1 cpu-unlimited=true:NoSchedule

Step 3:更新 Deployment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-service
spec:
template:
spec:
tolerations:
- key: "cpu-unlimited"
operator: "Equal"
value: "true"
effect: "NoSchedule"
containers:
- name: app
resources:
requests:
cpu: "300m" # 基于监控数据设置
memory: "512Mi"
# 移除 CPU limits

Step 4:配置 HPA

1
2
3
4
kubectl autoscale deployment my-service \
--cpu-percent=70 \
--min=3 \
--max=10

Step 5:监控和调整

1
2
3
4
5
# 监控关键指标
- CPU 使用率
- 响应延迟
- 节点资源
- Pod 重启次数

回滚计划

如果出现问题:

1
2
3
4
5
6
7
8
9
10
11
# 1. 立即恢复 CPU 限制
kubectl set resources deployment my-service \
--limits=cpu=800m

# 2. 移除 Toleration
kubectl patch deployment my-service \
-p '{"spec":{"template":{"spec":{"tolerations":[]}}}}'

# 3. 分析原因
kubectl logs -f my-service-pod
kubectl describe node node-1

最佳实践

推荐配置

生产环境配置模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
apiVersion: apps/v1
kind: Deployment
metadata:
name: production-service
namespace: production
spec:
replicas: 3
template:
spec:
# 节点选择器
nodeSelector:
workload-type: cpu-unlimited

# 容忍度(如果使用 Taint)
tolerations:
- key: "cpu-unlimited"
operator: "Equal"
value: "true"
effect: "NoSchedule"

containers:
- name: app
image: my-app:latest
resources:
requests:
cpu: "300m" # 峰值 × 1.2
memory: "512Mi" # 峰值 × 1.2
limits:
# cpu: "1000m" # 移除 CPU 限制
memory: "1Gi" # 保留内存限制

# 健康检查
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10

readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5

---
# HPA 配置
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: production-service-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: production-service
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80

监控指标

关键监控项:

指标说明告警阈值
CPU 使用率节点/Pod CPU 使用> 85%
内存使用率节点/Pod 内存使用> 90%
响应延迟P50/P95/P99 延迟基线 × 1.5
Pod 重启异常重启次数> 3次/小时
节点状态NotReady 节点> 0

注意事项

关键点:

注意事项说明
⚠️ 保留内存限制内存缺 OOM killer,必须限制
⚠️ 设置合理 Requests防止过度调度
⚠️ 配置 HPA应对突发流量
⚠️ 监控节点资源防止节点过载
⚠️ 逐步推广先测试再生产

总结

核心观点:

  1. Linux < 4.19 存在 CPU 流控 Bug

    • 即使 CPU 空闲也会触发流控
    • 严重影响服务性能
  2. 移除 CPU 限制可大幅提升性能

    • Buffer 案例:最高 22 倍提升
    • 但需要配套措施保障稳定性
  3. 推荐做法:

    • 优先升级内核到 4.19+
    • 如果无法升级,考虑移除限制
    • 必须配合隔离、监控、HPA
  4. 权衡:

    • 性能 vs 稳定性
    • 容器密度 vs 响应速度
    • 根据业务需求决策

行动建议:

1
2
3
4
5
6
7
8
9
10
11
# 1. 检查内核版本
uname -r

# 2. 监控流控指标
# 查看是否有不必要的流控

# 3. 如果确认问题
# - 优先升级内核
# - 或按本文方案移除限制

# 4. 持续监控和优化