第 1 级 · 有问题的发布
09:10 发布了一个新构建。这个月已经很艰难:错误预算只剩 20 %。部署几分钟后,燃烧速率告警响了。保护好预算。
- 结束时剩余错误预算 ≥ 18.5 %
- 部署后平均错误率 ≤ 0.65 %
- 集群成本 ≤ $9.50
你是一项 Web 服务的值班工程师:负载均衡器后面是一组实例,缓存放在数据库前面,可用性目标是 99.9 %。模型每分钟用教科书公式计算排队延迟、超时、缓存命中和数据库负载,以及你的决策要花多少钱。
实例集群的下限。调高会立即启动实例,但它们仍要等启动延迟过去才能提供服务。
按利用率目标跟踪;实例还在启动时不会再次扩容。关闭 = 恰好保持最小实例数。
越低 = 余量越大,成本也越高。测得的利用率不会超过 100 %,所以已饱和的集群只能一步一步增长。
TTL 越长 = 命中越多,但返回的答案可能越旧(平均时龄 ≈ TTL/2)。
加载热点键 6 分钟(每分钟填充缓存的 12 %),代价是每秒额外 600 次数据库查询。
占 30 % 的低优先级流量(预取、批处理、爬虫)中,在负载均衡器处以“请稍后重试”拒绝的比例。
这个重量级功能让每个请求多耗 20 ms CPU,并多一次数据库查询。关闭 = 优雅降级。
在一组全新的实例上重新部署上一个良好版本(5 分钟,计费两次),然后切换流量。再次按下会重新开始准备;如果线上没有有问题的版本,这样做只会花钱。
| 燃烧速率(1 小时) | 0.0 × |
|---|---|
| 请求数 | 1125 req/s |
| 提供服务的实例 | 10 |
| 正在启动的实例 | 0 |
| 缓存命中率 | 77 % |
| 数据库利用率 | 19 % |
| 被丢弃的流量 | 0 % |
| 集群成本 | 4.00 $/h |
| 迄今成本 | 0.00 $ |
| 缓存答案的平均时龄 | 30 s |
| 问题版本上的流量 | 0 % |
| 推荐功能可用 | 100 % |
09:10 发布了一个新构建。这个月已经很艰难:错误预算只剩 20 %。部署几分钟后,燃烧速率告警响了。保护好预算。
一个指向该服务的链接正在迅速传开,预计今早某个时候会出现流量激增——没人知道是何时,也不知道会有多大。目前新实例需要 8 分钟才能启动。流量来临时,要让错误率和延迟保持低水平,同时不要为闲置的容量浪费金钱。
中午高峰时,一个维护脚本清空了整个缓存。现在每个请求都落到数据库上,而数据库是按平时 85 % 的命中率设计的。要让服务恢复,又不能压垮数据库。
模拟器使用的每一个关系式及其来源。标注为假设的常数是示意性校准值。
λ(t) = base × (1 + 0.25·sin(2π(t + clock − 6 h)/24 h)) × surge(t), clock = time of day at the start (peak at 12:00); count/min ≈ N(60λ, √(60λ)) × (1 + N(0, 0.02))[6]假设:工作进程数、服务时间、数据库容量、缓存大小、回填速度、价格以及问题版本的错误率,均为中型 Web 服务的示意值。N = instances × 16 workers, a = λ·S; C(a, N) = B / (1 − (a/N)(1 − B)), B = Erlang B[3][6]P(W > t) = C·e^(−(N/S − λ)·t); timeouts = P(W > 2 s); a ≥ N ⇒ failed share = 1 − N/a[3]p99 ≈ S·ln 100 + ln(C/0.01)/(N/S − λ) (service + waiting quantile, an approximation)[3][6]近似:把服务时间分位数和等待时间分位数相加,并不是二者之和的精确 p99(可能略高或略低);由于请求只需几毫秒,每一分钟内把队列视为稳态。busy workers L = λ·S ⇒ utilization = λ·S / N[4]hit = warm × rT/(1 + rT), r = λ / 20,000 objects; mean age of a cached answer ≈ T/2[5]DB load = λ·q·(1 − hit); query time = 5 ms/(1 − ρ_db) (≤ 250 ms); S = S_app + (1 − hit)·q·query time[6]假设:工作进程数、服务时间、数据库容量、缓存大小、回填速度、价格以及问题版本的错误率,均为中型 Web 服务的示意值。budget = 1 − SLO = 0.1 %; burn = error rate / 0.1 %; Δbudget per min = burn / 43,200; burn (1 h) = mean error rate over the last 60 min / 0.1 % (window pre-filled with the opening minute)[1][2]desired = ⌈serving × utilization / target⌉ (utilization saturates at 100 %); new instances serve after the boot delay[6]假设:工作进程数、服务时间、数据库容量、缓存大小、回填速度、价格以及问题版本的错误率,均为中型 Web 服务的示意值。16 workers/instance · app time 50 ms (+20 ms and +1 query with the feature on) · 2 queries/request · DB 4,000 queries/s · 20,000 hot objects · cache refill τ = 30 min (slower while the DB is saturated) · warm-up job +12 %/min for 6 min, +600 queries/s · timeout 2 s · 30 % low-priority traffic · bad build +5 % errors, ×1.25 CPU · rollback 5 min · $0.40 per instance-hour · scale-in by ≤ 20 % of the fleet after 10 quiet minutes · up to 40 instances假设:工作进程数、服务时间、数据库容量、缓存大小、回填速度、价格以及问题版本的错误率,均为中型 Web 服务的示意值。随机性:带种子的 mulberry32 生成器;所用分布包括均匀分布、指数分布(逆 CDF)、正态分布(Box–Muller)和泊松分布(Knuth)。种子会显示出来,并且可以分享。
教育模型,不可用于实际运营决策。真实站点会根据自己的设备和数据校准每一个常数。