CAL(Central Application Logging)系統主要負責收集和處理 eBay 内部各個應用程序池的日志,日處理超過 3PB 的數據,供運維團隊和開發團隊日常監控使用。
CAL 系統通 過 HTTP 接口接受應用産生的日志,将日志持久化到經 NFS 挂載 的網絡存儲上, 用戶(運維團隊和開發團隊)可以通過 CAL 系統方便地 查找、查看日志 。同時,日志也會被導入 Hadoop 進行進一步的分析形成報告。該系統自 21 世紀初至今,已經有 10 多年 的曆史了。
CAL 從第一天起就運行在 Netapp 的商業存儲 上。随着業務的發展,業務産生的數據量急劇增加,單個存儲集群已經無法承載 CAL 的流量和性能需求,存儲團隊和業務團隊通過增加集群來解決性能問題。一直到 2018 年 ,CAL 在每個數據中心已經需要 25 個 集群才能支撐起其性能需求。
雖然統計上,CAL 隻使用了 eBay 5% 的 NFS Filer 總容量,但實際上卻消耗了 50% 的總性能。性能和容量的巨大偏離,使得實際成本已經比該存儲方案的 裸 $/GB 成本 高了一個數量級。
高成本,疊加上由 eBay 業務驅動的每年 30% 自然增長,這套架構亟需重構優化。
2014 年,eBay 存儲團隊開始在公司内将 Ceph 應用于生産環境。第一階段主要是 RBD 塊設備服務。
2017 年,Jewel 版本發布後,我們開始嘗試提供 CephFS 分布式文件系統服務,多個天使應用從商業存儲遷移到 CephFS 上,從中我們發現修複了 MDS 的多個 bug 并貢獻回社區,也在社區首次完成了 MDS 的性能分析。
2018 年,随着 MDS 多活功能的初步成熟,我們也和業務團隊一起,開始了把 CAL 遷移到 CephFS 上的征程。
2019 年初上線至今,Ceph 在大流量下穩定運行。平均讀寫帶寬同時超過了 3GB/S ,讀寫請求數合計超過 100K IOPS(每秒進行讀寫操作的次數), 文件系統元數據操作數超過 30K OP/S(每秒操作次數)。
01 總體設計
通過對 CAL 現有業務邏輯和曆史性能數據的分析,我們明确了 設計目标 :
1)文件系統元數據路徑,支持 100K OP/S ,包括:create / getattr / lookup / readdir / unlink 等文件系統操作;并預留足夠的升級空間滿足業務發展。
2)文件系統的數據路徑,支持 150K IOPS(50% 讀,50% 寫),以及至少 6GB/S(50% 讀,50% 寫)的吞吐。
3)單集群容量需求300TB 可用空間,滿足性能需求的前提下盡可能降低存儲成本。
這個略顯激進的設計目标,其實在很大程度上已經決定了我們的方案。
- 基于我們之前針對 Ceph MDS 的性能研究,單 MDS 隻能支持 2K ~ 4K OP/S, 要支持 100K OP/S, MDS 多活負載均衡集群 是唯一的路徑。至少需要 25 個 MDS Active-Active 組成集群。同時,業務負載還要能相對均衡地分布到每個 MDS 上。
- 150K IOPS 的讀寫性能, 如果單用機械硬盤 HDD ,需要至少 1500 塊 盤,可用容量接近 3PB ,又回到了用盡性能而浪費容量的老路。 如果全部用 SSD , 1PB 裸容量的 SSD 雖然能比商業存儲省錢,但仍是一筆不小的數字。針對日志數據有明顯 冷熱度 的特點, 使用分層存儲,把頻繁訪問的數據放在 SSD 上,相對冷的數據放在 HDD 上, 才能達到最佳費效比。
02 實現細節
1. MDS 多活負載均衡集群
要實現向外擴展(scale-out)的架構,核心是任務分配,把負載相對均等地分配到每個 MDS 上。對于文件系統來說,整個目錄結構是一棵樹。任務分配的本質,是通過設計目錄樹的結構,使得 葉子(文件) 平均地長在這顆樹的每個 枝幹(目錄) 上。同時,每個層次的文件 / 文件夾個數必須是 可控且相對均衡的 ,這樣才能盡量降低樹的深度,避免對一個巨大文件夾進行 ListDir 操作 帶來的延遲和開銷。
CAL 原先的目錄層次如下圖 1 所示,一共有 15 層,100 多萬 個文件夾, 幾乎違反了上面讨論的所有原則,目錄過深且不均衡 。eBay 每個 pool 承擔一個服務,每個服務的流量,服務器數量差異顯著。更糟糕的是整個目錄樹,在每個小時開始的時候,幾乎完全重建(虛線部分),導緻每小時開始時元數據操作風暴。
圖 1(點擊可查看大圖)
針對這個問題,我們和 CAL 團隊合作設計并實現了 一套新的目錄層次 ,如圖 2 所示:
圖 2(點擊可查看大圖)
1)在根目錄下建立 1024 個 Bucket, Client (CAL 的 Client,其實是業務 APP 服務器) 通過哈希映射到其中一個 bucket 下 。由于哈希的(統計意義)均衡性,每個 Bucket 裡的 Client 文件夾數量是均衡的。而 Client 文件夾的總數,和業務 APP 服務器的總數對應。
雖然虛拟化、容器化使得 Client 越來越小,越來越多,但因為 Bucket 數量足夠多, 每個 Bucket 下的文件夾數依舊是可控的 。而對于每個 Client 文件夾裡的文件數,根據配置,每小時産生 5-20 個 不等的文件,文件總數 = 5~20 /hour * rotation_hours。 可見,其文件數也是可控的 。
2)Bucket 通過 auth_pin , 綁定到 MDS_rank 上, 簡單來說:
auth_pin = bucket_id % NUM_MDSs
基于之前讨論的 Bucket 均衡性,以及所有 Bucket 被均衡地分布到各個 MDS。 可見,MDS 的負載也是均衡的 。
除此之外,我們也預留了拓展性。當前我們用了 33 個 MDS 做了 AA,其中 32 個 MDS 分别綁定了 32 個 Bucket,rank 為 0 的 MDS 綁定到了根目錄。日後,隻需要增加 MDS 的數量,例如從 33(32 1) 增加到 65(64 1) ,修改 auth_pin 綁定關系,就能在客戶不感知的情況下實現無縫擴容。
2. Cache Tier
Ceph Cache Tier 是一個在誕生之初被抱以巨大期望,而在現實中讓不少部署踩坑的技術,尤其是在 RBD 和 CephFS 上。在社區郵件組和 IRC 裡最經常出現的問題就是應用了 Cache Tier,性能反而下降了。
其實,那些場景多數是誤讀了 Cache Tier 的應用領域。Cache Tier 的管理粒度是 Object(默認 4MB) , 而在 RBD/CephFS 上的用戶 IO 通常明顯 小于 4MB 。當用戶訪問未命中,Cache Tier 決定将這個 Object 從 Base Tier 緩存到 Cache Tier 時, 付出了 OBJ_SIZE 大小的 慢速讀 (Base Tier)加上 OBJ_SIZE 大小的 快速寫 (Cache Tier)。
隻有後續用戶對同一個 Object 的重複訪問達到足夠多的次數,才能體現 Cache Tier 的性能和成本優勢。例如,業務負載是随機 IO,平均請求大小 4KB ,則命中率必須達到 **99.9%** 以上。
現實是許多業務負載并沒有這樣的特性。
在 CAL 的業務模型裡,典型的 IO 負載是應用日志每隔一分鐘或者 128KB 觸發一次寫入。同時,讀取端不斷地追蹤日志,以同樣的間隔把數據讀走。 針對這個特點,我們決定将 Cache Tier 配置成為一個 Writeback Cache(回寫式緩存), 這樣就能實現多個目的 :
1)寫入緩沖與合并。Cache Tier 的設計容量足夠緩存一小時的寫入量,不論業務的寫入請求大小是多少,都會被 Cache Tier 吸收,以 OBJ_SIZE(4M)為單位刷回 Base Tier。4M 的大塊寫,是對 HDD 最友好的訪問行為,可以最大化 Base Tier 的吞吐量。
2)讀取緩存。CAL 的讀取行為也大多集中在對最近一個小時寫入數據的讀取。這部分數據在 Cache Tier 裡,所以讀請求也能基本全部命中 Cache Tier。
3)故障隔離。機械硬盤的故障率比 SSD 高幾個數量級,并且更容易因壞道導緻響應時間增長,從而使得 Ceph 集群出現 慢請求(slow request) 。表現在服務質量上,就是延遲相對不穩定且 尾延遲(tail latency) 特别長。借助 Cache Tier 的緩沖,業務并不會直接感受到由全機械硬盤構成的 Base Tier 的性能抖動,從而達到更好的性能一緻性。
值得一提的一個優化點是:我們通過禁用 Hitset 關閉了 Proxy Write 這個功能。Ceph Cache Tier 的默認行為是 Proxy Write——即若待寫入數據在 Cache Tier 不命中,Cache Tier 會直接把請求寫入 Base Tier,并在寫入完成後才将結果返回給應用。
這是一個正确的設計,主要是為了避免寫請求不命中時 promote 帶來的額外延遲,并解耦數據寫入邏輯和 Cache Promote 邏輯。
但在 eBay 日志的應用場景中,因為寫入行為的順序性和日志不可變更的特性,可以明确知道 寫入不命中一定是因為寫入了一個新的 Ceph Object,而非對老 Object 的改寫,因此 proxy write 的邏輯就帶來了額外的開銷 。
例如,對 obj x 的第一次寫入, write(obj_x, 128kb), 在默認的 Proxy Write 行為下 ,這個寫被 proxy 到了 Base Tier 。寫入成功後,在 Base Tier 留下 128KB 的 object,然後 Cache Tier 再把 obj_x 從 Base_tier promote 上來。
而關閉 proxy write 後,則先對 obj_x 做一次 promote,在 Cache Tier 中産生一個空對象,再直接寫入 128KB 數據即可。
相比之下,後者節約了 Base Tier 一次 128KB 寫和一次 128KB 讀,對于由全機械硬盤構成的 Base Tier 來說,這樣的節約意義重大,并且應用的寫入延遲大大降低了。這部分 Ceph 具體代碼,可以閱讀 PrimaryLogPG::maybe_handle_cache_detail 的實現。
其他的配置優化包括設置 min_flush_age 和 min_evict_age 來保證最近一小時的數據不會被刷出 Cache,以及對 target_max_byte, target_dirty_ratio, target_dirty_ratio_high 的調整,在此不一一贅述。
通過上述一系列優化, 如圖 3 所示,在寫入側, Cache Tier 非常好地完成了寫入緩沖與合并,來自應用的 25K IOPS 經由 Cache Tier 緩沖與合并之後到 Base Tier(fs_data) 寫入請求隻有 1.5K IOPS。
換句話說,通過 Cache Tier 我們減少了 94 的寫 IOPS。
圖 3 集群客戶端寫入性能(點擊可查看大圖)
而在讀取側,如圖 4 所示,幾乎所有的 IO 都在 Cache Tier 上發生, Base Tier 讀流量近乎為 0 。
圖 4 集群客戶端讀取性能(點擊可查看大圖)
綜合來看, 在 Cache Tier 上的總 IOPS 達到了 70K ,而 Base Tier 隻有 1.5K IOPS。通過深入理解業務的 IO 模型,合理配置 Cache Tier,我們實現了 分層存儲 。
應用享受到了全閃存集群的性能,成本上卻接近全機械盤的價格,實現了了性價比最大化。
03 遇到的問題
下面分享幾個在實施日志存儲方案中遇到的 軟硬件問題 ,附上我們的 解決方案 ,以供大家參考。
1. Bluestore Allocator(空間分配器)
在上線過程中,我們很順利地度過了 25%, 50% 的流量。但在 75% 流量時,遇到了詭異的性能問題。
如下圖 5 所示, Cache Tier 的寫入延遲暴增 ,甚至能達到分鐘級别,通過 OSD Performance Counter 很快把問題縮小到 Bluestore 内部, 結合日志發現延遲主由 STATE_KV_COMMITING_LATSTATE_KV_COMMITING_LAT 約等于 commit_lat。
我們第一個懷疑的對象是 RocksDB 的性能,尤其是 compaction 帶來的影響,并在此做了大量的調優,然而一無所獲。
但我們發現,在調優的過程中,為了修改參數重啟 OSD 服務後,被重啟的 OSD 能保持 20 小時 良好性能,之後延遲慢慢衰減到重啟以前。 既然重啟 OSD 能暫時緩解問題,那 RocksDB 就沒有嫌疑了 。
圖 5 Bluestore commit latency 性能監控(點擊可查看大圖)
如下圖 6 所示,進一步觀察分析 性能計數器(performance counter) 的數據并結合代碼,我們注意到: STATE_KV_DONE_LAT 雖然平均值隻有 STATE_KV_COMMITING_LAT 的 3% ,但與 STATE_KV_COMMITING_LAT 有明顯的相關性。
這個發現大大縮小了問題的範圍,因為 STATE_KV_DONE_LAT 所覆蓋的代碼範圍基本隻包含 _txc_finish 這一個函數。在某些時候,_txc_finish 釋放空間後,需要等待超過 100 毫秒 才能獲得分配器的鎖。同時, perf 的結果也指向了 StupidAllocator 這個循環 。
圖 6(點擊可查看大圖)
如下圖 7 所示,進一步分析日志,我們發現在 StupidAllocator 中的空間碎片非常嚴重 。雖然磁盤利用率不超過 50% ,但分配器裡已經沒有超過 256K 的 數據段(segment) 可供分配。這就解釋了為什麼 StupidAllocator 會在上述的分配空間循環中耗費大量時間。因為分配時持有了分配器鎖,所以釋放空間時需要等待很長時間來獲得鎖。
圖 7(點擊可查看大圖)
通過分析 Bluestore 的分配表,我們發現其實物理空間并沒有碎片化,隻是 StupidAllocator 在内存中的實現導緻了碎片化。
StupidAllocator 是一個類似于内存管理中 Buddy 分配的一個實現。
通過下圖 8 的例子,我們看看碎片是如何産生的:
一個 20K 的連續空間,經過 5 次 4K 分配,和亂序的 5 次 對應的 4K 釋放後,會變成 8k 4k 8k 三塊空間。
其中 [8K, 12K) 區域已經是個碎片,但因為和周邊區塊不是同樣大小,落到不同的 bin 中,已經很難再被合并了,而類似的操作序列,在 Bluestore 日常面臨的空間分配回收請求中并不鮮見。
OSD 重啟過程中, 分配器的内存結構會根據磁盤上的位圖重新建立,恰好是一次全局的合并,這也就解釋了為什麼重啟服務能暫時緩解這個問題。
圖 8(點擊可查看大圖)
我們首先在社區發現定位了這個問題,并與核心開發者一起驗證了 Naultilus 中的 位圖分配器(Bitmap Allocator) 沒有類似問題,且性能表現更為穩定。因此也使得位圖分配器被完整地反向移植到 Luminous 和 Mimic 版本中,并成為默認分配器。
2. 應對 SSD 穩态性能下降
關于 SSD 性能有一個并不冷門的常識——SSD 的穩态性能與标稱性能不一定是一緻的。對于普通數據中心級别的 SSD 來說, 穩态性能 通常比 非穩态性能 低(相應的,非穩态可以認為 SSD 尚有足夠空間供分配,或者後台維護行為對業務 IO 無幹擾),非寫入密集型的型号差距會尤其明顯。
SSD 進入穩态是需要一定寫入量和時間的,因此在做硬件性能和軟件性能調優時需要将這個因素充分考慮進去,否則實際産品運行起來以後就會出現意料不到的問題。
圖 9 是我們實測某 DWPD 為 0.7 的産品穩态與非穩态性能比較。 藍色線 是穩态性能, 橘色線 是非穩态性能(對 SSD 做了全盤 TRIM)。
圖 9 SSD 穩态與非穩态性能對比(點擊可查看大圖)
受限于成本以及當前可用服務器選擇,我們所采用的 SSD 即屬于 非寫入密集型 (DWPD 較低),同機型同容量,來自 3 個 不同廠商的 SSD 的 DWPD 分布在 0.7~1 之間。而 eBay 日志存儲 Cache Tier 恰好是寫入讀取都很密集的類型。這下,我們似乎面臨了巧婦難為無米之炊的困境。
基于我們對 SSD 實現原理的理解,非寫入密集型 SSD(DWPD 指标低)穩态與非穩态性能差距大的主要原因之一是廠商針對這類 SSD 的産品設計上,出廠預留的 Over Provision 空間 較小。
當合理的産品設計面臨“不合理”的業務需求時,大寫入壓力持續一段時間以後,SSD 固件做 空間整理 和 垃圾回收(GC) 的效率會變低。應用端的表現是在持續的大負載寫入下,GC 發生時延遲和吞吐都會受到很大的負面影響。
分析完這個可能原因後,在無法變更硬件的前提下,我們的解決辦法是通過主動預留空間,來彌補廠商原有 OP 先天不足的“缺點”:
在 SSD 被加入集群前,我們對全盤 TRIM 之後,分區時隻使用 80% 的空間;留下 20% 被 TRIM 過的區域因為在固件 FTL 中标記為空閑,自然會在 GC 等操作的時候當作緩沖來使用。加上這額外的 20% OP 後,我們 SSD 的寫入延遲和帶寬都能穩定在最佳狀态(圖 9 中 橘色線 的性能)。
這套實踐應用在同機型、多批次來自 3 個不同廠商的 SSD 上都獲得同樣好的效果,上線至今并未觀察到衰減。
那麼 20% 的空間預留是目前的經驗值,當應用行為有所改變時 SSD 性能再次衰減又該怎麼應對呢?需要停下服務重新修改預留空間大小嗎?
其實不然, 對于已經衰減的 SSD, 隻需要 TRIM 20 的區域提供 OP,再經過一個完整的寫入周期後,性能就可以回升到最佳狀态 。
圖 10 展示的就是這樣一個過程,在模拟測試開始約 600 秒 時,性能開始大幅下降,此時對預留的 20% 區域做 TRIM,經過一個多小時,性能逐步回升,最終回到峰值并持續穩定下去。
圖 10 對預留區域 TRIM 恢複全盤性能測試(點擊可查看大圖)
在這裡需要指出:上述經驗是我們基于存儲産品的理解和合理猜測基礎上,經測試驗證通過,并在生産中應用的。 限于條件和知識所限,我們無法進一步探究 OP 是否是同系列不同定位的 SSD 産品之間的主要差别 。
通過我們所接觸的有限樣本中的數據,可得出以下觀點: 讀密集型 SSD 加上大的 OP,能達到同系列寫入密集型 SSD 同樣的性能和可靠性,但我們并不确定該經驗是否在更大範圍内具有通用性 。這裡僅供讀者參考,也歡迎來自各廠商的專家提供更多的分享。
04 總結
在 eBay 存儲團隊和業務團隊的無縫合作下,我們完美地将 eBay 應用的日志從商業存儲遷移到了 CephFS 上,達到了非常高的性能和費效比(遷移給 eBay 帶來的成本下降在單數據中心達 數百萬美金 )。整個方案沒有單點和瓶頸,各個部分均可以橫向拓展,支撐更高的性能和吞吐。
CephFS由于成熟得相對晚,在國内外的大規模商用案例有限,更多的被作為歸檔和冷存儲使用,較少有大流量的線上業務應用。盡管我們比較激進的使用了 MultiMDS 多活 , Cache Tier 等當前在業界還較少部署的技術, 但 CephFS 在大流量的生産環境中證明了自己的價值和穩定性,為其他 eBay 内部應用遷移到 CephFS 打下了堅實的基礎 。
,