增加負載的兩種方式
歡迎
當一個服務在承受負載時開始變得緊張,操作者面臨著一個選擇。將現有的機器變大(更多的 CPU、更多的 RAM、更快的磁盤)。或是添加更多的機器,每個機器都做同樣的工作。
第一條路線稱為垂直擴展(scale up)。第二條路線稱為橫向擴展(scale out)。
這堂課將教你為什麼幾乎每個現代的網絡架構都選擇橫向擴展,且什麼特性使得這個選擇成為可能。答案隱藏在一個詞中:狀態。
在結束時,你將了解:
- 垂直與橫向擴展的成本曲線,以及各自適用的情況
- 在實踐中,'狀態有'和'無狀態'的含義,以及其中一個可以經濟擴展的原因
- 在預期和激增負載下,規模一個副本隊伍的數學
- 保持一個層次不崩潰的頭room規則,超過排隊關節
- 狀態必須放在哪里(它永遠不會消失)以及如何將其從需要擴展的層次推出
為什麼橫向擴展在閾值後獲勝
垂直擴展:一個更大的盒子
優點:簡單。無需更改代碼。無需協調。同一過程現在具有更多的 CPU。
缺點:天花板。商業上可得的最大 VM 具有限 RAM 和核心。超過該限制,錢無法買到更多的頭room。成本在超過供應商的甜點區後會呈現超線性增長。該機器失敗時,整個服務都會下線。
橫向擴展:多個較小的盒子
優點:沒有天花板(到你的付款意愿和協調機器為止)。容量與副本數目成線性關係,預測性強。單個副本失敗將移除 1/N 的容量,而不是 100%。
缺點:需要工作負載支持。一些工作負載(一個大型數據庫、一個保持活躍會話的狀態遊戲服務)抵制橫向擴展。協調和負載分配成為運營問題。
交叉点: 任何需要在多台机器上运行以抵御单点故障的生产服务都必须在至少两台机器上运行。一旦接受了两台机器,你就已经选择了水平扩展。从那时起,问题不再是‘我们应该吗?’而是‘我们如何以最低成本添加下一个副本?’
关键驱动力: 一个不需要在机器本身上保存每个请求状态的工作负载。那么任何副本都可以回答任何请求,并且添加一个副本可以不需要任何协调来增加容量。
有状态 vs 无状态 在实际应用中
状态从系统中消失了,它只是移动了
有状态组件: 持有信息,如果丢失将改变行为。例如,持有用户帐户的数据库,持有会话令牌的缓存,持有长时间运行流连接的特定用户的工作者。
无状态组件: 不持有丢失将重要的信息。例如,读取请求的Web层,查询数据库,写入响应。每个请求独立;层不记得请求之间的任何内容。
关键见解: 系统中的状态从未消失。它移动到一个设计来持有的层(数据库、Redis集群、对象存储)。面向流量的层可以成为无状态,然后无状态层可以水平扩展,因为任何副本都可以回答任何请求。
实际测试: 如果你在这个层中随机杀死一个进程并重新启动它,用户是否会遇到错误的答案或丢失会话?如果是的话,它持有状态;如果不是,它不持有状态。
示例
- 读取请求的Python Web进程,查询Postgres,返回JSON:无状态。状态存储在Postgres中。
- 持有用户购物车的本地内存的Python Web进程:有状态。杀死进程会丢失购物车。
- 持有打开的连接到聊天用户的WebSocket服务器:在连接上有状态。杀死进程会丢失连接;客户端必须重新连接。通常,这些仍然可以以小心的方式水平扩展(粘性会话、一致性散列)。
- 使用 Redis 缓存保护 Postgres: 缓存内容是有状态的,但如果缓存失效是可以接受的。复制失败意味着缓存失效,而不是数据丢失。
为了实现水平扩展,需要将状态从需要扩展的层次中移出。
审计可疑层
一组团队在 6 个后端 VM 上运行一个推荐 API。该应用程序: 从请求中读取用户 ID,从 Postgres 中检索用户的最近活动,运行评分算法,返回一系列推荐项目。两个非标准行为:
- 应用程序在进程内存中保留一个 '最近用户活动' 缓存,在用户首次请求时填充,在后续请求时重复使用。
- 应用程序使用粘性会话: 一旦用户访问到 VM #3,随后的所有请求都会发送到 VM #3 (代理配置为基于 cookie 的粘性路由)。
复制公式
最简单的容量公式
一旦层变为无状态,就可以用算术来计算容量。你需要足够的复制实例,以便在稳定状态下,负载的到达和离开的速度相等,以留有应对峰值的头room。
公式:
replicas = ⌈ (peak_load × surge_factor) / per_replica_capacity ⌉ + headroom
其中:
- peak_load: 期望在正常运行中达到的最大持续请求/秒
- surge_factor: 覆盖在峰值之上的短暂爆发的乘数(通常为 1.5x 到 2x 的可预测流量,3x 或更高的突发/不可预测流量)
- per_replica_capacity: 允许接受的延迟和利用率下一个复制实例处理的请求/秒(通常在 70% CPU 测量,而不是在饱和情况下)
- headroom: 为了让几 个复制实例的失败不会让整个层崩溃,额外的复制实例(对于较小的舰队通常为 1-2 个,对于较大的舰队为 10-20%)
工作示例:一个后端每秒处理100个请求,占用70%的CPU每个副本。峰值负载为600个请求每秒。您期望偶尔会有2倍的洪峰。您希望在出现3个副本故障时survive不超过80%的负载。
副本数 = ⌈ (600 × 2) / 100 ⌉ + 2 = 12 + 2 = 14个副本
80%的规则
副本的容量不是饱和点。永远不要在100%的CPU使用率时测量容量,而要在70-80%的CPU使用率时测量。
在80%利用率以上,队列曲线会陡然上升:在60%利用率时运行的队列在90%利用率时运行时间会从10毫秒延长到80毫秒。延迟,而不是吞吐量,会首先出现问题。(《stateless_horizontal_scaling的几何》这个补充课程推导了这个曲线。)
自动扩展与静态分配
静态:为峰值乘以增压头room并接受低利用率的成本在非峰值时段。
自动扩展:根据观察到的利用率、目标延迟或队列深度,控制器添加和删除副本。
自动扩展的注意事项:冷启动时间很重要。如果一个新的副本需要2分钟来启动,自动扩展无法响应30秒的洪峰。成熟的自动扩展在scale-up阈值下保留一个预先分配的副本池,只用于预先预留副本。
为新服务分配舰队规模
您的团队计划推出一个视频元数据API。基准测试显示一个副本每秒处理250个请求,占用70%的CPU,99%的延迟为50毫秒。营销预测峰值负载为4000个请求每秒,在黄金时间段内。计划的促销活动可能会突增到峰值的3倍。您希望服务在出现3个副本故障时survive不超过80%的利用率。
冷启动、缓慢排放和其他现实边缘
真实的舰队有真实的边缘
公式假设副本瞬间出现,立即接受流量,立即排除流量。生产中这些条件都不成立。
冷啟動:一個新的副本需要啟動操作系統、啟動進程、加載配置、預先加載快取以及通過健康檢查。冷啟動的時間範圍從(容器重啟)5秒到(全虛擬機啟動加載圖像)5分鐘。自動調整無法在這個延遲內響應更短的突發需求。
慢放水:一個副本在從池子中移除時,需要一定的時間來完成在途中的請求,然後再終止。否則用戶會看到截斷的響應。反向代理支持放水(停止接受新的請求,完成正在進行的請求),但這需要幾秒鐘到幾分鐘的時間。
溫暖池:生產隊伍保持一個預先配置但空閒的副本池,隨時準備接收流量。它在保持一定穩定成本的同時,能夠快速響應突發需求。
放水排水 vs 立即殺:優雅關機很重要。一個發出SIGTERM的信號進行排水需要更長的時間,而SIGKILL則不會中斷用戶請求。
健康檢查時間窗:一個剛啟動的副本可能在其數據庫連接池加熱之前通過其第一次健康檢查;代理然後將實際流量發送到這個副本,首先的十二個請求會比較慢。調整健康檢查,以測試實際路徑,而不是僅僅測試進程存活。
黏性滲透:即使是 nominally stateless 層也會在一段時間內獲得黏性(CDN快取,DNS解析器快取,連接池)。對於‘相同副本’表示有疑慮,即使它們在行為上有所不同。
溫暖池或反應式自動調整?
您的視頻元數據API(與前一個問題相同,規模為51副本,為穩定峰值和突發事件)在新視頻上傳時遇到30秒的突發需求,達到正常需求的5倍。當前自動調整從冷啟動中添加一個新的副本需要90秒(圖像下載加載)。在90秒的斷裂期間,延遲急劇上升,部分請求超時。
根據限制設計無狀態層
整合
您已學會為何水平擴展在小範圍內優於垂直擴展,實際上狀態意味著什麼,如何根據預期與突發負載來規劃艦隊規模,以及水平擴展在邊緣處的局限性。
應用所有四點。
為 feed.example.com,一個社交feed API設計後端層。限制:每個副本的容量為200 req/s,CPU使用率為70%;預期峰值負載為1500 req/s;突發因素為2.5x(偶爾會有趨勢故事);在兩個副本失敗的情況下保持運行;冷啟動時間為60秒;突發負載可以持續45秒;預算允許一些閒置容量,但不允許2.5x的永久擴展。
課程下一步將去向
課程下一步將去向
您現在已經擁有一個無狀態層的工作思維模型:為何它可以擴展、如何規劃其規模、在邊緣處出現的問題以及狀態在被推出需要擴展的層次時,需要移動到的位置。
接下來的課程 (cs_distsys_ingress_egress_separation) 探討一個更微妙的問題,即使無狀態層的規模已經被完美規劃,當進入與出入流量共用相同網絡路徑時,它仍然可能出現意想不到的失敗。經典的例子涉及到代理嘗試連接到自己;解決方案涉及將一個層分為兩個,具有不同責任。
伴隨課程:geometry_of_stateless_horizontal_scaling Derived the queueing curve, Little's Law applied to a replica fleet, & the geometric meaning of the 80% utilization knee。
好工。繼續前進。