1 PG介紹
pg的全稱是placement group,中文譯為放置組,是用於放置object的一個載體,pg的創建是在創建ceph存儲池的時候指定的,同時跟指定的副本數也有關系,比如是3副本的則會有3個相同的pg存在於3個不同的osd上,pg其實在osd的存在形式就是一個目錄,可以列出來看下:
[root@abc ~]# ll /var/lib/ceph/osd/ceph-2/current/ total 332 drwxr-xr-x 2 root root 32 Sep 19 15:27 1.11_head drwxr-xr-x 2 root root 98 Sep 21 15:12 1.14_head drwxr-xr-x 2 root root 83 Sep 21 14:12 1.1f_head drwxr-xr-x 2 root root 98 Sep 21 18:43 1.24_head drwxr-xr-x 2 root root 6 Sep 21 18:43 1.24_TEMP drwxr-xr-x 2 root root 32 Sep 19 15:27 1.25_head drwxr-xr-x 2 root root 164 Sep 21 15:14 1.2d_head drwxr-xr-x 2 root root 32 Sep 19 15:27 1.2_head drwxr-xr-x 2 root root 98 Sep 21 15:15 1.34_head
這里只列出來一部分,可以看到有1.24這樣開頭的,這其實就是pg的id,前面的1表示這個pg是對應哪個存儲池的,存儲池id可以通過ceph df看到:
[root@u131Oi ~]# ceph df GLOBAL: SIZE AVAIL RAW USED %RAW USED 10704G 9808G 896G 8.37 POOLS: NAME ID USED %USED MAX AVAIL OBJECTS volumes 1 116M 0 3059G 46 images 2 29218M 0.92 3059G 3669 backups 3 0 0 3059G 0 vms 4 269G 8.09 3059G 103678 snapshots 5 0 0 3059G 0 gnocchi 6 394M 0.01 3059G 23310
說明該pg是屬於volumes存儲池的,后面的24是用於區分同個池中的不同的pg的,兩者結合起來就作為pg的id了,crush map通過pgid可以計算出該pg是分布在哪組osd上的,可以通過如下命令查看pg分布於哪些osd上:
[root@abc ~]# ceph pg map 1.24 osdmap e1008 pg 1.24 (1.24) -> up [0,2,8] acting [0,2,8]
可以看到該pg的3副本分別是分布在osd.0,osd.2和osd.8上的,其中最前面的0表示主副本存在於osd.0上。
2 PG在ceph中的作用
從上面可以看到所有數據其實都是抽象成多個object,每個object都會對應到唯一的一個pg上(多副本表示有多個相同的pg,當然object也自然是多副本的),然后pg映射到osd上存儲,所以pg可以說是ceph的核心概念了,那為什么要引進pg這個概念呢?
這是因為如果要追蹤的目標如果是object,那么要追蹤的數量就太多了,這樣可能會加大復雜性,而且也會帶來不小的開銷,於是引進pg這個概念,把object裝進pg中,以pg為存儲單元個體,直接追蹤pg狀態,一般pg數量是遠遠小於object數量的。
3 PG數量計算方法
官方給出的計算公式是這樣的:
Total PGs = (Total_number_of_OSD * 100) / max_replication_count
但每個池中pg數量最好接近或等於2的次方
例:
有100個osd,2副本,5個pool
Total PGs =100*100/2=5000
每個pool 的PG=5000/5=1000,那么創建pool的時候就指定pg為1024
ceph osd pool create pool_name 1024
下面給出我在程序中寫的pg計算函數(僅供參考):
def pg_calc(osd_count, pool_count, rep_size): osd_count = int(osd_count) pool_count = int(pool_count) rep_size = int(rep_size) pg_num = 512 if rep_size == 2: if osd_count > 0 and osd_count < 5: pg_num = 128 elif osd_count >= 5 and osd_count <= 10: pg_num = 512 else: pg_num = int((osd_count * 100) / (rep_size)) else: pg_num = int((osd_count * 100) / (rep_size)) per_pool_pg = pg_num / pool_count for i in range(0, 21): tmp = 2 ** i if tmp >= per_pool_pg: pg_num = tmp break return pg_num
4 PG的狀態有哪些
從第2點我們知道由於pg的引進,我們只要追蹤pg的狀態即可,因此pg在集群中是存在多種狀態的,pg的狀態也決定着當前集群數據的健康狀態。
(1)Active:當前擁有最新狀態數據的pg正在工作中,能正常處理來自客戶端的讀寫請求。
(2)inactive:正在等待具有最新數據的OSD出現,即當前具有最新數據的pg不在工作中,不能正常處理來自客戶端的讀寫請求。
(3)Clean:PG所包含的object達到指定的副本數量,即object副本數量正常。
(4)Unclean:PG所包含的object沒有達到指定的副本數量,比如一個PG沒在工作,另一個PG在工作,object沒有復制到另外一個PG中。
(5)Peering:PG所在的OSD對PG中的對象的狀態達成一個共識(維持對象一致性)。
(6)Degraded:主osd沒有收到副osd的寫完成應答,比如某個osd處於down狀態。
(7)Stale:主osd未在規定時間內向mon報告其pg狀態,或者其它osd向mon報告該主osd無法通信。
(8)Inconsistent:PG中存在某些對象的各個副本的數據不一致,原因可能是數據被修改。
(9)Repair:pg在scrub過程中發現某些對象不一致,嘗試自動修復。
(10)Undersized:pg的副本數少於pg所在池所指定的副本數量,一般是由於osd down的緣故。
(11)Scrubbing(deep + Scrubbing):pg對對象的一致性進行掃描。
(12)Recovering:pg間peering完成后,對pg中不一致的對象執行同步或修復,一般是osd down了或新加入了osd。
(13)Backfilling:一般是當新的osd加入或移除掉了某個osd后,pg進行遷移或進行全量同步。
(14)down:包含必備數據的副本掛了,pg此時處理離線狀態,不能正常處理來自客戶端的讀寫請求。
5 PG修復
5.1 通常查看集群是否正常都是通過ceph -s命令查看,如果顯示是HEALTH_OK則表示集群是健康的,一切運行正常,比如:
[root@ctl0 ~]# ceph -s cluster ce0d75a9-9d6a-4b8e-ab4c-c3645f69506a health HEALTH_OK monmap e3: 3 mons at {ctl0=10.16.30.16:6789/0,ctl1=10.16.30.14:6789/0,ctl2=10.16.30.15:6789/0} election epoch 20, quorum 0,1,2 ctl1,ctl2,ctl0 osdmap e39: 3 osds: 3 up, 3 in pgmap v58: 192 pgs, 6 pools, 0 bytes data, 0 objects 102 MB used, 98153 MB / 98255 MB avail 192 active+clean
但有時也會遇見ceph -s顯示的是HEALTH_WARN,如下所示:
[root@ctl0 ~]# ceph -s cluster ce0d75a9-9d6a-4b8e-ab4c-c3645f69506a health HEALTH_WARN 135 pgs degraded 135 pgs stuck unclean 135 pgs undersized 1/3 in osds are down monmap e3: 3 mons at {ctl0=10.16.30.16:6789/0,ctl1=10.16.30.14:6789/0,ctl2=10.16.30.15:6789/0} election epoch 20, quorum 0,1,2 ctl1,ctl2,ctl0 osdmap e41: 3 osds: 2 up, 3 in pgmap v64: 192 pgs, 6 pools, 0 bytes data, 0 objects 103 MB used, 98152 MB / 98255 MB avail 135 active+undersized+degraded 57 active+clean
可以看到很多pg的狀態是不健康的了,但從中其實也可以發現有一個osd掛了,其實只要把該osd重新拉起來就好了,拉起來后集群會自動重新恢復健康狀態。但是也有可能出現這個osd再也起不來了,比如硬盤損壞了,這時多副本就發揮作用了,因為還有其它副本在其它osd上,這時我們可以通過均衡數據的方法來將集群恢復並將該osd踢出集群。
這里我們以osd.2為例子。
均衡數據恢復步驟:
# 先將該osd reweight 到0,也就是將權重降低到0,讓數據副本分散到其它osd上
ceph osd reweight 2 0.0
# 待集群重新恢復為ok后執行以下命令將osd踢出集群
service ceph stop osd.2 ceph osd out 2 ceph osd crush remove osd.2 ceph auth del osd.2 ceph osd rm osd.2
5.2 有時可能碰上osd拉起后還是有些pg不健康的,比如:
(1)Unfound objects
ceph集群知道該對象存在,但無法定位該object在哪時會報這個錯誤。
解決辦法:
<1>嘗試讓失敗的osd起來,如果起來后集群恢復正常,則結束
<2>嘗試將該pg的unfound對象回滾到上一個版本,ceph pg $pgid mark_unfound_lost revert,如果恢復正常,則結束
<3>如果還是不行,那只有將該object刪除掉了,注意這會導致丟失數據,ceph pg $pgid mark_unfound_lost delete
(2)inconsistent objects
pg中保存的object中有些副本數據不一致,有些事伴隨着scrub errors錯誤
<1>ceph health detail 找出問題pg
<2>嘗試ceph pg repair $pgid,若成功,則結束(這個執行修復后一般要等久點,實在不能自動repair,再用以下方式)
<3>通過ceph pg map $pgid,找出主osd,打開其日志查看是哪個object不一致
<4>找出所有該objects所有副本存放的位置,用摘要算法(md5sum,sha256)等計算出其hash值,如果是3副本,刪除與其他副本不一致的;如果是2副本,則可能會誤刪。
<5> 再次執行 ceph pg repair $pgid
(3)stale pg
pg出現stale狀態,也就是pg處於僵死狀態,該狀態是無法處理新的請求了的,新的請求過來只會block,這種情況一般是由於所有副本pg的osd都掛了,要模擬其實也很簡單,比如設置2副本,然后將2個不同故障域的osd掛掉即可出現,最好的恢復方法當然是重新拉起這兩個osd,但有時可能出現這樣的情況,兩個osd永遠也拉不起來了,然后你把這兩個osd清理出去了,清理完后這些pg當然就是stale的狀態,這時的恢復方法只能是丟掉這個pg里的數據了,重新創建pg:
<1>使用命令ceph pg dump |grep stale 找出所有的stale的pg,也可以ceph health detail |grep stale
<2>執行ceph pg force_create_pg $pg_id命令強制重新創建pg,這時可以看到pg會轉為creating狀態
<3>重啟ceph集群中的所有OSD