作者:吳香偉 發表於 2014/10/10
版權聲明:可以任意轉載,轉載時務必以超鏈接形式標明文章原始出處和作者信息以及版權聲明
心跳是用於OSD節點間檢測對方是否故障的,以便及時發現故障節點進入相應的故障處理流程。故障檢測需要在故障的發現時間和心跳帶來的負載之間做權衡,如果心跳頻率太高則過多的心跳報文會影響系統性能,如果心跳頻率過低則會延長發現故障節點的時間,從而影響系統的可用性。
建立連接
在大規模部署的場景中,如果任意兩個OSD節點間都建立心跳連接將帶來巨大的負擔。尤其,當新加入一個OSD節點時這個負擔就會幾倍地增加。Ceph中每個OSD只和以下兩類節點建立心跳連接:一類是同個PG下的OSD節點之間,因為屬於同個PG的OSD節點會保存同份數據的副本,如若出現故障則會直接影響數據的可用性。另一類是OSD的左右兩個相鄰的節點,這兩個節點同自己物理上存在比較緊密的聯系,例如可能連接在同台交換機。另外,如果建立心跳的Peer數目少於osd_heartbeat_min_peers,那么OSD會繼續同離他較近的幾個OSD建立心跳連接。
OSD節點會監聽public、cluster、front和back四個端口,其中front和back兩個端口都是用於心跳的,cluster端口用來監聽來自OSD Peer的連接,public用來監聽來自Monitor和Client的連接。如果啟動OSD時沒有提供back的IP地址,則back使用cluster的IP地址;而front不單獨提供IP地址,直接使用public的IP地址。另外,OSD單獨創建了一個名為hbclient的Messenger,作為心跳的客戶端,單獨用來建立連接發送心跳報文。心跳報文優先發送給back連接。
代碼注釋
// ceph-osd.cc 啟動osd時創建Messengers
OSD::maybe_update_heartbeat_peers() 確定同哪些peer建立心跳連接,剔除已經down掉的節點的心跳連接
OSD::_add_heartbeat_peer() 同給定的peer建立心跳連接
OSDServeice::get_con_osd_hb() 獲取peer的front和back連接
配置
OPTION(public_network, OPT_STR, "")
OPTION(cluster_network, OPT_STR, "")
OPTION(osd_heartbeat_min_peers, OPT_INT, 10) // minimum number of peers
檢測故障
OSD使用T_Heartbeat線程定時向Peer OSDs發送心跳報文,發送報文的時間間隔在0.5~6.5之間,由osd_heartbeat_interval配置選項決定。心跳報文會同時向Peer OSD的front和back端口發送。心跳報文分兩種類型一種是Ping類型,另一種是Reply類型。Ping類型的報文是OSD主動發送給Peer OSD的報文,而Reply是Peer OSD回應給自己的報文。兩種類型的心跳報文都攜帶時間戳,但它們的時間戳代表的含義不一樣。Ping類型報文的時間戳是發送報文時的時間,而Reply類型報文的時間戳是從Ping報文中讀取出來的,不是代表它自己的發送時間而是代表它對應的Ping報文的發送時間。OSD接收到Reply報文時將記錄報文的時間戳,並以此來判斷是否超時。
對每個Peer節點,如果其最近的應答的時間(最近的Reply報文的時間戳)位於cutoff之前(即超時grace秒),則將其加入到failure_queue隊列。OSD會定時向Monitor匯報自己的狀態,在匯報狀態時將failure_queue隊列中Peer發送給Monitor,由Monitor將其標記為down狀態。Monitor在接收到OSD對Peer的故障報告后,通過PAXOS算法決定是否將Peer OSD標記為Down狀態。如果將Peer OSD標記為Down狀態,那么將更新OSD MAP,OSD接收到OSD Map更新的消息后,斷開和Peer OSD的心跳連接。
如果在向Monitor報告故障之后但在接收到OSD Down消息之前,再次接收到Peer OSD對心跳報文的回應,則將Peer OSD從failure_queue隊列中移除,並通知Monitor該節點依舊存活着。
代碼注釋
void OSD::heartbeat_entry() // T_Heartbeat線程入口函數,定時向心跳Peers發送心跳報文
void OSD::heartbeat()
map<int,utime_t> failure_queue; // 檢測到peer長時間沒心跳時,將peer加入到failure_queue隊列
map<int,entity_inst_t> failure_pending; // 故障報告給Monitor的Peer OSD
void send_failures();
void send_still_alive(epoch_t epoch, const entity_inst_t &i);
void OSD::note_down_osd(int peer)
void OSD::handle_osd_ping(MOSDPing *m) // 處理MOSDPing消息
配置
OPTION(osd_heartbeat_interval, OPT_INT, 6) // (seconds) how often we ping peers
OPTION(osd_heartbeat_grace, OPT_INT, 20) // (seconds) how long before we decide a peer has failed