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






















