JuiceFS 在攜程海量冷數據場景下的實踐

作者簡介

妙成,攜程雲原生研發工程師,主要從事 Elasticsearch、JuiceFS 的研發運維,關注分佈式數據庫、NoSQL。
小峯, 攜程雲原生研發工程師,主要專注於數據庫容器化領域,對分佈式存儲有濃厚興趣。

一、摘要

攜程的冷數據規模在 10PB+,包括備份數據、圖片語音訓練數據和日誌數據等,存儲方案主要是本地磁盤和 GlusterFS。在實際使用中這些方案遇到了不少痛點:

隨着雲計算技術的發展,公有云廠商爲混合雲客戶提供了海量冷數據的廉價存儲方案,經過嚴謹的成本計算,我們發現使用公有云的對象存儲可以顯著降低存儲和運維成本。爲了減少遷移成本,我們一直在尋找後端存儲能支持各類公有云對象存儲、高性能的文件系統,直到 JuiceFS 出現在我們的視野中。JuiceFS 有以下優勢:

經過大半年的測試和使用,我們已經對接了數據庫備份和 ElasticSearch 冷數據存儲,將 2PB + 的數據遷移到了 JuiceFS,預計後續還會有 10PB + 的數據接入。目前 JuiceFS 系統穩定,在降低運維成本和存儲成本方面取得了良好的效果。本文將對 JuiceFS 原理以及我們在使用中所遇到的問題和採取的優化方案進行簡單介紹。

二、JuiceFS 架構與 POC 測試

2.1 架構簡介

JuiceFS 將元數據信息和真實數據塊分開管理,通過 FUSE 實現 POSIX 接口,允許用戶像本地文件系統一樣使用。用戶將文件寫入 JuiceFS 掛載的目錄後,文件數據會存儲到對象存儲,相應的元數據(文件名、文件大小、權限組、創建修改時間和目錄結構等)會存到元數據引擎中。在該架構下,ls、數據刪除等操作只是對元數據引擎的操作,不受到對象存儲的速度限制,性能上會有較好的保證。

圖片

2.2 元數據引擎選型與測試

JuiceFS 的元數據引擎有很多選擇,包括開源的 Redis、TiKV 以及官方提供的閉源的企業版元數據引擎。考慮到攜程的數據規模較大並且後續會有更多的數據接入,元數據引擎需要能夠支持 TB 級元數據的存儲並且能橫向擴容。因此 TiKV 和官方的企業版元數據引擎成了我們的備選方案。

爲了驗證 TiKV 的性能,我們使用 go-ycsb 做了一些性能測試。

t5texC

測試結果:

1)Write 事務寫入操作,隨着客戶端線程數增加,TPS 上升,峯值超過 30000

圖片

2)Get 事務讀取操作, 隨着客戶端線程數增加,QPS 上升,單節點峯值接近 70000

圖片

從測試結果看,TiKV 有較高的讀寫吞吐量,並且單次操作的響應時間 P99<10ms,在冷數據場景中性能表現可滿足業務需求。

官方的企業版元數據引擎比 TiKV 有更好的性能表現,但是考慮到冷數據存儲對性能要求並不苛刻,而且相比於對象存儲 20~200ms 的訪問速度,元數據引擎並不會明顯降低整個系統響應的速度。爲了減少技術黑箱,我們選擇了 TiKV 作爲元數據引擎。

2.3 JuiceFS 整體 POC 測試

在交付生產之前,爲了明確 SLA 指標和最佳使用場景,我們使用 mdtest 對以 TiKV 爲元數據引擎的 JuiceFS 進行了整體 POC 測試,部署使用如下架構:

圖片

1)單線程寫入,測試文件大小與吞吐量的關係

圖片

測試結果表明隨着文件大小增大,吞吐量也隨之增大。在單文件爲 128MB~256MB 左右時,原先的吞吐量與文件大小的增長曲線明顯放緩。可以理解爲當文件較小時,JuiceFS 客戶端與元數據引擎和對象存儲的交互成本與有效數據傳輸成本相比,佔比較高,限制了吞吐量;當文件較大時,交互成本佔比降低,吞吐量上升。爲了發揮充分 JuiceFS 的吞吐能力,建議存儲 128MB 以上的文件。

2)目錄深度與 JuiceFS IOPS 的關係

圖片

測試結果表明目錄深度與 JuiceFS IOPS 沒有明顯關係。研究 JuiceFS 代碼可知,雖然深度越深,文件路徑變長,但 JuiceFS 在創建文件 / 目錄時在 TiKV 裏的 Key 是父目錄 inode + 新條目的名字,所以目錄深度不影響 TiKV 裏的鍵值對大小,就不影響 TiKV 的查詢性能,符合測試結果。

3)目錄大小與 ls 速度的關係

yNrVtH

測試結果表明目錄下文件個數對 ls 幾乎沒有影響。

2.4 元數據引擎故障測試

理論上 TiKV 節點中 Region 通過 Raft 保證一致性,即非 Leader Region 故障完全不影響上層應用,Leader Region 故障則會在該 Region 的副本中重新選舉出一個 Leader Region,選舉需要時間,並且需要上報 PD 節點進行處理,因此會影響到上層應用的部分請求。

PD 集羣用來管理 TiKV 集羣,PD 的非 Leader 節點故障完全不影響上層應用,Leader 節點故障則需要重新選舉新 PD Leader,選舉過程 JuiceFS 客戶端的請求無法得到響應,新 Leader 節點確立後 JuiceFS 重新建立連接也需要一定耗時,該時間段內會對上層應用的請求產生影響。

據此我們模擬節點故障的場景,測試實際應用過程中元數據引擎故障後恢復所需時間,計算正常場景中讀寫一定數量文件與異常情況下的耗時差異。結果表明故障影響時間可以控制在秒級。

1)TiKV 故障

tLQZXe

2)PD 故障

dEVW4f

三、JuiceFS 原理解析

3.1 文件寫入

JuiceFS 接收到寫請求會先將數據寫入 Buffer,並按照 Chunk、Slice、Block 的規則進行數據塊管理,最後以 Slice 爲維度 Flush 到對象存儲。一次 Flush 實質上是對 Slice 中的每個 Block 進行 PUT 操作,將數據寫到對象存儲,並完成元數據修改。如下圖:

圖片

1)大文件先經過 FUSE 處理成 128K 的塊,在 JuiceFS 內部拼成一個個 4M 大小的 Block,Slice 管理的 Block 不斷增加,直到 Slice 達到 64M(即一個 Chunk 的大小),觸發一次 flush 操作。Chunk、Slice、Block 的拼裝使用的是內存 buffer,其大小受 JuiceFS 啓動參數 buffer-size 的限制。                                                                              

2)小文件由新的 Slice 單獨管理,在文件寫入完成時被上傳到對象存儲。

3)如果客戶端設置 writeback 模式,JuiceFS 不會直接寫數據到 Object Storage,而是寫到 JuiceFS 所在機器的本地磁盤,後續異步寫到對象存儲。這種方式存在丟數據的風險,但是可以提升數據寫入速度。

3.2 文件讀取

讀取流程數據處理方式與寫入流程類似,讀取請求被 JuiceFS 進程接收到後會先訪問元數據引擎,找到需要讀取的 Block,向對象存儲併發發出 GET 請求。由於 Block 數據不變性,讀取出的 4M 的 Block 會寫到本地的緩存目錄中。讀取過程中按照 4M(Block) 的方式實現了一定程度的預讀,可以通過調整 prefetch 參數,將預讀窗口設置的更大,默認 prefetch = 1。如下圖:

圖片

1)大文件順序讀場景下,會讀取對象存儲中 4M 大小的對象,經過 FUSE 處理成 128K 的塊返回給用戶。此場景中緩存命中率會很高,由於預讀和本地 Block 緩存,吞吐性能較好。                            

2)大文件隨機讀場景下流程和順序讀一致,該場景下的預讀、緩存被命中的概率很低,這些邏輯反而可能影響讀取性能(需要將讀取到的數據寫入本地緩存目錄),可以通過設置 cache-size = 0 關閉緩存。

3)小文件(例如 4K)讀取場景下,會讀取當前文件的 Block,經過 FUSE 後響應給用戶程序。獲取到的數據也會寫到本地緩存目錄中。

四、故障處理與性能優化

4.1 TiKV CPU 使用率過高,導致拒絕服務

現象: TiKV kv_scan 請求數突然上升,unified_read_po 線程池 CPU 使用率被打滿

分析: 客戶端運行 cleanTrash 任務導致的。Beta 1 版本的客戶端會同時進行該任務,當同一個 volume 掛載的客戶端較多,並且 trash 中的數據量非常多的時候,該任務會對元數據引擎造成突發的壓力。

解決方案:

1)增加客戶端對元數據引擎各個接口的調用量監控,便於快速診斷是哪些客戶端導致的問題;

2)將後臺任務從客戶端中剝離,客戶端只需要執行用戶的請求,cleanTrash 這樣的後臺任務交給單獨的組件執行,便於 JuiceFS 管理員控制;

3)升級客戶端,Beta3 開始增加了分佈式鎖,並且增加了 no-bgjob 啓動參數。

4.2 TiKV 數據泄露

現象: 文件數目和 OSS 中的數據量沒有增加的情況下,region 數目不斷增加,store size 不斷增加

分析: 通過 tikv-ctl 查看 TiKV 裏的數據,發現 MVCC 的修改和刪除記錄沒有被清除。完整的 TiDB 部署會 10min 觸發一次數據 GC。但是單獨部署 TiKV,數據 GC 需要由其他程序觸發。另一方面 5.0.1 版本的 TiKV 有 bug,數據 GC 沒有清理刪除記錄,相關 issue

解決方案:

1)參考

https://github.com/tikv/client-go/blob/v2.0.0/examples/gcworker/gcworker.go 單獨實現一個組件,定期調用 GC 功能

2)升級 TiKV 到 5.0.6

4.3 CSI 掛載場景中,PV 清理後數據 OSS 中數據無法回收

現象: k8s 中的 ElasticSearch 所有 Pod、PVC、PV 下線一天後 OSS 數據仍沒被清理。

分析: PV 被清理時 CSI 執行了 JuiceFS rmr 指令,將 volume 數據全部放到回收站,根據默認配置 trash-day=1,即一天後開始回收數據。由於環境中的 JuiceFS mount Pod 已經全部下線,即沒有 JuiceFS 進程掛載了 CSI 的 volume,於是出現了沒有清理回收站的情況。

解決方案: 由於 CSI 模式使用 JuiceFS 是模擬了 subdir 的過程,即整個 CSI 管理 Pod 掛載的 volume 是同一個,通過寫到子目錄的方式進行數據隔離。我們停止了 mount pod 的所有後臺任務,另外找了一臺機器掛載該 volume 來完成自動清理回收站數據等後臺任務,該方法也消除了後臺任務帶來的客戶端性能抖動。

4.4 客戶端使用內存過高

現象: 部分使用 JuiceFS 的機器佔用內存過高,達到了 20GB+。

分析:

1)通過 cat /proc/$pid/smaps 查看,發現佔用的內存都是 Private_Dirty,說明是被 JuiceFS 進程長期持有,不是 Page Cache 緩存佔用導致。

2)通過使用 pprof 工具分析 heap 佔用情況,可推測出是 dump meta(backup)導致的異常。

圖片

解決方案: 將客戶端的啓動參數 backup-meta 默認值改爲 0,元數據備份參考官方的實現思路通過另外的組件統一實現,客戶端不執行元數據備份任務。

4.5 優化後架構

生產實踐過程中涉及數 PB 級的數據,業務場景相差巨大,經過規劃與調優,最終演進成如下架構。

圖片

1)量級較小的業務由用戶掛載的 JuiceFS client 治理 session、trash 等數據。                                                  

2)量級較大的業務(PB 級、數百 client 級)掛載的 client 不處理 session、trash 等數據的清理(開啓 no-bgjob 參數),由 admin 提供一個 client 單獨處理,並提供清理加速的能力。

3)提供了一個 client 統一做同一 tikv 集羣內所有 volume 的 backup-meta 操作。

4)提供訪問 OSS 和 TIKV 集羣的限流能力,通過命令下發修改 client 的限流能力,用於在必要情況下保護專線帶寬、元數據庫,實現服務降級。

5)使用多套元數據集羣用來隔離行爲差異較大的業務。

6)提供服務觸發 TiKV 的 GC。

五、總結與展望

通過 JuiceFS 將冷數據上公有云, Elasticsearch 實現了一定程度的存算分離,去除了副本帶來的內存需求,提升整體集羣數據存儲能力。DBA 備份數據從 GlusterFS 遷移到 JuiceFS 後 ls 等行爲的性能大幅提高,不僅運維人員不再需要投入精力進行磁盤擴容維修,大大降低了運維成本,而且用戶還能夠按照保留時間快速地控制存儲成本。

目前已有 2PB 來自 ElasticSearch、DBA 備份的數據存儲到 JuiceFS,後續我們會推動更多的數據上 JuiceFS,當然後續也很多需要進一步探索和優化的地方,例如:

1)進一步優化元數據引擎 TiKV 的性能與提升 JuiceFS 的穩定性,以應對 10PB + 的數據量

2)探索 JuiceFS 在 ClickHouse 冷數據存儲上的使用方法

3)公有云場景下使用 JuiceFS 替換 HDFS,以降低雲上的存儲成本

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/n3rkD-0nxTCHEEYWGe_GzA