记一次 Kubernetes 750m CPU 配额下的滚动发布压测

2026年09月02日2 次阅读0 人喜欢
KubernetesK8s容量压测滚动发布ResourceQuota性能测试本地化验证运维
所属合集

最近在处理一个 Kubernetes 滚动发布的资源问题。

测试环境给到的实际配额是:

yaml 复制代码
requests.cpu: 750m
requests.memory: 3Gi

后端服务原来的 request 比较大,Deployment 又不能取消滚动发布,发布时新旧 Pod 会同时存在。这样一来,平时运行没问题,不代表发布一定没问题。真正要看的,是新 Pod 能不能创建出来,以及高计算量接口在 CPU limit 下会不会开始排队或超时。

我没有直接去改真实业务 Namespace,而是在本机 OrbStack 里开了 Kubernetes,用一个单独的测试 Namespace 模拟这个环境。

先把服务降下来

这次实验使用本地构建的前端静态资源镜像和后端 JAR 镜像,imagePullPolicy 设为 Never,不依赖外部镜像仓库。

最后采用的候选资源是:

服务 request limit
前端服务 50m CPU / 64Mi 100m CPU / 256Mi
后端服务 250m CPU / 1Gi 600m CPU / 2Gi

前端服务主要是 Nginx,CPU request 没必要给得太高。后端服务需要留出计算峰值,所以 limit 设到了 600m,但 request 只保留 250m。

这里需要区分 request 和 limit。request 会影响调度和 ResourceQuota,limit 是容器运行时的上限。滚动发布能不能创建新 Pod,先看 request;接口运行时会不会被压住,再看 limit。

滚动发布先验证

Deployment 保持原来的滚动发布策略:

yaml 复制代码
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

我在持续访问期间做了两次后端滚动发布。发布时旧、新 ReplicaSet 都出现过,旧 Pod 和新 Pod 短时间共存。

峰值 request 是:

复制代码
CPU:    550m / 750m
Memory: 2112Mi / 3Gi

两次发布都没有出现 exceeded quota,新 Pod 能正常 Ready,旧 Pod 之后被回收。这个结果至少说明,按照这组 request 配置,发布瞬间没有被 Namespace 配额挡住。

不能只压健康检查

之前的压测如果只看健康检查或者一个简单列表,结论会比较虚。接口能返回 200,只能说明入口还活着,不能说明后端正在做复杂计算时也能撑住。

所以这次实际跑了登录、用户信息、菜单、部门查询、岗位和人员查询、看板查询、文件上传和导入等链路。之后又把维度台账单独拿出来测。

台账里面最重的不是普通 list,而是异步重算接口:

复制代码
POST /ability/score-ledger/recalculate

这个接口会按周期和组织范围处理人员,计算多个维度,写入明细、维度结果和综合结果,过程中还会涉及事务、数据库连接和线程调度。它比单纯查询一页列表更接近后端的资源上限。

我用测试周期和一个包含 14 人的测试范围执行了一次重算。提交接口本身耗时约 79ms,后台任务从开始到完成约 3.5 秒,最终状态是 SUCCEEDED,处理进度是 14/14。

再测台账查询

重算完成后,我把五类台账的 list、stat 和 detail 接口都跑了一遍。

常规并发下共请求 550 次,全部成功。单接口结果里,成果列表的 P95 是 0.484 秒,技能详情的 P95 是 0.289 秒,其余接口更低。

随后把并发提高到 75,混合请求技能列表、业绩列表、技能详情和统计接口,一共 800 次,800 次都返回 HTTP 200。

这次混合压测的结果是:

复制代码
P50:  0.783s
P95:  3.578s
P99:  4.513s
Max:  6.448s

这里能看到尾延迟已经明显上来了。P50 还可以,但 P99 已经超过 4 秒,说明并发升高以后,线程、数据库连接或者 CPU 限制开始影响请求完成时间了。

OrbStack 的 Metrics API 没有用上

我原本准备通过 kubectl top pod 采集资源,但 OrbStack 当前的 Metrics API 不可用,执行后直接返回:

复制代码
error: Metrics API not available

这次没有把这个空结果当成资源数据,而是进入后端 Pod 读取 cgroup:

复制代码
/sys/fs/cgroup/cpu.stat
/sys/fs/cgroup/memory.current
/sys/fs/cgroup/memory.events

75 并发混合查询期间,后端内存大约 772MiB,2Gi 的内存 limit 还比较充足。内存事件中的 oom=0oom_kill=0,Pod 重启次数也是 0。

CPU 这边确实有 throttling。600m limit 形成了实际的 CPU 限制,压测期间 nr_throttled 持续增加。不过这次 throttling 没有造成请求失败,800 次请求仍然全部完成。

这也是只看错误率容易漏掉的地方。服务没有挂,不代表完全没有性能代价。当前结果更准确的描述是:CPU limit 能把服务压住,但在当前数据量下还没有压到不可用。

中间还踩了两个小坑

专项脚本第一次轮询重算状态时,把变量命名成了 zsh 的只读变量 status,脚本直接中断了。重算任务本身已经成功提交,所以后面没有重复提交,而是重新登录后只轮询原任务状态。

另外,台账列表接口返回的是 rowstotal,不是普通接口里的 data。如果压测脚本只读取 data,很容易误判列表没有数据。后面我按实际 RuoYi 响应结构改了专项脚本。

最后结果

这轮实验的结论是:在当前测试数据规模下,前端服务 50m/100m、后端服务 250m/600m 的 CPU 配置可以通过验证。

滚动发布时新旧 Pod 能共存,ResourceQuota 没有拒绝新 Pod。真实前后端链路可以跑通,异步重算成功,五类台账查询在 75 并发混合压测下没有 5xx、超时、OOMKilled 或重启。

不过 600m CPU limit 已经能观察到 throttling,75 并发时 P99 也明显高于 P50。后面如果数据量或并发量继续上升,应该重点盯重算耗时、数据库连接池、锁等待、CPU throttling 增长和 P99,而不是只看平均响应时间。

这次没有修改滚动发布策略,而是通过降低 request、在本地 Kubernetes 中模拟配额、再跑真实链路来验证。对这个问题来说,这种结果比单纯根据 IAC 里的旧配置反推 request 和 limit 更有参考价值。

加载评论中...