GlusterFS探究(一): dht,afr,fuse, mgmt 層 幾個問題總結
0, 跟蹤學習 在dht層實現 的 rebalance 操作
a, 在目錄上記錄 layout範圍的結果,
i, mkdir (只在mkdir時刻) 會 有一個 layout物件,除了 start_off 和 stop_off 其他值都可以填充;
而其他值的填充 必須繼續往下走。
ii, dht_layout 讀寫 disk ? ? 客戶端第一次 讀時 會恢復到mem??? (where?)
iii,目錄是 無 拓撲關係; /dir1/dir2 是兩個目錄, 分別為 /dir1 和 /dir1/dir2兩個;
b, dht_layout_search (用 (path + filename)來計算,並在上級 目錄的 ctx裡 尋找 對應的訪問,然後 確定 哪一個brick通訊);
c, dht_subvol_get_cached /dht_subvol_get_hashed (cached 指最早加入的 brick 對應的client端!!)
d, 可能是因為brick裡資料量太少,導致rebalance操作一直沒發生 拷貝資料的現象,所以先暫停一下。
1,跟蹤學習 replace-brick 操作的實現
比如 命令
gluster volume replace-brick test 172.16.30.185:/sam6 172.16.30.185:/sam7 start force
也就是用本機的 /sam7 代替 /sam6。
a, 當glusterd 程序 處理這個命令時,會修改 /sam6對應brick程序的配置檔案,在 features/index 新增一個子xlator , 就是
cluster/pump -> protocol/client, 充當拷貝資料的客戶端。(設為程序A )
另外, 會生成一個 以protocol/server ->features/locks->storage/posix 為配置檔案,生成一個 新glusterfs程序,
作為實現replace-brick功能的臨時程序。(設為程序B )
b, 實際的拷貝資料 是 這兩個 程序 在socket.c 中 __socket_rwv函式中實現,
A 程序 會開啟在原來 brick 上的 老檔案,並讀取,再寫到 對B 的套接字裡。
B 程序 不斷接受從A來的資料,並且在新的 brick中建立同樣的檔案。
c, 具體實現 實在 程序B,會從 老brick上獲取每個檔案gfid, 並在新brick的 /sam7中查詢,因為查詢不到,
就會導致 cluster/afr層的self-heal
至於 cluster/afr 層 具體的 self-heal 的過程還得再跟蹤下日誌和程式碼。
d, replace-brick操作 在gdb跟程序時,只要有耽誤,資料就不會再被拷貝,重啟服務也不行,只能清理環境,重新再來一遍。
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx
1, cluster/afr 層 selfheal 過程實現
比如 命令
gluster volume replace-brick test 172.16.30.185:/sam6 172.16.30.185:/sam7 start force
老brick 對應程序設為 A , 新 的臨時 rb程序設為B。
a, 過程可能是:
程序B從程序A獲取在A上所有 檔案和目錄的gfid, 然後將 /sam7看作是一個與/sam6互為映象的brick。而此時它剛剛online, 在上面執行lookup.
而 cluster/afr 實現機制,發現 /sam7上 任何一個檔案有問題,就會從 /sam6上 拷貝一個檔案 覆蓋替換掉 /sam6上同名檔案。
(大致過程應該是這樣的,而是 還得有更仔細的分析日誌的驗證。 想法可以有,但是驗證需要不少時間。 )
b, 從呼叫 afr_launch_self_heal 函式進入 selfheal過程。(應該是features/index 層呼叫這個函式)
流程大概是: 1,從副本中找到一個能用的 brick對應的xlator;
2, 修復 父親目錄;
3, 修復 entry
4, 修復 metadata
5, 修復 data
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1, dht 層 每個目錄的 layout資訊 是否 儲存 到叢集的對應目錄擴充套件屬性上
因為掛載的客戶端畢竟是多變的,而每個目錄的layout資訊顯然 非常重要。
分析了一下程式碼, 每個目錄的inode的 inode_ctx 中儲存的是layout資料,對它的操作也只是在記憶體中,並沒有跟對應brick發起通訊。
這個是否可靠?還是因為對應的程式碼沒找到?
實驗:清空所有日誌,在 /mnt 下建立一個目錄,來分析整個過程。
分析了storage/posix程式碼和日誌,發現 伺服器端實際上只是在對應目錄的擴充套件屬性裡記錄了gfid, (按照設計,這個gfid 包含了 根目錄的路徑字首)。並 沒有記錄 layout資訊。
結論: 每個目錄layout資訊確實只在客戶端記憶體裡,與伺服器端無關。
layout資訊中快取的是last 在目錄下動作(如本身建立,目錄下建立檔案等,不包括建立子目錄)以來,在本卷中各個brick的可用狀態。 如果出現某個brick DOWN情況,這樣這個目錄的layout資訊就是有問題的,它會臨時把 新檔案建立到 臨時的brick上。但是過一段時間,會進行 self-heal,即 在 brick 全UP 情況下 應該在的brick上建立連結檔案。以待以後來rebalance .
2, 修改了日後實現 ()
a, 每條日誌頭上增加一個序列號,方便分清楚次序。
b, 對最核心的xlator_fops中 各個函式,建立了 fopslog 日誌,只記錄 它的名字和序列號。
c, (將做 )按照 各個 程序 建立目錄,所有的日誌落在這個目錄下。
3,iatt gfid stat 資訊
iatt 資訊是 glusterfs自己定義的 檔案屬性資訊的結構,它主要是posix的 stat結構(時間增加了微妙部分),和 gfid.
gfid 是 整個叢集裡的 inode-number, 唯一標誌了一個檔案和目錄。
在posix層 最重要的是 通過系統呼叫 fstat(取stat) 和 lgetxattr (取gfid), 來構造 一個 iatt 或者將新的iatt落盤(實際上只有gfid).
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1, Gluster 使用 fuse 實現掛載功能
a, { ls -l } => kernel => fuse-k => fuse-u => glfs-graph
fuse-u實現 命令列的功能, 即來自fuse-k解析的請求. (這一切 在 fuse-loop大迴圈中完成, 其監聽fd,處理請求)
執行緒 B: 死迴圈中,平時一直監聽/dev/fuse 的fd, 有task時被喚醒,走graph流程來處理請求。(A, A-cbk 阻塞在哪裡?)
執行緒 A: 解析請求,並且 新增 task, 並且 最終把資料按照 fuse spec 格式 寫到 /dev/fuse 開啟的fd 中去。
Gluster裡fuse相關程式碼是 fuse-u 相關實現部分,也就是,當標準命令列處理gluster裡面的資料時的實現。
由於客戶端只會啟動一個程序,而且在掛載點操作很方便,這樣就方便了復現和除錯 。
另外,標準命令列實現是由coreutils庫來實現的。通常每個命令都是依次呼叫幾個系統呼叫函式,將取得的結果返回給客戶。
使用GDB 跟掛載的程序,能很輕鬆的跟蹤到 每個命令實現時,實際上呼叫了那些的posix標準函式。
b, fuse: fuse_attr
glfs: iatt
posix: stat
這三個結構是三個系統中定義的元資料相關的核心資料結構。非常核心的功能實現時將這些資料結構互相轉換。
c, 實現掛載功能時,fuse成為一個xlator, 並且在被畫在客戶端graph上。
在 客戶端程序的核心結構上, 一般是 fuse xlator作為功能棧的 連結串列頭節點。
當阻塞在 readv系統呼叫上的 fuse xlator 執行緒B 接受到核心資料後,按照 fuse 標準格式 會解析 OPCODE,它們是posix標準的介面
操作碼。然後 會驗證資料,在有必要時 會執行 STACK_WIND,將呼叫請求按照功能棧向下面呼叫,最終是Gluster來處理的請求。
還需解決問題, i, 掛載時埠對映問題; ii, syncop 做的什麼。iii,fuse 層快取資料了麼? iiii, inode 是 獨立的一份資料,而每個
xlator只是 借用了 唯一的 inode地址來 ctx ?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1, gd_svc_cli_actors 這個 是在哪裡起作用的?
這是個struct陣列。而這個struct有個成員為函式,在這個函式中會最終呼叫glusterd_op_begin_synctask
(裡面實作是gd_sync_task_begin)
而這個整體 和 gd_svc_peer_prog, gd_svc_cli_prog, gd_svc_mgmt_prog,
gluster_pmap_prog, gluster_handshake_prog, glusterd_mgmt_hndsk_prog,
這些個整體被 glusterd_program_register 載入到 glusterd 地址空間中來執行。
2, 這個流程在 gd_sync_task_begin 函式中確定好了。
a, glusterd_lock
b, glusterd_op_set_op
c, gd_build_peers_list
d, gd_lock_op_phase
e, glusterd_op_build_payload
//往glusterd程序的一個成員dict添加了一個值,下面流程會用到。
//哪個dict ?? 棧上構造的臨時空容器,會按後面的需要採集glusterd維護的資料資訊。
f,gd_stage_op_phase
g, gd_brick_op_phase
h, gd_commit_op_phase
這些流程中 的每一般 對會按照 op 分別呼叫不同的函式。在處理結束後,會更新 glusterd儲存的元資料資訊。
3,一個dict指標打天下 不斷處理會更新其中某些 key 對應的value.
任何操作都會需要一些glusterd程序快取的資訊,dict會事先採集這些資訊,而這個本身也充當對 操作的validate。
4, 返回給命令列的結果 是在rpc-xdr來實現,按照xdr標準格式解析請求,再將結果按標準格式返回給gluster命令列程序。
最好能夠 多 用GDB跟蹤一下glusterd程序,熟悉很多的介面。