隨著公司業務的快速發展,同程旅行的非結構化的數據突破 10 億,在 2022 年,同程首先完成了對象存儲服務的建設。當時,分布式文件系統方面,同程使用的是 CephFS,隨著數據量的持續增長,CephFS 的高復雜性和運維難度逐漸成為瓶頸。考慮到可觀測性、穩定性和管理效率等維度,同程最終決定轉向 JuiceFS。
目前,同程已在 JuiceFS 上構建了一個企業級存儲平臺,平臺規模涵蓋了超過 20 個文件系統和 2000 多個客戶端掛載點,能夠高效管理億級文件和百 TiB 級別的數據量。值得一提的是,整個存儲平臺的日常運維工作僅需一人。該平臺主要應用于多個場景,包括 AI 應用、容器云環境以及應用級共享存儲需求。
01 文件系統選型:從 CephFS 到 JuiceFS
在使用 JuiceFS 之前,同程內部使用的是 Ceph 來提供對象存儲和分布式文件存儲服務。然而,Ceph 的技術棧復雜度較高,掌握難度大,對使用經驗和運維經驗都有較高要求。同時,在可運維性和生態建設方面也存在一定不足,對日常穩定性保障構成了較大挑戰。
相比之下,JuiceFS 具有諸多優勢。JuiceFS 設計上實現了元數據和數據分離,這與我們內部已有的成熟對象存儲系統和分布式數據庫系統高度契合。憑借已有的技術經驗,能夠自主進行問題排查和性能分析。此外,JuiceFS 的工具鏈和生態建設相對成熟,具備良好的 POSIX 兼容性和云原生支持。特別是其 CSI 功能,提供了多種掛載模式,使我們能夠靈活選擇。中文的用戶社區也使得我們在使用過程中的溝通更為順暢。
選擇 JuiceFS 的另一個原因是它能夠與我們現有的技術棧良好融合。簡要介紹一下我們現有的基礎系統:我們自建了一個基于開源 Seaweed 構建的 S3 集群,并搭建了 S3 代理,兼容 Seaweed、Ceph 以及騰訊 COS 等公有云S3服務。S3 集群具備主從機制,可以在代理層實現從主集群切換到從集群。此外,我們的 DCDB 是一個內部使用的分布式數據庫系統,語法兼容 MySQL,基于百度的 BaikalDB 構建。
02 JuiceFS 在同程旅行的平臺化建設
在平臺化建設過程中,系統的可觀測性、應用接入與部署以及數據安全性是至關重要的要素。為了確保平臺的高效運行,我們在多個維度上進行了精心設計和優化,以實現全面的監控和高效的服務管理。
在可觀測方面,我們構建了一系列監控大盤,以全面監控關鍵指標。同時,我們接入了公司內部的監控告警系統,為容量、接口耗時等重要指標配置了告警規則。為了更高效地接入內部監控系統,我們開發了一個掛載點自動發現程序。該程序從元數據引擎中獲取當前的客戶端列表,并實時將客戶端列表的更改信息推送給我們內部的監控采集系統。
在應用接入與部署方面,我們提供了一系列易用工具,以支持應用的快速接入和部署。這些工具不僅簡化了操作流程,還降低了運維難度。此外,我們高度重視數據安全性,對重要的文件系統都實現了全面備份,以確保數據的完整性和可用性。
在監控告警的具體內容方面,我們主要關注服務端的情況,特別是元數據與 S3 服務的平均時延、請求成功率以及異常情況等關鍵指標。這些指標對于評估系統性能和穩定性至關重要。
高可用 JuiceFS 服務集群:單中心閉環
它解決的一個典型場景是 Kubernets 單中心集群。 Kubernets 本身并非跨中心集群,而是每個中心都部署獨立集群,即單中心閉環架構。在這種架構下 Kubernets 的各類資源和服務依賴通常限制在同一數據中心內,以避免跨數據中心通信帶來的延遲、帶寬消耗和網絡復雜性。JuiceFS 在 Kubernets 中,主要解決的是持久化的需求,而不是數據共享的需求。
另外,一些對于性能要求較高的應用,流量也需要保持在同一數據中心內,以避免跨中心傳輸帶來的延遲和帶寬消耗。因此,在這些場景中,會采用單中心閉環的方案,將 JuiceFS 相關服務在每個 IDC 部署為獨立集群,以確保數據存儲和計算任務在同一中心內進行,最大化性能并降低延遲。
這種方式適用于內部 Kubernets 集群等場景,主要解決有狀態應用的持久化存儲問題。同時,通過將流量閉環在同一 IDC 內,避免了跨機房傳輸帶來的性能瓶頸,從而保證系統的穩定性和性能。
高可用 JuiceFS 服務集群:跨中心閉環
這種部署方式主要應對的是那些本身跨機房部署且存在共享數據需求的應用場景。這類應用不僅需要訪問同一個文件系統,而且對高可用性的要求也相對較高。若某個機房的服務出現故障,不可能要求所有應用都切換到其他中心,因此,我們采用了跨中心部署的后端服務集群方案。
在此方案中,對象存儲,如 S3 集群以及 DCDB 等關鍵組件均實現了跨中心部署。具體而言,S3 的 Master 節點會在每個 IDC 都部署一個,以確保服務的全局可達性。同時,數據副本也會存儲在多個中心,以提高數據的可靠性和容錯性。DCDB 同樣采用了跨中心部署策略,其服務在每個中心都有部署,數據副本則通過其內置的復制機制在多個中心間同步。
為了優化流量路徑并減少跨中心傳輸帶來的延遲和成本,我們在正常情況下將客戶端請求限制在本地機房,流量通過負載均衡轉發到本機房的 S3 服務節點。這不僅保證了性能,還減少了不必要的跨機房流量。由于跨中心集群內部的數據同步和復制需求,集群內部仍然會有跨中心流量。
在出現故障的情況下,例如某個中心的 S3 服務出現問題,我們可以將流量入口切換到其他中心。這一故障切換機制進一步保障了集群的高可用性。
03 落地 JuiceFS 收益
JuiceFS 的架構與我們內部已有的對象存儲系統和分布式數據庫系統高度兼容,使得集成過程非常順利,整個項目僅投入了 2 人力,從選型到原理研究,再到最終落地的實施,僅用了半年的時間完成了從 CephFS 到 JuiceFS 的切換。基于 JuiceFS,我們成功構建了一個企業級存儲平臺,顯著提升了存儲系統的可觀測性、穩定性與管理效率。
從 CephFS 切換到 JuiceFS 后的主要收益:
- 擴展性和靈活性
- 可以無縫擴展存儲容量,并輕松應對數據量的快速增長,無需停機或影響現有業務。
- 更好地適配云計算和容器化環境,便于企業在多云或混合云環境中運行。
- 簡化運維
- 完善的可觀測性功能,方便集成到企業內部系統。
- 運維簡單,能更好的支持穩定性保障工作。
- 數據安全和可靠性
- 更強的數據容錯能力,能夠自動進行故障恢復,確保數據的高可用性。
- 提供強大的備份和災難恢復能力,保證數據長期安全可靠。
目前,JuiceFS 已在多個場景中提供了強大的存儲解決方案,滿足了不同應用的需求:
- 容器云平臺:作為基礎架構,JuiceFS 有效支持了云中心的持久化存儲,尤其是通過容器存儲接口(CSI)實現了有狀態應用的數據持久化功能,解決了容器化環境中對持久存儲的核心需求。
- 大數據與 AI 平臺:在大數據和 AI 應用中,JuiceFS 為海量數據存儲提供了高效的支持,特別是在模型訓練和數據處理過程中,顯著提升了存儲性能,解決了對大規模數據存儲的需求。
- 應用共享文件場景:JuiceFS 使多個應用實例能夠便捷地共享文件資源,替代了傳統的數據傳輸方案(如 SFTP),優化了應用間的數據交換和資源共享。
- 數據冷備:盡管對象存儲被廣泛采用,但一些用戶仍然偏好文件系統接口,JuiceFS 正是為這些場景提供了可靠的數據備份解決方案,確保了數據的長期可靠性與可訪問性。
04 JuiceFS 使用中的挑戰與優化實踐
讀寫性能優化
首要挑戰來自于一個關鍵業務場景——商品庫業務。在該場景中,寫服務負責數據的全量和增量更新,而讀服務需要在更新完成后(通常為 10 分鐘內)迅速加載數據。
在此過程中,我們觀察到了一系列性能上的挑戰。特別是在數據加載階段,會產生大量的目錄列表讀取和文件操作,峰值帶寬需求高達 20Gbps,同時元數據操作量也達到了 4 萬次的高峰。為了應對這些挑戰,我們對后端服務進行了針對性的優化,涵蓋了資源存儲和元數據管理兩大方面。
在 S3 存儲方面,我們進行了大量的鏈路對比測試。通過對比經過四層負載均衡(tvs)與直接連接 S3 的性能差異,我們發現了一些隱藏的鏈路節點存在較大的延時損耗。特別是在高帶寬場景下,四層負載均衡的性能瓶頸尤為明顯。為了解決這個問題,我們將負載均衡器升級到了采用高性能 DPDK 技術的版本,從而顯著提升了鏈路性能。
元數據方面,我們深入調研了部署方案和元數據引擎的性能邊界。盡管我們對 DCDB 進行了多項優化,如事務處理等,但由于其基于 Raft 協議,存在多次 RPC 請求的問題,即使經過極致優化,也只能達到毫秒級響應。對于跨中心集群部署而言,網絡消耗和成本也是不可忽視的問題。因此,我們最終確定了將流量閉環在單個中心的方案,并選擇了 Redis 作為元數據引擎,以確保元數據操作在 1 毫秒以內完成。
此外,通過將原始文件系統按照頂層目錄拆分為多個子文件系統,我們成功地將整體流量分散到各個JuiceFS服務中。在進行了上述優化后,我們已經能夠滿足業務的性能要求。
我們還深入業務邏輯,協助業務方優化了代碼流程,進一步降低了請求量,為系統創造了更多的性能提升空間。
低版本 FUSE 緩存同步 bug
這個bug的現象是當我們在一臺機器上刪除目錄后再創建同名目錄時,其他機器上看到的卻是之前刪除的目錄內容。經過深入分析,我們發現這是 Linux 內核 2.6 版本中的一個已知 bug。該 bug 導致在刪除目錄后,Linux 的目錄項緩存和 inode 緩存未能及時更新,從而在其他機器上產生了錯誤的目錄視圖。幸運的是,在 Linux 內核 3.10 版本及更高版本中,這個 bug 已經被修復。因此,我們建議大家在使用 JuiceFS 時,盡量使用 3.10 版本及以上的 Linux 內核,以避免類似的性能問題和 bug。
寫入阻塞問題的排查與解決方案
在進行寫入壓測過程中發現偶爾應用寫入會卡住,一段時間后應用寫入報錯 input/output error,查看掛載進程日志是訪問 S3 403 鑒權失敗
首先,由于問題偶發,我們可以排除密鑰錯誤的可能性。通過直連 S3 測試,我們確認問題與中間鏈路節點有關,特別是與 Nginx 的交互存在問題。
為了深入了解問題根源,我們在 S3 端增加了日志記錄,并發現 S3 在進行簽名時使用了“Expect: 100-continue”請求頭。這里需要解釋一下,HTTP 協議中,“Expect: 100-continue”是一種機制,用于在上傳大文件時,先發送 HTTP 請求頭給服務器,服務器若接受,則返回 100 狀態碼,客戶端再發送HTTP請求體。然而,在我們的案例中,打印日志發現 403 的請求不帶有 Expect 頭,但 S3 簽名串用到了 “Expect:100-continue” 這引發了我們的進一步調查。
通過排查 JuiceFS 代碼及 S3 SDK 發現是 S3 SDK 處理重試時,如果上傳請求體大于 2M 時,會默認加上 “Expect:100-Continue”。然而,Nginx 在處理此類請求時,雖然遵循 HTTP 規范返回了 100 狀態碼,但在后續向 S3 轉發請求時,卻未保留該頭部,S3 在收到請求后校驗簽名,由于沒有收到 Expect 請求頭,會使用默認的 “Expect:100-continue”,最終由于 Expect 請求頭的值大小寫不一致,導致簽名校驗失敗,導致 juicefs mount 進程會重試阻塞。
針對此問題,我們提出了兩種修復方案:
- 一是將 SDK 中的“Expect: 100-Continue”頭部修改為規范的小寫形式;
- 二是為 Nginx 添加一個不啟用“Expect: 100-continue”的選項,這個選項是很必要的,可以減少一次網絡交互。
這兩種方案均能有效解決問題,我們向 S3 SDK 社區 JuiceFS 社區提交了 pr。
其他 Tips
文件權限
經常會出現在一批機器中,存在用戶名相同但 uid 不相同的情況。針對這種情況,建議如果要做隔離,就在掛載時做好隔離,比如使用掛載子目錄的方式來做隔離。同時,文件寫入方負責文件權限的正確分配,確保其他文件使用方能有正確的權限。
元數據備份
當元數據數量龐大時,可能會遇到備份失敗的情況。如果你的元數據引擎已經具備副本機制或者你已經實施了定時備份策略,那么可以考慮關閉元數據備份功能,以節省資源。
k8s-CSI
在使用 k8s-CSI 時,開可以選擇禁用 format 選項。具體操作是,給 k8s-CSI只提供name和metaUrl兩個參數即可。這樣一來,在 k8s-CSI 運行過程中,就不會實際執行 format過程。這一做法能夠帶來兩大好處:
首先,它能夠保護我們的安全信息,如 S3 密鑰等敏感數據不被泄露。由于format 過程中可能涉及文件系統的關鍵信息,禁用該功能能夠減少信息暴露的風險。
其次,它允許存儲提供方來管理文件系統的配置。在將JuiceFS交付給容器云平臺之前,我們已經完成了文件系統的配置工作。這樣一來,容器云平臺就可以直接使用已經配置好的文件系統,無需再進行額外的配置或調整。
05 未來展望
分布式元數據引擎
我們注意到當前在某些場景中,對元數據性能的要求較高。針對這些場景,我們可能會考慮使用 Redis。然而,目前的 Redis 存在容量瓶頸問題,因為它為了保證事務的一致性,只使用了集群中的一個節點進行數據存儲,無論集群中部署了多少個節點,實際上只有一個節點在運行,這導致了容量不能水平擴展。
此外,Redis 在運維方面也存在不便之處。因此,我們部門內部正在開發一個分布式的 KV 存儲系統。在系統的調研階段,我們已經與相關部門進行了多輪的溝通。
分布式緩存
通過引入分布式緩存,我們可以更有效地處理大數據場景下的數據存儲和訪問需求,進一步提升系統的整體性能和穩定性。
希望這篇內容能夠對你有一些幫助,如果有其他疑問歡迎加入?JuiceFS 社區與大家共同交流。