返回首页

8.4 性能调优与生产最佳实践

本章目标

Kubernetes 性能优化并不是“把 CPU 调大一点”这么简单。到了生产环境,真正要优化的是:

  • 控制平面能否稳住高并发读写
  • 节点资源是否被高效利用
  • 对象数量增长后系统开销是否可控
  • 集群配置是否符合长期运行的工程规范

这一章会尽量用“能落地”的方式来讲清楚这些问题。


1. 控制平面性能瓶颈与优化方向

Kubernetes 控制平面的性能问题,本质上大多集中在三件事上:

  1. API Server 压力过大
  2. etcd 读写延迟升高
  3. 控制器 / 调度器处理队列堆积

官方关于大规模集群的最佳实践文档提醒,大集群里很多附加组件如果沿用默认资源限制,可能会因为资源不足被频繁杀掉,或者长期处于性能不佳状态::cite[169].

1.1 控制平面的常见压力源

先把“慢”拆开看:

组件 常见瓶颈
kube-apiserver 高并发 list/watch、Webhook 链过长、序列化开销大
etcd 热 key、写入频繁、磁盘延迟高、数据碎片
controller-manager 控制器过多、调和频率高、失败重试堆积
scheduler 待调度 Pod 太多、过滤 / 打分成本高

1.2 大规模场景里,list 请求为什么危险

Kubernetes 官方关于 API Streaming 的文章特别指出,在大集群中,list 请求带来的内存开销一直是 API Server 的主要压力来源之一,因为传统处理方式需要先在内存里组装完整响应,再发送给客户端::cite[463].

这意味着下面这些行为要特别谨慎:

  • 无限制 kubectl get pods -A -o yaml
  • 控制器频繁全量 list 大对象集合
  • 监控或自研平台反复扫全量资源
  • 无差别 watch 大量低价值对象

1.3 控制平面的优化方向

一个比较实用的优化思路如下:

方向 1:减少无效 API 调用

  • 能 watch 就别频繁全量 list
  • 能按 namespace / label selector 缩小范围就不要扫全量
  • 平台工具尽量缓存而不是每次重新获取全量数据

方向 2:收敛 admission 链路

每多一个 webhook,请求路径就更长、失败点就更多。生产环境里应定期审查:

  • 哪些 webhook 仍然必要
  • 哪些校验可以下沉为 CRD Schema 或 CEL
  • 哪些变更逻辑可以放到离线流程而不是同步 admission

方向 3:为控制平面组件设置合理资源

大集群文档明确提醒,附加组件在大规模场景下经常需要调整资源请求与限制,否则会不断触发资源瓶颈::cite[169].

一个教学型静态 Pod 资源配置片段如下:

apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  priorityClassName: system-node-critical
  containers:
    - name: kube-apiserver
      image: registry.k8s.io/kube-apiserver:v1.32.0
      resources:
        requests:
          cpu: "2"
          memory: 4Gi
        limits:
          cpu: "4"
          memory: 8Gi

提醒:真实控制平面组件通常由 kubeadm 或发行版管理,实际调整要遵循对应发行版方式。这里主要用于理解“控制平面组件也必须做容量规划”。

方向 4:优化 etcd 使用方式

虽然 etcd 本身是底层组件,但很多控制平面性能问题最终都会反映到 etcd。典型优化方向包括:

  • 使用低延迟磁盘
  • 避免无意义对象频繁更新 status
  • 定期压缩 / 碎片整理
  • 避免让过多客户端直接访问 etcd

方向 5:定位高频调和器

如果一个 Operator 或自定义控制器在短时间内疯狂 Requeue,就会放大控制面压力。排查时建议关注:

  • 是否因为状态比较不稳定导致反复更新
  • 是否存在无意义的 status 抖动
  • 是否对每个对象都立即重试
  • 是否把外部依赖查询放在高频 Reconcile 路径中

2. 节点资源利用率优化思路

控制平面稳住之后,第二个核心问题就是:节点资源有没有被用好。

2.1 节点利用率低,通常不是因为机器太差

更常见的原因是:

  • requests / limits 设置不合理
  • Pod 分布不均匀
  • 节点预留过多
  • 高性能工作负载没有用好 CPU / NUMA 拓扑
  • 大量僵尸工作负载长期占资源

Kubernetes 文档说明了 Pod 资源请求和限制的基本机制:调度器依赖 requests 做放置决策,而 kubelet 会按照限制和 QoS 行为执行资源控制::cite[481].

2.2 第一原则:先把 requests 配准

如果 requests 长期高估,会导致:

  • 调度器误以为节点资源不够
  • 节点实际利用率看起来很低
  • 集群扩容被提前触发

如果 requests 长期低估,又会导致:

  • CPU / 内存争抢严重
  • 噪声邻居问题
  • 节点更容易触发驱逐

一个更适合生产的 Deployment 模板如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: checkout-api
  namespace: shop
spec:
  replicas: 3
  selector:
    matchLabels:
      app: checkout-api
  template:
    metadata:
      labels:
        app: checkout-api
    spec:
      containers:
        - name: app
          image: ghcr.io/example/checkout-api:v1.0.0
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 512Mi

2.3 第二原则:关注节点预留与可分配资源

CPU Manager 文档指出,节点中可独占分配的 CPU 数量,会受到 kubelet --kube-reserved--system-reserved 预留的影响::cite[490].

这说明一个很关键的事实:

  • 节点总资源 ≠ 可供 Pod 使用的资源
  • 你必须为系统守护进程和 Kubernetes 组件预留容量

一个 kubelet 配置示例:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
kubeReserved:
  cpu: 500m
  memory: 1Gi
systemReserved:
  cpu: 500m
  memory: 1Gi
evictionHard:
  memory.available: "500Mi"
  nodefs.available: "10%"

2.4 第三原则:高性能工作负载用好资源管理器

Kubernetes 在节点侧提供了 CPU Manager、Topology Manager、Memory Manager 等资源管理能力。官方资源管理器文档说明,Topology Manager 负责协调 NUMA 对齐等优化,CPU Manager 则提供更可控的 CPU 分配::cite[491].

另外,Memory Manager 在 v1.32 已是稳定能力,面向 Guaranteed QoS Pod 提供更合适的内存 / NUMA 亲和分配::cite[492].

对低延迟业务、NFV、AI 推理、实时计算这类工作负载来说,这些能力很重要。

一个适合 Guaranteed QoS 的工作负载示例如下:

apiVersion: v1
kind: Pod
metadata:
  name: inference-engine
spec:
  containers:
    - name: engine
      image: ghcr.io/example/inference:v2.1.0
      resources:
        requests:
          cpu: "4"
          memory: 8Gi
        limits:
          cpu: "4"
          memory: 8Gi

2.5 第四原则:用调度策略改善资源分布

Pod 分布不均会导致有些节点很闲、有些节点很满。可以结合节点标签、反亲和、Topology Spread Constraints 进行优化。Kubernetes 调度文档也建议优先使用标签选择这类更结构化的方式来控制放置行为::cite[229].

例如:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-api
spec:
  replicas: 6
  selector:
    matchLabels:
      app: payment-api
  template:
    metadata:
      labels:
        app: payment-api
    spec:
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: payment-api
      containers:
        - name: app
          image: ghcr.io/example/payment-api:v1.4.2
          resources:
            requests:
              cpu: 300m
              memory: 512Mi
            limits:
              cpu: "1"
              memory: 1Gi

2.6 第五原则:理解驱逐机制,避免“假高可用”

官方文档说明,节点压力驱逐是 kubelet 在内存、磁盘、inode 等资源逼近阈值时,主动终止 Pod 以回收资源、避免节点饿死的机制::cite[496].

所以,资源优化不是一味把节点塞满,而是:

  • 让 requests 更接近真实消耗
  • 给系统预留容量
  • 预设合理驱逐阈值
  • 避免重要工作负载在压力下被错误淘汰

3. 大规模集群中的对象数量与系统开销控制

很多团队在集群规模扩大后,问题并不是 CPU 不够,而是“对象太多”。

3.1 为什么对象数量会成为系统开销

每增加一类对象,系统就可能多出这些成本:

  • etcd 存储占用
  • API Server 序列化 / 反序列化开销
  • watch 缓存压力
  • 控制器监听和调和开销
  • 权限、审计、日志体量增加

所以,在大规模场景里,对象数量本身就是成本项。

3.2 要特别警惕哪些“对象膨胀”现象

  • 每个租户都创建大量独立 ConfigMap / Secret
  • Job / CronJob 历史对象长期不清理
  • CRD 设计过细,产生海量小对象
  • 高频写 status,导致对象持续更新
  • 每个业务都自带一套 Operator 和 CRD

3.3 用配额和基线约束对象增长

ResourceQuota 文档指出,它可以限制命名空间内的聚合资源消耗与对象数量,避免某个团队独占或挤爆共享集群资源::cite[209].

一个控制对象数量和资源总量的示例:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-quota
  namespace: team-a
spec:
  hard:
    pods: "300"
    requests.cpu: "80"
    requests.memory: 160Gi
    limits.cpu: "160"
    limits.memory: 320Gi
    configmaps: "200"
    secrets: "200"
    persistentvolumeclaims: "50"

3.4 控制历史对象和临时对象生命周期

生产里经常看到下面的问题:

  • Job 运行完了,历史对象不清理
  • Helm release 或 CI 临时对象堆积
  • 失败 Pod 留一大片

一个带 TTL 的 Job 示例:

apiVersion: batch/v1
kind: Job
metadata:
  name: daily-report
spec:
  ttlSecondsAfterFinished: 3600
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: report
          image: ghcr.io/example/report:v1.0.0
          command: ["/app/report"]

3.5 设计 CRD 时就要考虑“对象密度”

如果你在一个平台系统里设计 CRD,建议从一开始就问自己:

  • 每个业务会创建多少对象?
  • 每次变更会更新多少对象?
  • 状态是不是必须实时写回?
  • 能不能把细粒度事件收敛成聚合状态?

很多平台到后期才发现,控制面被自己设计的对象模型拖慢了。

3.6 大规模集群的附加组件也需要容量规划

官方大集群最佳实践明确提到,很多 addon 在大规模集群中会消耗远超默认限制的资源,如果不调整,它们会被持续杀掉或性能不佳::cite[169].

因此,以下组件都应该单独做容量评估:

  • DNS
  • CNI 组件
  • metrics-server
  • 日志采集器
  • Ingress Controller
  • Service Mesh 控制面
  • 各类 Operator

4. 面向生产环境的配置规范与最佳实践清单

前面讲的是原理,这里给你一份更接近落地的清单。

4.1 控制平面规范

  • 控制平面组件必须有明确资源请求与限制
  • Admission Webhook 数量定期审查
  • etcd 监控延迟、磁盘、Leader 变更、DB 大小
  • 高频 list/watch 客户端做白名单治理
  • 自定义控制器必须评估 Reconcile 频率和对象规模

4.2 节点与工作负载规范

  • 所有生产 Pod 必须设置 requests
  • 关键业务优先采用 Guaranteed / 明确 QoS 策略
  • 节点设置 kubeReservedsystemReserved
  • 使用 topology spread 避免热点节点
  • 对高性能工作负载评估 CPU / Topology / Memory Manager 能力::cite[489]

4.3 对象治理规范

  • 对命名空间启用 ResourceQuota 和 LimitRange
  • 清理历史 Job、失败 Pod、无主资源
  • CRD 字段与状态更新要控制频率
  • 配置管理尽量复用,避免对象爆炸
  • 给平台对象设计生命周期和回收策略

一个简单的 LimitRange 示例:

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: team-a
spec:
  limits:
    - type: Container
      defaultRequest:
        cpu: 100m
        memory: 128Mi
      default:
        cpu: 500m
        memory: 512Mi

4.4 监控与告警规范

至少要有这些指标:

指标类型 示例
API Server 请求延迟、429、5xx、长尾 list/watch
etcd fsync 延迟、DB 大小、Leader 变更、wal sync
Scheduler 调度延迟、待调度队列长度
Kubelet 驱逐次数、容器重启、节点压力
Workload CPU throttling、OOMKilled、P95 / P99 延迟

4.5 一份可执行的生产检查表示例

clusterProductionChecklist:
  controlPlane:
    resourceSizing: true
    apiAuditAndLatencyMonitoring: true
    etcdBackupAndDefrag: true
    webhookInventoryReview: true
  nodes:
    kubeReservedConfigured: true
    systemReservedConfigured: true
    evictionThresholdConfigured: true
    topologySpreadAdopted: true
  workloads:
    requestsRequired: true
    limitRangeEnabled: true
    resourceQuotaEnabled: true
    ttlAfterFinishedEnabledForBatch: true
  governance:
    crdReviewProcess: true
    objectCountBudget: true
    addonCapacityReview: true

5. 一个实用的性能优化排查顺序

如果线上感觉“集群越来越慢”,建议按下面顺序排查:

  1. 看 API Server:延迟、429、5xx、慢请求来源
  2. 看 etcd:磁盘延迟、DB 体积、碎片、Leader 变化
  3. 看对象规模:Pod、Secret、ConfigMap、CR 数量是否异常增长
  4. 看控制器:有没有异常高频 Reconcile
  5. 看节点:requests 是否严重失真,是否频繁驱逐
  6. 看附加组件:DNS、日志、CNI、Service Mesh 是否吃掉大量资源

这套顺序的核心思想是:先看控制面,再看对象,再看节点。


6. 本章小结

这一章最重要的结论有四个:

  • 控制平面性能问题往往先表现为 API Server 和 etcd 压力::cite[463]
  • 节点利用率优化的关键不是“压榨资源”,而是 requests、预留、调度分布和 QoS 配准::cite[481]
  • 大规模集群里,对象数量本身就是成本,需要通过配额、生命周期和模型设计控制::cite[209]
  • 生产最佳实践不是零散参数,而是一套持续治理机制

如果你要记一句最关键的话,我会建议你记这个:

Kubernetes 性能优化,最终优化的不是某一个参数,而是“控制面压力、对象规模、节点装载、平台治理”四者之间的平衡。


📝 版权声明:本文为原创技术博客,转载请注明出处。

如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!

上一篇

4.2 CNI 插件选型

下一篇

2.5 Namespace 与资源隔离:多环境与多团队基础