ceph之crush演算法樣本

來源:互聯網
上載者:User

標籤:

[email protected]:~# ceph osd tree

# id    weight  type name       up/down reweight

-1      0.05997 root default

-2      0.02998         host osd0

1       0.009995                        osd.1   up      1

2       0.009995                        osd.2   up      1

3       0.009995                        osd.3   up      1

-3      0.02998         host osd1

5       0.009995                        osd.5   up      1

6       0.009995                        osd.6   up      1

7       0.009995                        osd.7   up      1

儲存節點

在進一步之前,先考慮這點:Ceph是分布式儲存系統,不管其“分布式邏輯”的細節,資料最終是要儲存到裝置上。

此時有兩種選擇:直接操作裝置,或者由本地檔案系統代理。前者表示直接面對硬碟,怎麼在硬碟上組織資料都是自己做。後者表示不直接面對硬碟,而是使用已有的檔案系統。Ceph使用第二種方式,可選的本地檔案系統有ext4、btrfs和xfs等,我們使用ext4.

 

crush演算法

設想你是使用者,你有一部動作片要存放到Ceph叢集中。你拿著你的筆記本來到機房,你所看到的Ceph叢集是數個機架的伺服器。你在想,我的動作片最終是儲存在哪裡?

你考慮的實際是資料定位的問題。

常見有兩種資料定位方法:

  1. 記錄。將“資料A:位置location(A)”這樣的資訊記錄下來,訪問資料時查詢記錄,擷取位置,再進行讀取。
  2. 計算。存放和資料A時,其儲存位置location(A)是即時計算得到的。能感覺到這種方式更為便利。

常見的計算方式是一致性雜湊(consistent hashing),GlusterFS使用的是這種方式,基本思路是,面對資料A,以資料A的檔案名稱等類似資訊為key,通過一致性雜湊計算consist_hash(keyA) = location(A)得到儲存位置。

ceph使用的是crush演算法:Controlled, Scalable, Decentralized Placement of Replicated Data。crush是ceph核心之一,本文也將重點描述。

 

簡單來說crush也利用雜湊來計算位置,只不過它更多地利用了叢集的結構資訊。下面通過執行個體來嘗試理解。

 

前文提到rbd、cephfs和rgw這三種使用情境,它們都基於RADOS層。RADOS層對外提供有librados介面,據此可以實現自己的工具。ceph預設提供一個程式rados,通過rados可以直接上傳一個object到ceph叢集。

mon0# rados put bigfile bigfile.data -p rbd // 將資料bigfile.data上傳為object bigfile

mon0# rados ls -p rbd

bigfile

bigfile的儲存位置是通過CRUSH計算得來,ceph提供命令可查詢一個object的位置。

mon0# ceph osd map rbd bigfile

osdmap e67 pool ‘rbd‘ (1) object ‘bigfile‘ -> pg 1.a342bdeb (1.6b) -> up ([6,3], p6) acting ([6,3], p6)

[6,3]表示bigfile這個object儲存在osd.6和osd.3上(ceph叢集部署參考前文),並且放置在pg 1.6b下,也即放在目錄1.6b下。


驗證這一點:

osd0# ls /var/lib/ceph/osd/ceph-6/current/1.6b_head/ -lh

total 61M

-rw-r--r-- 1 root root 61M 10月 29 08:12 bigfile__head_A342BDEB__1 // 感受下object的命名方式

pg (placement group)

上例中提到pg,這是ceph crush資料對應的一個中介層。

 

討論pg需要提及pool,pool是ceph的一個邏輯概念,使用者可在ceph叢集上建立數個pool,設定不同的屬性,然後根據需求,將不同的資料放在不同的pool中。比如我有兩種資料,一種只需要儲存2個備份,另一種更重要,我需要儲存3個備份,從而可以建立兩個pool,設定size=2和size=3。

我的實驗環境中只有一個pool,命名為rbd(這個命名是隨意的,不要和rbd塊裝置使用情境混淆),其備份數目是2份:

mon0# ceph osd dump | grep pool

pool 1 ‘rbd‘ replicated size 2 min_size 2 crush_ruleset 0 object_hash rjenkins pg_num 128 pgp_num 128 last_change 48 flags hashpspool stripe_width 0

 

 

ceph在pool的概念下提供placement group的概念,並通過參數pg_num指定placement group的數目。從上面的輸出可知我的rbd pool下有128個pg。

pg實際對應目錄,rbd pool下有128個pg,意即rbd pool設定為有128個目錄來存放資料。之所以增加pg層來管理資料,是為了資料管理的便利,也減少了中繼資料資訊量。 

 

rbd pool128個pg,每個pg放置在哪些osd上,這也是計算確定的,計算過程是crush演算法的一部分。128個pg整體分散儲存在所有osd上,因此在某個osd上,我們不會看到所有128個目錄。

1.11_head  1.1f_head  1.27_head  1.33_head  1.39_head  1.42_head  1.4c_head  1.54_head  1.60_head  1.66_head  1.74_head  1.79_head  1.7e_head  1.8_head  1.b_head  1.f_head       nosnap

1.15_head  1.21_head  1.2e_head  1.36_head  1.3_head   1.44_head  1.4f_head  1.56_head  1.61_head  1.6a_head  1.75_head  1.7b_head  1.7f_head  1.9_head  1.d_head  commit_op_seq  omap

1.1b_head  1.26_head  1.2f_head  1.37_head  1.41_head  1.45_head  1.50_head  1.5a_head  1.63_head  1.73_head  1.78_head  1.7c_head  1.7_head   1.a_head  1.e_head  meta

說明:pg的命名方式是${pool_id}.${pg.id}_${snap},pgid是16進位值,如1.a_head表示128個pg中的第10個pg。

因此crush資料定位演算法的大致邏輯是:

  • step 1: 輸入object id,計算得到它應該被放置在哪個pg下,得到pgid。
  • step 2: 輸入pgid,計算pg位於哪些osd上。
  • step 3: 訪問object.
crush計算樣本

我們想在這個理解基礎上,來瞭解更多代碼細節,考慮到ceph貌似沒有提供訪問crush演算法的介面,我們在ceph0.86的代碼上做些修改,讓librados提供ceph叢集的layout資訊,同時將crush代碼拎出來做成libcrush,然後利用修改過的librados和libcrush寫程式驗證crush演算法的步驟。

  • libcrush代碼以及對ceph0.86的改動見:https://github.com/xanpeng/libcrush
  • crush演算法步驟驗證代碼見crush-tester.cc:https://gist.github.com/xanpeng/a41a25b5810cb2c8852c#file-crush-tester-cc

 crush-tester.cc main()展示了資料定位的步驟,其邏輯等同於"ceph osd map"的邏輯。需注意:代碼是強依賴於我們前文部署的ceph環境的。

int main(int argc, char **argv) { assert(argc == 2); string objname = argv[argc-1]; // 假設objname=bigfile,這一步是通過objname計算得到一個數值,和pg其實沒有關係,但官方代碼就是這麼命名的,我們這裡也做類似命名 // bigfile->some_value,這裡利用的演算法是ceph_str_hash_rjenkins,演算法這裡不細究,其實也無必要細究。 // 其作用是將不同的objname映射層不同的數值,相信其特點是得到的數值衝突率很低。 pg_t pg = object_to_pg(object_t(objname), object_locator_t(g_pool_id)); printf("objectr_to_pg: %s -> %x\n", objname.c_str(), pg.seed); // bigfile得到的值是a342bdeb,作為這一步的輸入。 // 這一步的作用是將這個數值,也即將object bigfile映射到具體的pg。 // 用到的方法其實是模數,根據pg_num做模數計算,對應函數是crush_stable_mod(a342bdeb, 128, 127)=6b。 pg_t mpg = raw_pg_to_pg(pg); printf("raw_pg_to_pg: %x\n", mpg.seed); // 這一步是crush演算法的核心步驟, // 輸入是pgid=6b,以及ceph叢集的layout,也就是crushmap, // 輸出是6b的儲存位置是哪兩個osd(因為備份數設定size=2), // crush演算法的過程在libcrush中,注意這樣連結本程式"-lcrush -lrados",從而使用libcrush而不是librados中的crush代碼,這樣我們通過介入libcrush去理解crush演算法。 // crush演算法計算6b的位置的過程見下文。 printf("pg_to_osds:\n"); vector<int> up; pg_to_up_acting_osds(mpg, &up); } 

# g++ crush_tester.cc -o test_crush -lcrush -lrados --std=c++11 -g -O0

# ./test_crush bigfile

objectr_to_pg: bigfile -> a342bdeb

raw_pg_to_pg: 6b

pg_to_osds:

 osd_weight: 0,65536,65536,65536,0,65536,65536,65536,  // 這一輸出我覺得奇怪,感覺是錯誤的,但偏偏最後crush映射的結果是正確的

 ruleno: 0

 placement_ps: 1739805228

 

// 這是crush演算法計算6b位置的輸出,注意計算依據之一是我們實驗環境的crushmap,

// 有數種方法可以從一個live ceph cluster中得到當前的crushmap,我們環境的crushmap見:https://gist.github.com/xanpeng/a41a25b5810cb2c8852c#file-ceph-env-txt

// 通過crushmap可以知道,從crush演算法的視角,我們的環境是這樣分層的:

// 第一層:root

// id=-1,alg=straw,hash=rjenkins1,包含兩個item [osd0, osd1]

// 第二層:host

// host osd0: id=-2, alg=straw, hash=rjenkins1,包含三個item [osd.1, osd.2, osd.3]

// host osd1: id=-3, alg=straw, hash=rjenkins1,包含三個item [osd.5, osd.6, osd.7],之所以不是4,5,6,表示我操作過程中出過點點紕漏,但無礙。

// 第三層:osd

// 共6個osd,id是[1,2,3,5,6,7],每個osd有個權重,通過設定osd的權重,影響資料是否存放在當前osd的偏好。

 

---start crush_do_rule---

CHOOSE_LEAF bucket -1 x 1739805228 outpos 0 numrep 2 tries 51 recurse_tries 1 local_retries 0 local_fallback_retries 0 parent_r 0

 

// 演算法第一步,從第一層開始,擷取第一個資料備份的存放位置,判斷我該進入第二層的哪個host。

// 由於第一層設定的alg=straw,表示用straw演算法去選取第二層的host,straw演算法又利用到rjenkins1雜湊演算法

// 得到第一個備份存放位置是item -3上,即osd1上

crush_bucket_choose -1 x=1739805228 r=0

 item -3 type 1

 

// 演算法第二步,在osd1下的第三層中選一個osd,

// 使用的演算法是osd1這個bucket設定的straw+rjenkins1,

// 這是crush演算法過程中的第二次雜湊。我們的實驗環境簡單,layout只有三層,在層次更多的環境中,crush需要做更多次數的雜湊。

// 得到第一個備份存放位置是osd1上的osd.6

CHOOSE bucket -3 x 1739805228 outpos 0 numrep 1 tries 1 recurse_tries 0 local_retries 0 local_fallback_retries 0 parent_r 0

crush_bucket_choose -3 x=1739805228 r=0

 item 6 type 0

CHOOSE got 6

CHOOSE returns 1

CHOOSE got -3

 

// 演算法第三步,計算第二個資料備份的位置,求得位於osd0

crush_bucket_choose -1 x=1739805228 r=1

 item -2 type 1

 

// 演算法第四步,同理進而求得第二個備份的位置:osd0上的osd.3

CHOOSE bucket -2 x 1739805228 outpos 1 numrep 2 tries 1 recurse_tries 0 local_retries 0 local_fallback_retries 0 parent_r 0

crush_bucket_choose -2 x=1739805228 r=1

 item 3 type 0

CHOOSE got 3

CHOOSE returns 2

CHOOSE got -2

CHOOSE returns 2

---finish crush_do_rule---


 numrep: 2, raw_osds: [6,3,]

對比執行多次crush-tester和"ceph osd map",證實上述理解應為正確

ceph之crush演算法樣本

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.