由一次slow-request淺談Ceph scrub原理

01 背景介紹

Ceph是一款開源分佈式存儲系統,其具有豐富的功能和高可靠、高擴展性,並且提供統一存儲服務,既支持塊存儲RBD,也支持對象存儲RadosGW和文件系統CephFS,被廣泛應用在私有云等企業存儲場景。

在京東數科內部,Ceph也被廣泛應用,用來支撐公司基礎存儲需求,並且支撐部分關鍵業務,隨着數據量和集羣規模逐漸擴大,在日常維護中,我們經常遇到各種異常情況,其中頻次較多的就是慢請求slow-request,慢請求會導致性能抖動,直接影響集羣的穩定性,需要謹慎對待。前段時間我們再一次遇到slow-request,問題比較典型,主要和scrub有關,現將該問題的定位過程以及scrub相關的原理優化整理如下,本文內容都是基於Luminous版本。

02 Slow-request問題說明

1.問題分析

某晚19:13左右接到手機slow-request告警:個別請求響應時間比較高,有50s左右,之後登陸上機器查看集羣狀態,見下圖:

圖1 Ceph健康狀態

集羣狀態是OK的,僅發現了有兩個pg正在做deep-scrub(Ceph靜默檢查程序,主要用來檢查pg中對象數據不一致,本文後續章節有詳細介紹),這兩個pg屬於業務數據pool(對象元數據、對象數據、日誌等數據是存儲在不同的pool中的),另外,發現運行scrub的時間段是23:00~06:00。

圖2 Ceph配置文件

報警時間和scrub的運行時間是對得上的,從pg對應的osd日誌上也能確認這一點:

圖3 Ceph osd日誌

可以看到rocksdb正在進行compacting,說明業務寫請求比較多。

所以可確定本次slow-request的原因:大量的用戶寫入操作導致rocksdb進行compacting,加上deep-scrub進一步引發底層IO資源的競爭,最終導致用戶請求超時。

2.問題解決

當時緊急處理方法就是將deep-scrub關掉,後續慢請求就不再出現了。但是deep-scrub直接影響數據的一致性,不能一直關掉,我們優化的思路就是控制deep-scrub的速度和調整其運行時間,本文後續章節會有詳細說明,這裏就不再展開。

03 Ceph scrub簡述

Scrub主要是爲了檢查磁盤數據的靜默錯誤,在英文中被稱爲:Silent Data Corruption,大家都知道硬盤最核心的使命是正確的讀取和寫入數據,在讀、寫失敗的情況下及時拋出異常,但是在某些場景下,寫入成功,讀取的時候才發現數據已經損壞,這就是靜默錯誤,一般靜默錯誤產生原因有這幾種:

  • 硬件錯誤
  • 傳輸過程信噪干擾
  • 軟件bug
  • 固件bug

Ceph的scrub類似於文件系統的 fsck,對於每個 pg,Ceph生成所有對象的列表,並比較每個對象多個副本,以確保沒有對象丟失或數據不一致。Ceph的scrub主要分兩種:

(1)Scrub:對比對象各個副本的元數據來檢查元數據的一致性;

(2)Deep scrub:檢查對象各個副本數據內容是否一致,耗時長,佔用IO資源多;

scrub 對於數據一致性十分重要,但是由上文可知,它會對集羣的性能會帶來一些負面的影響,主要是會和業務IO競爭資源。下面首先分析下scrub的基本原理,然後介紹具體的優化方案。

04 Ceph scrub原理

1.Scrub參數說明

在分析代碼前,首先說明一下Ceph比較重要的概念和一些常用的參數。

以上是Ceph中重要的組件和概念,下面是scrub常用的參數:

下面介紹scrub的詳細流程,scrub是一個生產者消費者模型,生產者生成scrub job,消費者負責消費scrub job。

2.Scrub任務的產生

生產者由定時任務觸發,具體流程如下:

圖4 生產者流程

主要流程說明如下:

(1)首先判斷osd正在執行scrub的pg數是否大於osd_max_scrubs,如果大於則返回;

(2)是否達到pg的預期scrub時間,如果沒達到則返回,預期的scrub時間是由上次scrub的時間、osd_scrub_min_interval、osd_scrub_interval_randomize_ratio參數決定;

(3)判斷當前時間是否大於deadline,如果小於,則判斷是否在osd_scrub_begin_hour和osd_scrub_end_hour,如果處於則判斷集羣負載是否在osd_scrub_load_thredhold之下,如果不滿足則等待時間再重試。如果當前時間大於deadline,則不會判斷時間和負載,強制執行scrub任務,到這一步仍然是osd_scrub_min_interval和osd_scrub_max_interval起作用;

(4)一個scrub任務最後會經過判斷,從而決定這個scrub任務到底是scrub還是deepscrub,接下來我來分析一下這個判斷流程;

(5)在主osd判斷deep scrub的時間有沒有超過deep_scrub_interval,如果超過,這個任務會是deep scrub;

scrubber.time_for_deep =ceph_clock_now() >=
         info.history.last_deep_scrub_stamp+ deep_scrub_interval;

(6)如果沒過期,這時osd_deep_scrub_randomize_ratio這個參數會起作用:

deep_coin_flip = (rand()% 100) < cct->_conf->osd_deep_scrub_randomize_ratio* 100;

scrubber.time_for_deep= (scrubber.time_for_deep || deep_coin_flip);

(7)首先判斷osd正在執行scrub的pg數是否大於osd_max_scrubs,如果大於則返回;

(8)之後就是具體將任務加到隊列,這裏是用統一的數據結構表示scrub和deep scrub任務;

(9)獲取deep scrub和scrub的標誌位,如果設置了no deep scrub或者no scrub,則不執行相應任務。

從代碼中看到的幾個細節:

(1)預期的scrub時間,是由 last_scrub_time + min_interval + random_postpone_time,從而錯開了pg的開始時間,這裏起到了消峯的作用,並且隨着系統的運行,這個時間是會錯開的:

sched_time += scrub_min_interval;
double r = rand() / (double)RAND_MAX;
sched_time += scrub_min_interval *cct->_conf->osd_scrub_interval_randomize_ratio * r;
deadline += scrub_max_interval;

(2)last_scrub_time + osd_scrub_max_interval作爲deadline,所以如果osd_scrub_max_interval設置的不對,就會導致系統在業務的正常時間出現deep scrub和scrub,並且不會受到load thredhold的限制;

(3)osd_deep_scrub_randomize_ratio這個參數會把普通的scrub任務變成deep scrub任務,但是隻要max interval設置的合理,是有均衡deep scrub任務的作用的。

3.Scrub任務的消費

消費者是由線程池控制,具體流程如下:

圖5 消費者流程

(1)由前文可知,scrub是已pg爲單位的,而每個PG的scrub啓動是由該PG所在的主OSD啓動執行;

(2)在比較大的集羣規模下,每個PG中可能承載了幾十萬的對象數,在進行scrub過程中會根據對象名的哈希值的部分作爲提取因子,選擇一部分對象進行校驗,這部分被選中的對象稱爲chunky,這也是爲什麼ceph被稱爲chunkyscrub的原因;

(3)scrub的發起者即pg所在的主OSD,向其它副本OSD發起進行數據校驗的消息,根據scrub的類型不同,需要校驗的數據也不同:

  • scrub 讀取對象的元數據信息,檢查對象是否一致
  • deep scrub 讀取對象的數據並做checksum來檢查數據是否一致

(4)校驗信息統一放到ScrubMap中,發起者通過比較ScrubMap中的信息,判斷對象是否一致,不一致的信息會上報給monitor。

從流程也可以看出幾個細節:

(1)chunky scrub裏面的object會被鎖住,寫請求受到影響;

(2)osd_scrub_sleep是控制兩次chunkyscrub的間隔,從而會拉長一次scrub(這裏包括deepscrub 和 scrub)的時間,睡眠是通過定時器實現的。

05 Ceph scrub優化

瞭解了scrub的原理,下面從如下兩個方面來進行介紹scrub優化方案,一種是調整Ceph的相關參數,一種是自研的scrub調度策略。

1.參數優化

首先,可以解決之前的幾個迷惑的問題:

(1)針對正在執行的scrub任務,即便時間超過配置的osd_scrub_end_hour 後,仍然會執行,新的任務在OSD::sched_scrub()開始時 OSD::scrub_time_permit返回 false不會執行;

(2)如果osd_scrub_max_interval配置的不合理,則會導致scrub任務的deadline超出,那麼就會導致在規定時間外的任意時間出現scrub/deep scrub,從而影響業務IO;

(3)一個scrub任務到底是deep還是普通的scrub,和osd_deep_scrub_interval還有osd_deep_scrub_randomize_ratio參數有關,超過osd_deep_scrub_interval的一定是deep,否則按照osd_deep_scrub_randomize_ratio對應的概率轉換成deep;

(4)不管是deep還是shallow scrub任務,執行的邏輯都是一樣的,函數也一樣,狀態機也一樣,唯一不同的是ScrubMap如果是deep會額外的請求CRC校驗值。

其次,可以總結出參數調優的主要方向:

(1)首先,確定osd_scrub_max_interval,這個時間很重要,如果設置的太小就會導致業務在正常時間IO受到影響,並且此時osd_scrub_load_thredhold、begin hour、end hour都不會生效,osd_scrub_sleep時間生效。通過ceph pg dump --format json | jq -r ‘.pg_stats[] | [.last_clean_scrub_stamp ] | @csv’ | sort –r命令查看線上環境,可以看到較多scrub任務在設置的scrub時間之外執行,所以這個參數需要調整,調整到一個月之後,沒有出現類似現象;

(2)其次,osd_scrub_end_hour的設置,如果業務7點開始使用,那麼如果設置成7點,可能最後一個任務沒有執行完,deep scrub任務會持續到7點之後,具體取決於最後一個pg scrub的執行時間,那麼這個值可以再提前一點;

(3)然後,osd_scrub_load_threshold的設置,這個值默認0.5,假如在begin hour和endhour之間,如果cpu高於這個值,那麼是不會執行scrub的,這個值太小會導致在正常規定時間不能執行scrub,從而影響deadline,一旦超過deadline會出現第一種情況;

(4)bucket index對應的shard對象不宜過大,如果太大,這個對象在執行deep scrub的時候,會影響bucket級別的對象無法寫入。這個參數通過修改osd_scrub_chunk_min,osd_scrub_chunk_max,可以緩解但是如果一個shard對象太多,仍然會比較嚴重;

(5)osd_scrub_sleep參數可以降低客戶端在scrub時間內的感知,代價是增加了一次scrub任務的時間,所以如果修改這個參數,仍然需要確保一個osd_scrub_max_interval週期裏,所有的pg能夠被正確執行完scrub任務。

總結一下,總體的優化思路就是首先確保scrub任務不會在osd_scrub_begin_hour和 osd_scrub_end_hour之外的時間執行,其次就是在osd_scrub_begin_hour和osd_scrub_end_hour之間,儘可能減少業務的感知。

2.調度優化

由上文的分析可知,通過調整參數,可以解決一部分問題,但是如果某些參數設置的不合理,仍然會導致在scrub任務在非規定的時間內運行,影響正常的任務,scrub可控性仍然存在一些問題,並且在618和雙11大促期間,需要完全避免執行scrub任務。針對這種情況,關閉使用ceph osd set noscrub;ceph osd set nodeep-scrub命令關閉了ceph的scrub機制,採用了自研程序進行scrub任務調度。

圖6 scrub調度流程

主要的邏輯說明:

(1)通過rados連接ceph集羣,如果連接失敗則返回對應錯誤信息;

(2)進入循環主流程,判斷當前日期是否是特殊日期,例如618,雙11,如果是則睡眠一定時間繼續循環;

(3)檢查執行時間和最大任務數是否滿足執行條件,如果不滿足,則睡眠等待下一輪檢查,一般都會設置deep scrub時間範圍爲晚上23點到第二天早上7點;

(4)通過pg dump獲取當前正在執行scrub的pg信息;

(5)如果當前執行scrub的任務數大於所設置的maxscrubs,則睡眠一段時間繼續循環

(6)對於非scrubbing狀態的PG,按照last_deep_scrub_stamp從遠及近排序,作爲備選PG組;

(7)循環檢查備選PG,對滿足以下條件的PG執行deep scrub操作:

  • PG的last_deep_scrub_stamp在1周之前;
  • PG的主osd不處於scrubbing狀態;
  • 當前deep scrub任務小於最大任務數;

(8)如果當前deep scrub任務達到最大任務數,跳出循環。

(9)睡眠等待下一輪檢查。

本文轉載自公衆號京東數科技術說(ID:JDDTechTalk)。

原文鏈接

由一次slow-request淺談Ceph scrub原理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章