news 2026/9/9 5:09:34

Kubernetes集群性能优化实战:从资源调优到监控体系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kubernetes集群性能优化实战:从资源调优到监控体系

说实话,一个 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_watchesmax_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.available,kubelet 会开始驱逐优先级最低的 Pod。evictionHard是硬性触发线,我这边还会配evictionSoftevictionSoftGracePeriod,给系统留出温和处理的缓冲时间。特别提醒:这里的nodefsimagefs分别对应 kubelet 的工作目录和容器镜像存储目录,如果你用 containerd,一般imagefsnodefs在同一块磁盘,这块磁盘满了会有各种诡异问题,比如镜像拉不下来、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-inflight=400--max-mutating-requests-inflight=200对小集群够用,但节点数和 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 nodekubectl top pod数据正常,再排查自定义指标链路,这样能快速缩小问题范围。

5. 监控体系搭建:用指标把集群"照"清楚

5.1 组件清单:哪些该装、各自负责什么

搭建 Kubernetes 监控,我见过两种典型偏差:一种是只部署一套 Prometheus 就觉得自己"有监控了",实际上连 Pod 级别的指标都没有;另一种是恨不得把能装的 Exporter 全部装上,告警每天轰炸,真正出问题时反而没人看。

合理的组件分工应该是这样的:

组件负责内容典型指标示例
metrics-server资源指标汇总,供 kubectl top/HPA 使用/metrics/resource
kube-state-metrics集群对象状态转换副本数、Pod 状态、PVC 用量
node-exporter节点物理指标CPU、内存、磁盘、网络、文件系统
内置 cAdvisor容器资源使用和隔离指标container_cpu_usage_seconds_total
kubelet 自带 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 的指标有上千个,但日常优化和排查真正值得重点盯的不会超过几十个。我一直跟团队强调三层盯法:

业务层看 RED:Rate(请求速率)、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{mode="steal"}如果长期占比超过 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 -Sdmesg调大 conntrack 上限、改用 IPVS、启用externalTrafficPolicy: Local
业务容器内存持续增长不回收JVM 或运行时堆外内存问题container_memory_working_set_bytes趋势结合应用和 GC 日志定位,限制堆大小或调整 GC 参数
节点 NotReadykubelet 卡死或磁盘满journalctl -u kubeletdf -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 类问题在事件里只能看到模糊的 "didn't match node selector",但调度器日志里会有更详细的原因,排障时别只看事件,日志才是完整信息源。

最后想说的是,集群性能优化没有终点。业务在变、流量在涨、新的工作负载形态不断出现,今天合适的参数明天可能就成了瓶颈。把监控做成习惯,把每次调优当成一次带记录的实验,这套方法论本身,才是比任何单项调优手段都值钱的东西。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 5:08:02

AI Agent自我改进:斯坦福CS329A专题解析与工程实践

这次我们来看一个偏工程向的学习资源&#xff1a;Stanford CS329A 课程体系里的 Self-Improving AI Agents 专题合集。它不是一个能下载下来双击运行的模型工具&#xff0c;而是一套课程向的技术内容。核心主题非常聚焦&#xff1a;AI Agent 如何在执行任务的过程中&#xff0c…

作者头像 李华
网站建设 2026/9/9 5:06:51

小程序商城运营提速:从搭建选型到RPA自动化实战

这两年做私域的人都绕不开一个现实&#xff1a;流量引到公众号、企业微信号、社群里之后&#xff0c;总得有一个地方把交易“接住”。小程序商城恰好就是这个承接点。它不用跳出微信就能完成浏览、下单、支付、售后全流程&#xff0c;对用户来说阻力最小&#xff0c;对商家来说…

作者头像 李华
网站建设 2026/9/9 5:04:19

数据中台数据模型设计模式:维度建模、Data Vault与湖仓实践

做数据中台这几年&#xff0c;我最大的感受是&#xff1a;平台组件可以买、可以搭&#xff0c;但真正让中台有价值或者没价值的&#xff0c;往往是那一张张表怎么设计。数据模型看着是个老话题&#xff0c;几乎所有中台项目都会喊“我们要做指标统一、数据打通”&#xff0c;但…

作者头像 李华
网站建设 2026/9/9 5:01:57

三相并联型APF仿真:双闭环PI、id-iq检测与SVPWM方案解析

做三相并联型有源电力滤波器APF仿真&#xff0c;绕不开一套非常经典的组合&#xff1a;电压外环、电流内环都用PI控制&#xff0c;谐波检测用id-iq法&#xff0c;最后调制走SVPWM。我可以直接说&#xff0c;这套方案是我见过最适合作为APF入门和课程设计模板的路子&#xff0c;…

作者头像 李华
网站建设 2026/9/9 4:57:38

重读操作系统原理:从服务器重启到实战排障

凌晨三点&#xff0c;服务器又自动重启了一次。业务群的消息像催命符一样往外弹&#xff0c;我登录上去先翻dmesg&#xff0c;再查上次开机的journalctl&#xff0c;最后在一堆硬件错误日志里找到了疑似根因。处理完问题&#xff0c;我靠在椅子上突然想起本科时啃《计算机操作系…

作者头像 李华
网站建设 2026/9/9 4:57:37

C#上位机开发全攻略:从串口通信到UI卡顿优化,.NET实战一条龙

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华