One self-hosted console to run your entire business — commerce, ERP, HRM, CRM & manufacturing

第 5 部分:测量、改变、再测量:负载测试、真实考试晚上和最终架构

七个测量、一个真实考试晚上和一个 scale-in 错误:负载测试、容量模型和诚实 soak 计划如何塑造 NovaCommerce 最终架构、成本以及解决方案架构师清单。第 5 部分结束系列。

Author

Anichur Rahaman

9 小时前18 min read2 views
第 5 部分:测量、改变、再测量:负载测试、真实考试晚上和最终架构

在 10 月 7 日晚上,661 名学生开始 NovaCommerce 上的一个考试,峰值 86 开始一分钟,439 个更多坐了第二考试在它之后。后端达到约 69 requests/s 的峰值并在二分钟平均上达到 47% CPU。数据库使用不到 25% 它的 CPU。没背景 job 失败。

然后一个池最小被降低在考试中间。一个服务 node 消失不排水,约 360 请求在两分钟内失败。

那晚的两半都是测试结果:冷静半来自在天前的短测试运行,丑陋半来自没有测试覆盖过还的东西。这部分按顺序走旅程(基准、负载测试、瓶颈、优化、重新测试、缩放、soak、最终结果),然后显示最终架构、它的成本,和我现在在每个评论中使用的清单。

这是五部分案例研究 "从单服务器到考试日就绪" 的第 5 部分。NovaCommerce 是虚构名称;架构、数字和错误都是真实的。

我们保持重复的循环

本系列中的每个改变来自一个循环:测量、找瓶颈、理解原因、改变设计、再测试、再测量。一个改变在原因被理解前造是一个猜测,一个猜测在一个服务器上发生每月花钱。想象一个有打结的花园软管:如果水弱,一个更大的水龙头什么都不做,所以你走软管直到你发现打结。我们的移动了三次,每次它是一个不同类型的问题。

七个从 9 月 25 日基准到 10 月 7 日考试的测量加计划负载测试和 soak 的时间线,有什么每个发现和它改变了什么
七个测量,每个有一个数字在它后,和一张虚线卡对于仍然计划的测试。

基准和负载测试:瓶颈移动三次

基准:状态代码,不是 CPU 图表

第一件我们在生产测量不是 CPU。在 9 月 25 日,学生在考试期间得到 HTTP 429(太多请求)。API 速率限制器按 IP 地址计键,因为针对基于令牌学生的默认 auth guard 是空的。一所学校或移动运营商把数百学生放在一个 NAT IP 后,所以整个建筑共享一个桶。一个 429 是应用说不,不是服务器呼吸急促,所以没额外 node 能帮。Implemented:限制器现在按学生或讲师账户计键,仅客人按 IP。

负载测试:2,854 错误,一句话

在 10 月 6 日 02:28,后端负载测试返回了 2,854 应用 500,每一个 RedisException: Operation timed out 在连接时(5 秒 connect timeout)。原因:Valkey(4 GB,primary 和 standby)无法快速接受连接,每个请求打开它的新 TLS 连接,约 5.4 ms CPU 每个请求针对约 MySQL 的 3.3 ms。更多 web node 会仅打开更多连接到同一 Valkey。

Implemented:Valkey 在原地调整到 8 GB 两个 node(primary 和 standby),数据保留,主机不变。它花更多钱,它移除了真实故障。

重新测试:从 25 到 500 requests/s 的逐步爬升,开始两个后端 node 而池扩展出,给 0 应用错误和 0 后端 5xx。有 143 错误在 load balancer,但仅在 node 在 CPU 上饱和时。那是预期的 "超出容量" 信号,不是 bug,它显示我们每个 node 跑出。

一个 node 能做多少

后端测试也给了我们每个之后的决定倚靠的数字。在满 CPU,一个 4 vCPU / 8 GB 后端 node 服务约 65 到 70 requests/s 真实 API 混合,约 59 ms CPU 每个请求。数字同意:4 vCPU 是 4,000 ms CPU 每秒,4,000 除以 59 约 68。Autoscale 在 CPU 触及 99% 后添加第一个额外 node 约 7 分钟。

那作三个瓶颈按顺序:规则(限制器)、数据层(Valkey 连接),然后仅纯后端 CPU。每一个需要不同的修复,仅最后一个由更多服务器解决。

缩放测试:新服务器到达吗,何时?

前端:两个 node,356 requests/s

有前端重建作一个池(nginx 在两个相同的 Next.js 容器前每 node),两个 node 服务约 356 requests/s,p95 0.05 s,0 错误,在 55 到 60% CPU,我们对比了 12 真实页面针对旧服务器。余地付了自己:池从专用 CPU droplet 移到共享 AMD 2 vCPU / 4 GB droplet。Implemented。

16 分钟 autoscale 测试

在 10 月 5 日,从 21:17 到 21:33,k6 重放了真实考试峰值请求混合(仅 GET,什么都没写):300 到 450 requests/s,然后约 750 六分钟。

  • 后端缩放从 2 到 3 个 node 在 21:29,当池平均达到 75%。前端从 2 到 3 在 21:30,在 71%。新 node 自行加入 load balancer。
  • 0 服务器错误,0 超时,总体 p95 132 到 153 ms。API p95 是 320 到 705 ms,而后端坐在接近 90% 等第三 node。
  • 约 4% 请求得 429,因为所有负载来自一个测试 IP:按 IP 限制做它们的工作。

课程在时间。DigitalOcean 的 CPU 指标滞后真实负载 5 到 8 分钟,所以缩放开始迟,然后过冲:后端短暂扩展到 4 在负载停止后。第一个额外后端 node 到达测试开始后十二分钟,考试不等十二分钟。所以对定时考试我们 pre-scale,在一个大考试前提高池最小约 45 分钟,保持反应式 autoscaling 作安全网。流量峰值自动扩展 中的通用版本论证。

Rollout 探针

Tested,然后 fixed:我们第一个后端模板 rollout 产生了一个短 503 闪烁,因为 DigitalOcean 删除旧 droplet 而 load balancer 仍路由到它们。受保护的 rollout 仅改变镜像,等直到新 node 应答 /lb-health,等约 40 秒给平衡器接纳它们,排水旧 node 首先。10 月 7 日两个池的完整 rollout 在真实页面上每 2 秒运行一个正常运行时间探针:219 探针,0 错误。

缩放帮助的地方,那儿没有

这是我祝我在开始前看过的表。对每个问题测试或生产暴露,它问一个问题:更多服务器会修复它吗?

发现的问题更多服务器?什么修复它状态
后端 CPU 在 65 到 70 requests/s 每 node 饱和是的Autoscale 池(2 到 10 node)加 pre-scalingImplemented
整个学校得 429(9 月 25 日)否限制器按学生账户计键,IP 仅客人Implemented
路由限制仍按 IP 计:74% 呼叫到一个仪表板端点返回 429 在考试(10 月 7 日)否限制按学生每路由计;0 之后这样 429Implemented
2,854 Valkey 连接超时(10 月 6 日)否Valkey 在原地调整到 8 GB x 2Implemented
Merit 卡(0.34 秒 job)等 10 到 115 分钟在 AI job 约 10 秒后(10 月 6 日)否AI job 的单独队列通道;merit 卡在约 1 分钟准备Implemented
后端看前端每个学生一个 IP否在一个签名头中通过真实学生 IPPlanned

在六个问题,一个是容量问题。其他五个是两个规则、连接限制、队列布局和缺失头。数据层一侧这样模式覆盖在 负载下的数据层 中。

Soak 测试:我们运行什么和我们没

一个负载测试问系统能取多少。一个 soak 测试问它能取多久:一个引擎在完整转一分钟证明很少关于三个小时,因为缓慢问题显示上升晚(记忆爬升、连接泄漏、磁盘填充、队列漂移)。"我们 soak 测试了它" 容易说而难以防守,所以我将精确:我们还没运行一个正式 soak 测试。

证据它覆盖什么状态
短持续运行:25 到 500 requests/s 爬升、16 分钟 autoscale 测试在直到约 750 requests/s、2 秒正常运行时间探针通过 rollout分钟持续负载:0 应用错误在爬升、0 服务器错误在 autoscale 测试Tested
10 月 7 日考试晚上:约两个小时、两个考试背对背稳定 CPU,0 失败 job,一个缩减错误在生产中观察
一个多小时 soak 一起下一个大负载测试,在一个同意维护窗口记忆增长、连接泄漏、队列漂移Planned

考试晚上是最接近我们有的,但它是观察,不是控制测试。一个相关的检查确实保有:后端 node 在 24 小时内向磁盘写 0 文件,所以磁盘不是 soak 风险那边。前端 node 仍保持约 440 MB nginx 日志一天本地,那是为什么 Fluent Bit 在前端在计划列表。

真实考试:10 月 7 日作生产证明

没什么替代真实学生。两个考试运行背对背,我们的一次每分钟的概要看他们:学生开始和提交、请求和 5xx 每 node、池 CPU、worker 负载和失败 job。

测量结果
考试 A(20 分钟)661 开始,638 提交(96.5%);开始达到每分钟 86
考试 B(25 分钟)439 开始,425 提交(96.8%)
前端直到 1,741 唯一访问者在考试 A,峰值 403 在一分钟;约 732,000 请求从 6 到 8 pm;CPU 直到 24%
后端峰值约 69 requests/s(每分钟日志;63 在提供商二分钟平均);CPU 直到 47% 在二分钟平均;一个 node 在 86% 一分钟
MySQLCPU 最大 24.5%(平均 13.4%),最多 5 运行查询,0 锁等待
Valkey12.5% 内存
WorkerCPU 46%,0 失败 job

三件事站出来。

  1. 瓶颈是每 node 后端 CPU,数据库有约 6 倍余地。下面的模型把 MySQL 限制放在接近 450 requests/s,针对晚峰值 69。
  2. 测试数字预测晚上。63 requests/s 在 59 ms CPU 每个是约 3.7 CPU 秒每秒。两个 4 vCPU node 有 8 vCPU,所以平均应该约 46%。仪表板显示 47%。
  3. 平均隐藏伤害的 node。流量分离 65/35 在两个后端 node 间,因为长期存活保活连接从前端代理 pin 流量。仪表板说 47% 而一个 node 碰到 86% 一分钟。

缩减错误

在考试中间,某人降低了后端池最小。DigitalOcean 移除了一个服务 node 不排水,约 360 请求在两分钟内失败。早期的测试教了我们 scale-out 慢和晚。考试教了另一半:scale-in 快速而不说再见。Implemented 作 runbook 规则:在考试前提高最小,仅在后降低,永不信任 autoscale scale-in 排水一个 node。

容量模型:它说什么,它停在哪里

在考试后我打开真实数据到一个小模型,所以下一个考试可以用算术而不是恐惧计划。它有四个测量输入:约 15 请求每学生当考试打开,然后约 1.5 一分钟(0.025 一秒);约 30 requests/s 基准流量;50 requests/s 作为一个后端 node 的安全负载(75% CPU);30% 为不均匀分割的边际。

用简洁词汇:取你将有的 node,乘以 50,除以 1.3 对于边际,减去基准流量的 30。剩下的是学生当最后一个加入可能使用。每个学生花他们 15 打开请求在加入窗口上展开,加 0.025 为现在。

取 4 个 node。4 x 50 / 1.3 约 154,减 30 留下约 124。如果学生在 10 分钟加入,每个花 15 / 600 + 0.025 = 0.05 requests/s,所以 124 / 0.05 给约 2,500 学生;表轮下到 2,400。如果他们全部在 2 分钟内加入,每个花 15 / 120 + 0.025 = 0.15,给约 800。

后端 node加入超过约 10 分钟所有加入在约 2 分钟内安全后端负载(衍生)
2约 900约 300约 77 requests/s
4约 2,400约 800约 154 requests/s
6约 4,000约 1,300约 231 requests/s
8 到 105,500 或更多1,800 到 2,300约 308 到 385 requests/s
条形图的学生由 2、4、6 和 8 到 10 后端 node 支持对于十分钟加入和两分钟加入,在 MySQL 限制近 450 旁的线图安全后端 requests/s 针对
后端 node 是限制一直到 10 node;MySQL 线坐在即使 10 node 上限之上。

真实晚上适配第一行:峰值 86 开始一分钟针对约 90 那行允许。模型停在哪里:

  • 它来自一个多项选择考试。有 PDF 上传的书面考试加载 worker 更多必须单独测量。
  • 行在 3 个 node 之上是外推;大多数我们见过在测试是 3 个 node,简短 4。Planned:完整负载测试在维护窗口。
  • Node 仅计数一次它们存在。用 5 到 8 分钟滞后的指标和约 7 分钟后 CPU 触及 99% 的第一个额外 node,表是一个 pre-scaling 指南,不是反应式缩放的承诺。
  • 它覆盖仅后端请求路径。Worker、Valkey 和 AI 提供商的速率限制有它们自己天花板。

最终架构,逐层

实心盒子今天运行;虚线条是计划。

最终生产架构:用户、前端 load balancer 和池、后端 load balancer 和池、MySQL primary 和 standby、Valkey、PostgreSQL 与 pgvector、worker、Spaces 与 CDN、OpenSearch 由 Fluent Bit 喂,ops 控制台,和计划项目的虚线条
每个盒子存在因为测试或真实考试问了它;虚线是我们没做的。
  1. 边缘。两个 load balancer,一个每层。Rejected:一个平衡 两个,因为 DigitalOcean 平衡不能按主机或路径路由。
  2. 前端池。2 到 10 个 node,nginx 在两个 Next.js 容器前(一个每 vCPU),在 70% CPU 缩放。
  3. 后端池。2 到 10 个 node 从一个黄金快照,仅 nginx 和 php-fpm,CPU 目标 55%(它开始在 70%)。在 web node 上没 job,所以额外 node 永不运行一个两次。
  4. 数据。MySQL Standard 8.4(4 vCPU / 16 GB,primary 和 standby,在 standby 读,sticky = true);Valkey 8 GB 在两个 node;PostgreSQL 与 pgvector(2 vCPU / 4 GB)保有分开,所以向量查询永不与考试写竞争。
  5. Worker。一个固定 4 vCPU / 8 GB droplet 对队列通道(包括单独 AI 通道)、调度器、WebSocket 和所有 SMS,因为 SMS 网关将一个 IP 列入白名单。一个已知单点故障。
  6. 文件、日志、看。Spaces 在 CDN 后;后端日志通过 Fluent Bit 到 OpenSearch;我们的 ops 控制台样品每层只读并运行受保护部署。

前后

区域之前(直到 10 月 5 日)现在
前端一个 8 vCPU / 16 GB droplet,一个 Next.js 容器,没 load balancer负载平衡 2 到 10 个 node 的池,两个容器每个
后端两个固定 droplet,按 ID 针对,所以新服务器无法加入平衡针对标签;2 到 10 个池从一个快照,前 pre-scaled 考试
MySQL一个 Advanced node,8 vCPU / 32 GB,没 standbyStandard 4 vCPU / 16 GB,primary 和 standby
"Standby" 服务器两个关闭 droplet,$112 一月,分钟开启移除;真实 standby 在 MySQL 和 Valkey 内(现在 8 GB,上升从 4)

钱买什么

项目(月,DigitalOcean 列表价格)成本
之前:整个 web 层(16 GB 前端、2 后端、worker、1 load balancer、2 关闭 standby)约 $416
现在,web 层:2 后端(每个 $56)、2 前端(每个 $28)、worker、两个 load balancer$112 + $56 + $56 + $48
现在,数据和日志:MySQL 对、Valkey 8 GB x 2、PostgreSQL、OpenSearch约 $389 + $240 + $60 + $20
现在:快照、Spaces约 $5 + $5 和使用
现在:总生产,后端池在 2 个 node约 $990

总值不相同:$416 是 web 层仅,$990 是整个生产堆栈。在同样列表价格 web 层单独现在约 $272。其他 $709 是托管 MySQL、Valkey、PostgreSQL 和 OpenSearch,那是额外钱买的:一个在 node 故障后活的数据库、有房间的 Valkey 和 standby、给考试写保持出的方式向量搜索、和活得比删除 node 久的日志。Pre-scaling 是便宜部分:额外后端 node 花约 $0.08 一小时。数字背后的决定在下面的清单。

计划,没完成

  • Planned:完整负载测试和正式多小时 soak 在维护窗口,在下一个大考试前。
  • Planned:Next.js 静态资源从 CDN、Fluent Bit 在前端 node、签名真实 IP 头从前端到后端所以每个按 IP 规则和日志是精确。
  • Planned:worker 高可用性:一个保留 IP 与 SMS 网关列入白名单、小 standby worker、考试前前启动的仅考试爆发 worker。
  • Considered:Kubernetes(DOKS)对秒级缩放,这是更多运行。温 standby 准备在 3 到 4 秒也被考虑,但 DigitalOcean 池没温池,所以我们 pre-scale 并保持备用容量服务在平衡内。

一个解决方案架构师清单

这是我现在在架构评论中使用的清单。每项是这个项目做或付的。

可扩展性

  • Web node 是无状态:会话、缓存和队列在 Valkey,上传在 Spaces,在 24 小时内 0 文件写到磁盘。
  • 每层由它自己的单位缩放:前端 node 两个 Next.js 容器,后端 node 每个 php-fpm worker。
  • 已知负载被 pre-scaled;反应式缩放仅是安全网。

可用性

  • 没有任何单个 node 能让网站宕机:两个 Load Balancer,每个 pool 最少 2 个 node,MySQL 和 Valkey 都有 standby。
  • 健康检查仅由 nginx 应答,node 被排水(/lb-health 上的 503)在移除前。
  • 旧系统保持直到流量真实离开:DNS 切换后一个小时它仍得约 36% 请求。

性能

  • 知道 CPU 每请求(59 ms),不仅 requests/s。
  • 看每请求设置成本:新 TLS 连接花约 5.4 ms CPU 给 Valkey 和 3.3 ms 给 MySQL。
  • 读请求在 standby 和 primary 之间分担,写请求进入 primary,学生依然能立刻看到自己的答案。

可靠性

  • 缓慢 job 得它们自己队列通道,所以 0.34 秒 job 永不等在 10 秒后。
  • 外部提供商的呼叫被步调和重试(8 一分钟共享、1 到 15 分钟的退避、直到 12 小时),所以学生永不看错误。
  • 速率限制按学生计,不是 IP,哪里许多学生共享一个地址。

可观测性

  • 一次每分钟概要在考试:开始和提交,请求和 5xx 每 node,池 CPU,worker 负载,失败 job。
  • 看每个 node,不仅池平均(47% 针对 86%)。
  • 日志活得比 node:Fluent Bit 到 OpenSearch 对后端;前端被计划。

故障恢复

  • 托管数据库备份和故障转移、黄金快照,最后 3 个好镜像保有对回滚。
  • 每个 cutover 保有一个返回方式:旧数据库 48 小时冻结,旧前端保持直到 DNS 排水。
  • 单点故障被写下。Worker 是一个:如果它死了,SMS、调度器和考试处理停止,提交在 Valkey 等安全。

成本

  • 测量余地在选择尺寸前:前端在测试后移到共享 CPU,标准 MySQL 有 standby 替换一个大 Advanced node(便宜和高可用性)。
  • 去掉不产生任何价值的开销:两台关机的 standby 每月花费 $112,却没有带来任何可用性。
  • Pre-scale 而不是过度提供,花在移除真实故障的地方,比如更大 Valkey。

可维护性

  • 永不手工编辑池 node:改变一个、测试它、快照它、指向池模板到快照。
  • 在池更新时通过每个设置,所以没什么无声重置。
  • 新守卫附带失败没修复的测试,当按学生限制一样。

未来增长

  • 写连接数学下:10 个 node x 80 php-fpm worker 是 800,在 MySQL 的 1,601 下。
  • 知道下一个天花板:MySQL 近 450 requests/s,约 6 倍晚峰值。
  • 书面考试单独测量,早期检查账户限制:droplet 限制 25 在两个池达到它们最大期间 rollout 前必须提高。

我们学到了什么,这把我们留在哪里

  1. 第一个瓶颈很少是你害怕的。我们担心服务器;前三个问题是速率限制规则、连接限制和队列布局。
  2. 修复原因,不是症状。仅在六个问题一个是容量问题。
  3. 对比生产针对模型。63 requests/s 在 59 ms 预测约 46% CPU,我们看到 47%。
  4. 平均隐藏伤害的 node,scale-in 是突然。47% 平均,86% 在一个 node;在考试前提高最小,仅在后降低。
  5. 说什么你没测试。我们有负载测试和一个真实晚上;正式 soak 被计划。

当这个系列开始时,平台是一个前端 droplet、两个后端 droplet 按 ID 固定和一个大数据库 node。10 月 7 日考试晚上在不同系统上运行,没有差异来自为自己的缘故添加服务器。它来自同一循环,再一次,包括时间的测量告诉我们我们错了。

测量。找瓶颈。理解它。改变一件事。测试。再测量。

那循环是架构;池、standby 和图表是它留后。如果你从这五部分保持一件事,保持循环,在你下一个考试晚上、销售或启动前使用它。

感谢阅读所有五部分。要回去,开始与 第 1 部分,起点和高可用性,然后 第 2 部分,缩放应用层,第 3 部分,数据库 和 第 4 部分,Valkey、worker 和日志。这是系列的最后部分。

About the Author

Anichur Rahaman

Continue Reading