
说实话一个 Kubernetes 集群被说性能不行的时候很少是单点问题。我维护过不少集群用户抱怨的往往是接口变慢了Pod 老重启节点 CPU 被打满但一层层追下去原因可能出现在内核参数上、调度策略上、甚至在监控数据本身就没采对。这正是《Kubernetes 集群性能优化从调优到监控》这个主题有意思的地方调优不是改几个参数监控也不是为了看几张图两者本质上是一件事——把集群里每一层的行为量化清楚再针对瓶颈做精准调整。这篇内容适合刚把集群跑起来、准备认真做生产化的人也适合已经被线上问题折腾过几次、想把调优经验系统化的运维和开发同学。1. 先想清楚调优和监控为什么永远分不开1.1 性能优化不是玄学是一场有输入、有反馈的实验我第一次做 Kubernetes 集群性能优化的时候犯过一个典型错误到处找最佳实践照着网上的文章把内核参数改了一遍把 kubelet 配置调了一遍重启完发现现象有缓解但说不清到底是哪项改动起了作用甚至不确定是不是只有心理作用。后来我才意识到性能优化本质上是在做对照组实验。任何改动之前必须有一个能反映问题的量化指标改动之后还要靠同一套指标来做验证。如果没有监控体系提前就位优化动作就失去了参照物出了问题也分不清是本次改动引入的还是本来就存在的。这也是我把监控放在调优前面讲的原因。集群里任何一个卡顿现象落到指标上都应该是可解释的CPU 用量高不高、是不是被 CFS 限流了、内存是否接近驱逐线、etcd 的 fsync 耗时是否飙升、API Server 的请求延迟有没有毛刺。把这些指标先采起来调优才有底气动手。1.2 从用户抱怨到根因其实是一条指标链路我曾经帮一个业务团队排查严重超时问题。业务方描述得很模糊一到下午高峰期部分请求会卡好几秒。看 Pod CPU 用量平均值不高看节点负载也不像被打满。后来用了监控里的容器 CPU throttle 指标才发现部分 Pod 的 CPU Limit 设得偏低流量稍大就周期性被限流外部表现就是请求排队和延迟毛刺。这种问题如果不看底层指标单靠应用日志很难定位因为应用本身没有报错只是慢了。所以你在构建自己的集群优化思路时也要建立类似的链路思维终端用户体验 - 服务 RED 指标 - Pod 资源指标 - 节点系统指标 - 控制面组件指标。每一层都有各自的角色越往下越接近物理真相越往上越接近业务感受。调优时通常从下往上排查从内核和节点开始确认底层没有瓶颈再逐步看工作负载层。2. 节点层调优先把地基夯实再谈上层建筑2.1 内核参数不要抄一堆只改与集群强相关的几项节点层调优最常见的错误是过度。Linux 内核参数有上千个真正跟 Kubernetes 运行相关的其实集中在文件句柄、连接跟踪、本地端口范围、进程数这几个维度。我一般在所有集群节点上统一落一份/etc/sysctl.d/99-k8s.conf内容不多但每一项都有明确用途# /etc/sysctl.d/99-k8s.conf vm.swappiness 10 vm.overcommit_memory 1 fs.file-max 20971520 fs.inotify.max_user_instances 8192 fs.inotify.max_user_watches 524288 net.core.somaxconn 32768 net.core.netdev_max_backlog 16384 net.ipv4.tcp_max_syn_backlog 8096 net.ipv4.tcp_tw_reuse 1 net.ipv4.ip_local_port_range 1024 65535 net.netfilter.nf_conntrack_max 1048576逐项解释一下vm.swappiness 10默认 60 在服务器上太高会让内核倾向于把不常用的内存页换到 swap而 K8s 节点上跑的大多是常驻型服务swap 换页一旦发生延迟很难看。调到 10 只是降低换页倾向不会完全禁用 swap。vm.overcommit_memory 1让内核总是允许超过 CommitLimit 的内存申请。这里要注意这个参数配合 K8s 的 cgroup 内存隔离是安全的因为容器内存超限会在 cgroup 层被杀掉而不是让整个节点直接 OOM但前提是你得把 kubelet 的驱逐阈值设置好见下一节。fs.inotify.max_user_watches、max_user_instances这两项跟 ConfigMap/Secret 挂载更新有关。Kubelet 通过 inotify 监听挂载文件变化集群里 Pod 数量多了、ConfigMap 多了很容易触到默认的上限。之前遇到过一次现象更新 ConfigMap 后部分 Pod 没有感知到变化排查半天最后发现是节点的 inotify watch 耗尽kubelet 根本没收到事件。后面几个net.*参数主要是应对高并发场景。Kubernetes 集群里 Service、Pod IP 变化很频繁节点上 NAT 和连接越多conntrack 表越容易被占满。nf_conntrack_max调大是常见做法但调大以后还要配合tcp_tw_reuse这类参数否则 TIME_WAIT 连接堆积仍然会导致端口耗尽。改完之后记得sysctl --system生效并用sysctl -a | grep 参数名验证。注意真正生产环境我一般还会配合节点健康检查脚本防止有人手动改了系统参数后重启丢失。提示每次修改内核参数后建议顺带看一下dmesg -T | tail有些参数与云厂商的虚拟化层冲突时会留下显式报错能省掉后面大量的怀疑时间。2.2 Kubelet 预留与驱逐阈值别让节点被 Pod 打趴Kubernetes 节点上跑的进程不止是 Pod还有 kubelet、容器运行时、systemd 等系统服务。如果你不提前给它们预留资源调度器会认为节点上的 CPU、内存全部可以分配给 Pod一旦 Pod 用量上去了系统组件先饿死节点状态开始异常Pod 被驱逐最终整个节点进入恶性循环。Kubelet 的--system-reserved和--kube-reserved就是干这个的。我在生产集群里一般会按节点规格分档设置比如 16C32G 的工作节点常见的配置是这样apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration evictionHard: memory.available: 500Mi nodefs.available: 10% imagefs.available: 15% kubeReserved: cpu: 500m memory: 1Gi systemReserved: cpu: 500m memory: 1Gi这里的逻辑是kubelet 向调度器汇报的可分配资源等于节点总资源减去 kubeReserved 和 systemReserved 的预留量。预留得太多会浪费可调度资源预留得太少则保护不了系统稳定性。我的经验是先从节点规格的 5%-8% 起步跑一阵记录真实用量再微调而不是拍脑袋定死。驱逐阈值同样关键。当节点内存逼近memory.availablekubelet 会开始驱逐优先级最低的 Pod。evictionHard是硬性触发线我这边还会配evictionSoft和evictionSoftGracePeriod给系统留出温和处理的缓冲时间。特别提醒这里的nodefs和imagefs分别对应 kubelet 的工作目录和容器镜像存储目录如果你用 containerd一般imagefs和nodefs在同一块磁盘这块磁盘满了会有各种诡异问题比如镜像拉不下来、Pod 无法启动而这些现象在监控里往往看不出 CPU 或内存异常。2.3 CPU Manager 和拓扑感知要不要上取决于你的业务Kubernetes 默认的 CPU 分配是允许 Pod 在节点上所有可用的 CPU 核心间自由调度的这在多数业务场景下没有问题。但对于延迟敏感型应用比如网络转发组件、实时数据处理上下文切换和 CPU 缓存命中率会带来明显抖动。此时可以给 kubelet 开启CPUManagerPolicy: static让满足条件的 Guaranteed QoS Pod 独占一组物理核心。条件包括Pod 中的所有容器都设置了 CPU request 等于 CPU limit并且 CPU 请求值是整数。开启后有两点要提醒。第一static 策略只对整数核的 Guaranteed Pod 生效小数核的 Pod 仍然走原来的共享池所以你要提前规划哪些工作负载需要独占哪些可以共享。第二如果你的机器是 NUMA 架构单开 CPU Manager 还不够最好配合 Topology Manager 的single-numa-node策略让 Pod 的 CPU、内存、设备都尽量落在同一个 NUMA 节点内。我实测过某些对内存带宽敏感的任务开启后延迟能下降 10%-20%但代价是调度约束变强碎片化会加剧小规格节点上可能反而出现调度不下的情况。不要为了高级特性而上这些能力。我见过不少集群开着 static CPU Manager但业务全是普通的 Web API效果不明显还平白增加了排障复杂度。CPU Manager 这类特性的正确姿势是先有明确的性能指标波动再考虑针对性引入。3. 控制面与网络层瓶颈常常藏在看不见的地方3.1 etcd 是整个控制面的命门集群控制面的很多慢最后都能追到 etcd。你创建一个 Deployment、更新一次 Service所有 API 对象的读写最终落到 etcd。如果 etcd 慢了API Server 的请求就会排队然后 kubectl 命令变慢、控制器调谐变慢、节点心跳上报变慢最终表现成集群整体迟钝。首先要保住磁盘性能。etcd 对 fsync 延迟极其敏感官方建议 SSD 或 NVMe而且不要跟容器镜像层、日志文件共享同一块高负载磁盘。我见过一个集群etcd 和 containerd 的数据目录都在同一块云盘上镜像拉取一频繁etcd 的写入延迟直接飙到几百毫秒整个控制面跟着抖动。监控里重点关注etcd_disk_wal_fsync_duration_seconds的 99 分位这个值如果长期大于 10ms就该排查磁盘或 IO 抢占问题了。其次是 etcd 自身的维护。Kubernetes 默认开启了历史版本压缩但压缩只是回收历史版本占用的空间底层存储引擎在实际删除大量数据后会产生碎片空间不会自动归还给文件系统。所以需要定期执行etcdctl defrag释放碎片空间。操作 defrag 时要小心它会造成一次短暂的阻塞建议在业务低峰期执行并且优先逐个节点做不要所有 etcd 成员同时操作。另外etcd 的--quota-backend-size默认 2GB超限后 etcd 会进入只读保护模式这个指标etcd_server_quota_backend_bytes长期接近上限就是该考虑清理历史数据和压缩的明确信号。3.2 API Server并发、缓存和事件流API Server 是控制面的入口所有的kubectl、控制器、调度器、kubelet 都在跟它通信。默认的--max-requests-inflight400、--max-mutating-requests-inflight200对小集群够用但节点数和 Pod 数上涨以后大量 list/watch 请求会把它拖住。把这两个值调高到 1000/500 通常是安全的前提是 apiserver 所在节点的 CPU 和内存有富余否则只是把瓶颈从并发队列转移到了处理能力。在调这个参数之前我建议大家先看一下监控里apiserver_request_duration_seconds的分位数尤其是apiserver_current_inflight_requests是否经常打满。如果打满光调参数是治标不治本往往是有哪个客户端写了个低效的 list 全量请求或者某个控制器在频繁刷全量对象。从调优角度可以先从源头排查过度热心的组件再决定是否放大并发额度。另一个容易被忽略的点是事件。Event 对象在 etcd 里的写入频率非常高高变更率的集群里 Event 甚至会占据 etcd 大量空间。我习惯把 Event 单独拆出来存比如用events.k8s.io/v1配合独立的事件存储后端或者至少给 Event 设置较短的 TTL防止它成为 etcd 容量瓶颈。3.3 kube-proxy 模式与长连接优化网络路径上的性能损耗经常被低估。默认的 kube-proxy 使用 iptables 做 Service 流量转发每一条 Service 规则在 iptables 里都是一长串链。当集群里 Service、Endpoints 数量达到几千条时新连接匹配规则的耗时明显上涨规则变更时整表刷新还会导致瞬时丢包或延迟。遇到这个规模最直接的调优就是把 kube-proxy 切到 IPVS 模式。IPVS 底层用哈希表规则数量变大后性能不会像 iptables 那样线性劣化。kube-proxy 的 mode 是启动参数改完必须重建 kube-proxy DaemonSet。切换前建议先在测试集群验证一遍确认你的 CNI 插件跟 IPVS 兼容大多数主流 CNI 都支持但有些自定义网络策略依赖 iptables 的标记链切换后需要重新检查 NetworkPolicy 是否恢复正常。节点上还有一个容易被忽视的隐性杀手conntrack 表溢出。如果集群里短连接特别多conntrack 表被占满新连接会直接被丢弃外部表现是过一会儿又通一下然后又开始超时。除了前面说的调大nf_conntrack_max还可以通过 Service 的externalTrafficPolicy: Local减少 DNAT 带来的连接跟踪开销。当然Local 模式会把流量只转发到当前节点上的 Pod绕开了一层转发代价是负载不均的概率变大要配合反亲和性让每个节点尽量都有副本。4. 工作负载资源模型把 Pod 的胃口告诉调度器4.1 Request、Limit 重新审视别再 average 一把梭Pod 的资源请求值是调度器的核心依据但很多团队把 request 和 limit 设得极其随意——所有容器一律 request 100m、limit 500m。这样带来的问题是调度器以为节点还很空疯狂把 Pod 往上堆实际运行时内存或 CPU 却远超 request节点很快被打到驱逐阈值而 Limit 又可能偏低引发前面提到的 CPU 限流。正确做法是把 request 当作正常工作负载的预测值limit 当作容忍突发的最大值。要得到这两个值不能靠猜必须回到监控数据。用 Prometheus 查一下工作负载最近一两周的 CPU 和内存用量取 P95 或 P99 作为 request 的依据再留出 30%-50% 的余量作为 limit。比如一个 Java 服务稳定状态下内存用量在工作集 1.2Gi 左右那么 request 给 1Gi 到 1.5Gi、limit 给 2Gi 是合理区间。这里还有个 QoS 的概念值得强调。Pod 的 request 和 limit 相等时属于 Guaranteed不等于时属于 Burstable都没有设置则算 BestEffort。节点 Memory 压力触发驱逐时最先被驱逐的是 BestEffort然后是 Burstable最后才是 Guaranteed。关键业务尽量做到 Guaranteed 或至少把 request 设置准确这样节点资源紧张时它不会首当其冲被清理。4.2 调度策略让 Pod 长在合适的位置请求值和 Limit 解决的是能不能塞下的问题调度策略解决的是塞在哪里更合理的问题。最常用的是把 CPU 密集型服务跟内存型服务混部或者通过反亲和性让同服务的多个副本分散到不同节点避免一台节点挂了服务整体受影响。以常见的无状态 API 服务为例我会在 Deployment 里加三段约束affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - api-gateway topologyKey: kubernetes.io/hostname topologySpreadConstraints: - maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: DoNotSchedule反亲和性负责让同一应用的 Pod 尽量不在同一台节点拓扑分布约束负责让副本尽量均匀分散到不同可用区。maxSkew: 1的意思是可用区间副本数最多差 1适合对容灾有要求的场景。代价是调度成功率会下降如果集群节点数太少或副本数太多可能部分 Pod 永远 Pending。建议先用preferredDuringSchedulingIgnoredDuringExecution这类软约束跑一段时间观察分布效果再决定要不要换成硬约束。4.3 HPA 的正确姿势指标没选对扩缩容就失真很多团队搭了 HPA但扩缩容反应很怪要么流量涨了副本半天起不来要么流量一波动副本抖动得厉害。这通常不是 HPA 本身的问题而是指标和配置的问题。Kubernetes 1.23 之后推荐使用autoscaling/v2版本它支持多指标和自定义指标。我的基本配置长这样apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: api-gateway-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: api-gateway minReplicas: 3 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60 - type: Pods pods: metric: name: http_requests_per_second target: type: AverageValue averageValue: 500 behavior: scaleDown: stabilizationWindowSeconds: 300同一时间配置 CPU 利用率和每秒请求数两个指标时HPA 会取两者计算结果的较大值避免单一指标失真。比如 CPU 因为限流一直上不去但请求量已经翻倍靠 CPU 指标不会扩靠 QPS 指标就能兜底。扩缩容抖动问题通过behavior里的stabilizationWindowSeconds解决。scaleDown 的稳定窗口建议设 5-10 分钟防止流量短时下跌就立刻缩容然后又因流量恢复而扩容形成来回震荡。缩容策略也应该保守一些比如每次只缩 20% 的副本给新扩出的 Pod 留足启动和预热时间。我见过不少一扩容就有大量 502 的情况那往往不是 HPA 的问题而是服务启动后需要较长预热时间扩容幅度又太激进导致所有新 Pod 同时处于未就绪状态。补充一点资源指标这块依赖 metrics-server它是只提供资源用量的轻量组件如果要基于自定义业务指标做伸缩比如消息积压数、队列深度就需要 kube-prometheus 体系里的 prometheus-adapter。在瓶颈排查时先确认kubectl top node和kubectl top pod数据正常再排查自定义指标链路这样能快速缩小问题范围。5. 监控体系搭建用指标把集群照清楚5.1 组件清单哪些该装、各自负责什么搭建 Kubernetes 监控我见过两种典型偏差一种是只部署一套 Prometheus 就觉得自己有监控了实际上连 Pod 级别的指标都没有另一种是恨不得把能装的 Exporter 全部装上告警每天轰炸真正出问题时反而没人看。合理的组件分工应该是这样的组件负责内容典型指标示例metrics-server资源指标汇总供 kubectl top/HPA 使用/metrics/resourcekube-state-metrics集群对象状态转换副本数、Pod 状态、PVC 用量node-exporter节点物理指标CPU、内存、磁盘、网络、文件系统内置 cAdvisor容器资源使用和隔离指标container_cpu_usage_seconds_totalkubelet 自带 metricskubelet 自身状态卷操作、Pod 生命周期控制面组件 metricsAPI Server、etcd、调度器、控制器etcd_disk_wal_fsync_duration_seconds很多从零开始配监控的人会混淆 metrics-server 和 kube-state-metrics。前者只采集节点和 Pod 的当前资源用量数据存在内存里不落库专门为kubectl top和 HPA 提供 API后者则是把 Deployment、ReplicaSet、Pod、PVC 这些对象的状态暴露成指标供告警和趋势查询使用。两者不是替代关系是互补关系。生产环境我建议直接用 kube-prometheus-stack 这套 Helm Chart它把 Prometheus、Alertmanager、Grafana、各种规则和 dashboard 打包在一起装完就有控制面的指标和告警。自己手动拼 Prometheus 不是不行但后面升级规则、维护告警会比较痛苦。如果集群规模大、指标量多还可以给 Prometheus 加 Thanos 做长期存储和全局查询不过那是另一个话题了。5.2 真正值得盯的核心指标别被几百个指标淹没Prometheus 里关于 Kubernetes 的指标有上千个但日常优化和排查真正值得重点盯的不会超过几十个。我一直跟团队强调三层盯法业务层看 REDRate请求速率、Errors错误数、Duration延迟。这一层是离用户最近的信号从 Service Mesh 或应用的 Prometheus client 里拿。业务指标异常时才往下追资源层。资源层看容器 CPU 和内存的用量、限制、节流。CPU 限流要看container_cpu_cfs_throttled_periods_total这个指标只看container_cpu_usage_seconds_total会被 limit 掩盖真实瓶颈。内存要看container_memory_working_set_bytes而非container_memory_usage_bytes后者包含文件缓存不能直接反映容器真实内存压力。系统层看节点 CPU 的 steal、IO 等待、磁盘使用率和 inode、网络丢包。云服务器上node_cpu_seconds_total{modesteal}如果长期占比超过 5%说明宿主机本身存在资源争抢这是云厂商超卖的信号无论你在集群内部怎么调优都很难彻底解决只能迁移实例或者换规格。另外控制面组件自身的指标在高负载排查中优先级最高。我会在 Grafana 里固定放几个面板API Server 请求延迟分位数、etcd wal fsync 延迟、etcd leader 变化次数、调度器调度失败率。控制面任何一个指标出现持续恶化都会在下游表现为 Pod 调度慢、变更慢早发现能避免把根因误判到业务代码上。5.3 告警规则与记录规则减少噪音盯住增量告警配置这里踩过大坑之后我有几条切身体会。一是不要在 Prometheus 的原始指标上直接写告警先定义 recording rule把常用查询固化下来。这样既能提前聚合减少告警查询对 Prometheus 的压力也能让不同告警之间复用一个计算结果。比如我通常会先算好每个命名空间的 CPU 总用量groups: - name: k8s-recording-rules rules: - record: namespace:cpu_usage_cores:sum expr: | sum(rate(container_cpu_usage_seconds_total{container!}[5m])) by (namespace)然后在告警规则里直接引用这个记录结果表达更清晰告警查询也更快。二是在进程重启这类高频事件告警上加上增量判断。直接对kube_pod_container_status_restarts_total做阈值判断很容易误报因为 Pod 可能在一个月前重启过总次数一直大于 0。正确的写法是看一个时间窗口内的增量- alert: KubePodCrashLooping expr: increase(kube_pod_container_status_restarts_total[15m]) 3 for: 5m labels: severity: warning annotations: summary: Pod {{ $labels.namespace }}/{{ $labels.pod }} 在 15 分钟内重启超过 3 次第三告警要有路由和分级。配置 Alertmanager 的路由规则把 warning 级别的告警发到工单或群里critical 级别的告警走电话或紧急渠道。没有分级的结果就是告警疲劳到真正出大事时大家已经习惯性忽略推送了。6. 排查实录把调优经验固化成可复用的手段6.1 高频问题速查表工具和参数都会过时但排查思路可以沉淀成一张表。下面是我在工作中反复用到的清单现象、优先排查项和常见解法都列出来每次定位问题都从这张表开始往后想现象高概率根因优先查看的指标/命令常见解法Pod 频繁重启内存超限触发 OOMKilledkubectl describe pod的 Last State调大内存 limit、优化内存占用、排查内存泄漏服务延迟毛刺但 CPU 不高CFS 限流container_cpu_cfs_throttled_periods_total调高 CPU limit 或降低 request 对齐业务实际用量节点 MemoryPressure 导致驱逐节点总内存不足或 request 设置过高kubectl describe node的 Pressure 状态扩容节点、调整 request、开启 HPA 及时缩容创建 Pod 一直 Pending资源请求过大或调度约束冲突kubectl describe pod的 Events检查节点可分配资源、放宽调度亲和性约束kubectl 执行很慢API Server 并发打满或 etcd 慢apiserver_current_inflight_requests、etcd fsync 延迟调大上限、排查低效 list/watch、清理 etcd 碎片Service 偶发超时、连接重置conntrack 表满或 iptables 规则膨胀conntrack -S、dmesg调大 conntrack 上限、改用 IPVS、启用externalTrafficPolicy: Local业务容器内存持续增长不回收JVM 或运行时堆外内存问题container_memory_working_set_bytes趋势结合应用和 GC 日志定位限制堆大小或调整 GC 参数节点 NotReadykubelet 卡死或磁盘满journalctl -u kubelet、df -h清理镜像/日志、检查 docker/containerd 存储目录这张表不能替代你对具体组件的理解但能帮你在告警刷屏时第一时间锁定排查方向而不是从零开始翻文档。6.2 一次 CPU 限流问题的完整追查过程去年我处理过一个比较典型的案例过程很适合用来展示调优和监控怎么协同工作。业务方上报说线上某个推送服务在每天 20 点准时有大量请求超时开发先查了应用日志和调用链发现下游依赖都很正常等于把问题定位到了基础架构。我打开 Grafana先看这几个面板这个服务的 CPU 用量只有 1.5 核但它所在 Pod 的 limit 是 2 核看起来远没到上限。接着看内存工作集稳定在 800Mi 左右也不紧张。然后我随手翻了container_cpu_cfs_throttled_periods_total这个指标发现从每晚 19:50 左右开始throttled periods 在快速上升——Pod 里的容器正在被强制限流。原因在于这个服务的 request 只设了 100m而它跑在 8 核的节点上调度器给它的 CPU 份额非常低。虽然 limit 是 2 核但 request 为 100m 意味着这个 Pod 被分配到的 CPU 权重只相当于 0.1 核节点上其他 Pod 只要一忙它的 CPU 时间就会被抢占。这解释了为什么 CPU 平均值看起来不高业务却感觉卡顿大量的 CPU 时间片被 CFS 调度器拿走了。修复方式很直接把 request 从 100m 上调到 1 核limit 保持 2 核不变。调整后 Pod 获得了稳定的 CPU 份额限流指标立即回落当晚高峰期的请求延迟也恢复正常。这个案例后来被我写进了团队的资源规范所有关键业务的 request 必须基于一个月的 P95 用量来设不能用占个位的心态写个 100m 糊弄过去。7. 几条越用越顺手的注意事项围绕 Kubernetes 集群性能调优和监控我最后再整理几条平时容易被忽略但实际帮助很大的经验。第一改动一次尽量只动一个变量。给集群做调优时把内核参数、kubelet 配置、HPA 阈值分开灰度一次只验证一项不要把所有改动一起推到生产。如果你把十项改动一起发上去出了问题根本没法回滚定位。第二基础指标要保留足够长的历史。内存类问题通常是缓慢累积的如果没有 30 天以上的历史数据你很难对照出从什么时候开始请求量、内存、重启数出现拐点。Prometheus 默认 15 天保留期可能不够建议根据容量调整到 30 天以上或者接上对象存储做长期归档。第三做变更前先看基线。任何一次调优前先记下当前的关键指标基线值比如 API Server 的 P99 延迟、etcd fsync 延迟、Pod 调度延迟、节点 CPU 均值改完以后再对比同一指标。没有基线的优化报告和感觉好了点没有区别也经受不住领导或客户的追问。第四调度器和控制面的日志值得定期扫一遍。kube-scheduler会记录每一个 Pod 的调度决策scheduler_scheduling_attempt_duration_seconds和调度失败原因会直接反映集群容量和约束问题。很多 Pending 类问题在事件里只能看到模糊的 didnt match node selector但调度器日志里会有更详细的原因排障时别只看事件日志才是完整信息源。最后想说的是集群性能优化没有终点。业务在变、流量在涨、新的工作负载形态不断出现今天合适的参数明天可能就成了瓶颈。把监控做成习惯把每次调优当成一次带记录的实验这套方法论本身才是比任何单项调优手段都值钱的东西。