【經驗】Ceph對象存儲運維的驚魂72小時
Ceph作為一款開源的分布式存儲軟件,可以利用X86服務器自身的本地存儲資源,創建一個或多個存儲資源池,并基于資源池對用戶提供統一存儲服務,包括塊存儲、對象存儲、文件存儲,滿足企業對存儲高可靠性、高性能、高可擴展性方面的需求,并越來越受到企業的青睞。經過大量的生產實踐證明,Ceph的設計理念先進,功能全面,使用靈活,富有彈性。不過,Ceph的這些優點對企業來說也是一把雙刃劍,駕馭的好可以很好地服務于企業,倘若功力不夠,沒有摸清Ceph的脾性,有時也會制造不小的麻煩,下面我要給大家分享的正是這樣的一個案例。
A公司通過部署Ceph對象存儲集群,對外提供云存儲服務,提供SDK幫助客戶快速實現對圖片、視頻、apk安裝包等非結構化數據的云化管理。在業務正式上線前,曾經對Ceph做過充分的功能測試、異常測試和性能測試。
集群規模不是很大,使用的是社區0.80版本,總共有30臺服務器,每臺服務器配置32GB內存,10塊4T的SATA盤和1塊160G的Intel S3700 SSD盤。300塊SATA盤組成一個數據資源池(缺省配置情況下就是名稱為.rgw.buckets的pool),存放對象數據;30塊SSD盤組成一個元數據資源池(缺省配置情況下就是名稱為.rgw.buckets.index的pool),存放對象元數據。有過Ceph對象存儲運維部署經驗的朋友都知道,這樣的配置也算是國際慣例,因為Ceph對象存儲支持多租戶,多個用戶在往同一個bucket(用戶的一個邏輯空間)中PUT對象的時候,會向bucket索引對象中寫入對象的元數據,由于是共享同一個bucket索引對象,訪問時需要對這個索引對象加鎖,將bucket索引對象存放到高性能盤SSD組成的資源池中,減少每一次索引對象訪問的時間,提升IO性能,提高對象存儲的整體并發量。
系統上線后,客戶數據開始源源不斷地存入到Ceph對象存儲集群,在前面的三個月中,一切運行正常。期間也出現過SATA磁盤故障,依靠Ceph自身的故障檢測、修復機制輕松搞定,運維的兄弟感覺很輕松。進入到5月份,運維兄弟偶爾抱怨說SSD盤對應的OSD有時會變的很慢,導致業務卡頓,遇到這種情況他們簡單有效的辦法就是重新啟動這個OSD,又能恢復正常。大概這種現象零星發生過幾次,運維兄弟詢問是不是我們在SSD的使用上有什么不對。我們分析后覺得SSD盤的應用沒有什么特別的地方,除了將磁盤調度算法修改成deadline,這個已經修改過了,也就沒有太在意這個事情。
5月28日晚上21:30,運維兄弟手機上接到系統告警,少部分文件寫入失敗,馬上登陸系統檢查,發現是因為一臺服務器上的SSD盤對應的OSD讀寫緩慢引起的。按照之前的經驗,此類情況重啟OSD進程后就能恢復正常,毫不猶豫地重新啟動該OSD進程,等待系統恢復正常。但是這一次,SSD的OSD進程啟動過程非常緩慢,并引發同臺服務器上的SATA盤OSD進程卡頓,心跳丟失,一段時間后,又發現其它服務器上開始出現SSD盤OSD進程卡頓緩慢。繼續重啟其它服務器上SSD盤對應的OSD進程,出現了類似情況,這樣反復多次重啟SSD盤OSD進程后,起不來的SSD盤OSD進程越來越多。運維兄弟立即將此情況反饋給技術研發部門,要求火速前往支援。
到辦公室后,根據運維兄弟的反饋,我們登上服務器,試著啟動幾個SSD盤對應的OSD進程,反復觀察比對進程的啟動過程:
1、 用top命令發現這個OSD進程啟動后就開始瘋狂分配內存,高達20GB甚至有時達到30GB;有時因系統內存耗盡,開始使用swap交換分區;有時即使最后進程被成功拉起,但OSD任然占用高達10GB的內存。
2、 查看OSD的日志,發現進入FileJournal::_open階段后就停止了日志輸出。經過很長時間(30分鐘以上)后才輸出進入load_pg階段;進入load_pg階段后,再次經歷漫長的等待,雖然load_pg完成,但進程仍然自殺退出。
3、 在上述漫長啟動過程中,用pstack查看進程調用棧信息,FileJournal::_open階段看到的調用棧是在做OSD日志回放,事務處理中是執行levelDB的記錄刪除;在load_pg階段看到的調用棧信息是在利用levelDB的日志修復levelDB文件。
4、 有時候一個SSD盤OSD進程啟動成功,運行一段時間后會導致另外的SSD盤OSD進程異常死掉。
從這些現象來看,都是跟levelDB有關系,內存大量分配是不是跟這個有關系呢?進一步查看levelDB相關的代碼后發現,在一個事務處理中使用levelDB迭代器,迭代器訪問記錄過程中會不斷分配內存,直到迭代器使用完才會釋放全部內存。從這一點上看,如果迭代器訪問的記錄數非常大,就會在迭代過程中分配大量的內存。根據這一點,我們查看bucket中的對象數,發現有幾個bucket中的對象數量達到了2000萬、3000萬、5000萬,而且這幾個大的bucket索引對象存儲位置剛好就是出現問題的那幾個SSD盤OSD。內存大量消耗的原因應該是找到了,這是一個重大突破,此時已是30日21:00,這兩天已經有用戶開始電話投訴,兄弟們都倍感“鴨梨山大”。已經持續奮戰近48小時,兄弟們眼睛都紅腫了,必須停下來休息,否則會有兄弟倒在黎明前。
31日8:30,兄弟們再次投入戰斗。
還有一個問題,就是有些OSD在經歷漫長啟動過程,最終在load_pg完成后仍然自殺退出。通過走讀ceph代碼,確認是有些線程因長時間沒有被調度(可能是因levelDB的線程長時間占用了CPU導致)而超時自殺所致。在ceph的配置中有一個filestore_op_thread_suicide_timeout參數,通過測試驗證,將這個參數設置成一個很大的值,可以避免這種自殺。又看到了一點點曙光,時鐘指向12:30。
有些進程起來后,仍然會占用高達10GB的內存,這個問題不解決,即使SSD盤OSD拉起來了,同臺服務器上的其它SATA盤OSD運行因內存不足都要受到影響。兄弟們再接再厲啊,這是黎明前的黑暗,一定要挺過去。有人查資料,有人看代碼,終于在14:30從ceph資料文檔查到一個強制釋放內存的命令:ceph tell osd.* heap release,可以在進程啟動后執行此命令釋放OSD進程占用的過多內存。大家都格外興奮,立即測試驗證,果然有效。
一個SSD盤OSD起來后運行一會導致其它SSD盤OSD進程退出,綜合上面的分析定位,這主要是因為發生數據遷移,有數據遷出的OSD,在數據遷出后會刪除相關記錄信息,觸發levelDB刪除對象元數據記錄,一旦遇到一個超大的bucket索引對象,levelDB使用迭代器遍歷對象的元數據記錄,就會導致過度內存消耗,從而導致服務器上的OSD進程異常。
根據上述分析,經過近2個小時的反復討論論證,我們制定了如下應急措施:
1、 給集群設置noout標志,不允許做PG遷移,因為一旦出現PG遷移,有PG遷出的OSD,就會在PG遷出后刪除PG中的對象數據,觸發levelDB刪除對象元數據記錄,遇到PG中有一個超大的bucket索引對象就會因迭代器遍歷元數據記錄而消耗大量內存。
2、 為了能救活SSD對應的OSD,盡快恢復系統,在啟動SSD對應的OSD進程時,附加啟動參數filestore_op_thread_suicide_timeout,設置一個很大的值。由于故障OSD拉起時,LevelDB的一系列處理會搶占CPU,導致線程調度阻塞,在Ceph中有線程死鎖檢測機制,超過這個參數配置的時間線程仍然沒有被調度,就判定為線程死鎖。為了避免因線程死鎖導致將進程自殺,需要設置這個參數。
3、 在目前內存有限的情況下,異常的OSD啟動會使用swap交換分區,為了加快OSD進程啟動,將swap分區調整到SSD盤上。
4、 啟動一個定時任務,定時執行命令ceph tell osd.* heap release,強制釋放OSD占用的內存。
5、 SSD對應的OSD出現問題的時候,按如下步驟處理:
a) 先將該服務器上的所有OSD進程都停掉,以騰出全部內存。
b) 然后啟動OSD進程,并攜帶filestore_op_thread_suicide_timeout參數,給一個很大的值,如72000。
c) 觀察OSD的啟動過程,一旦load_pgs執行完畢,可以立即手動執行ceph tell osd.N heap release命令,將其占用的內存強制釋放。
d) 觀察集群狀態,當所有PG的狀態都恢復正常時,再將其他SATA盤對應的OSD進程啟動起來。
按照上述步驟,我們從17:30開始逐個恢復OSD進程,在恢復過程中,那幾個超大bucket索引對象在做backfilling的時候需要較長時間,在此期間訪問這個bucket的請求都被阻塞,導致應用業務請求出現超時,這也是單bucket存儲大量對象帶來的負面影響。
5月31日23:00,終于恢復了全部OSD進程,從故障到系統全部成功恢復,我們驚心動魄奮戰了72小時,大家相視而笑,興奮過度,再接再厲,一起討論制定徹底解決此問題的方案:
1、 擴大服務器內存到64GB。
2、 對新建bucket,限制存儲對象的最大數量。
3、 Ceph 0.94版本經過充分測試后,升級到0.94版本,解決單bucket索引對象過大問題。
4、 優化Ceph對levelDB迭代器的使用,在一個大的事務中,通過分段迭代,一個迭代器在完成一定數量的記錄遍歷后,記錄其當前迭代位置,將其釋放,再重新創建一個新的迭代器,從上次迭代的位置開始繼續遍歷,如此可以控制迭代器的內存使用量。
前事不忘后事之師,汲取經驗教訓,我們總結如下幾點:
1、 系統上線前必須經過充分的測試
A公司的系統上線前,雖然對ceph做了充分的功能、性能、異常測試,但卻沒有大量數據的壓力測試,如果之前單bucket灌入了幾千萬對象測試,也許就能提前發現這個隱患。
2、 運維過程中的每一個異常都要及時引起重視
此案例中,在問題爆發前一段時間,運維部門已經有反饋SSD異常的問題,可惜沒有引起我們重視,倘若當時就深入分析,也許可以找到問題根由,提前制定規避措施。
3、 摸清ceph的脾性
任何軟件產品都有相應的規格限制,ceph也不例外。如果能提前深入了解ceph架構及其實現原理,了解單bucket過度存放大量對象所帶來的負面影響,提前規劃,也不會出現本案例中遇到的問題。RGW對配額的支持非常全面,包括用戶級別的、bucket級別的,都可以配置單個bucket允許存放的最大對象數量。
4、 時刻跟蹤社區最新進展
在Ceph的0.94版本中,已經支持了bucket索引對象的shard功能,一個bucket索引對象可以分成多個shard對象存儲,可有效緩解單bucket索引對象過大問題。