Ceph-Mgr之源碼解析

導(dǎo)讀
ceph luminous版本中新增加了一個(gè)組件: Ceph Manager Daemon赴魁,簡(jiǎn)稱ceph-mgr氨距。 該組件的主要作用是分擔(dān)和擴(kuò)展monitor的部分功能恩伺,減輕monitor的負(fù)擔(dān)徙瓶,讓更好地管理ceph存儲(chǔ)系統(tǒng)喊积。
本文檔基于luminous版本簡(jiǎn)單介紹ceph-mgr的源碼實(shí)現(xiàn)烹困。由于ceph-mgr還在開(kāi)發(fā)完善,可能最新版與本文檔部分內(nèi)容有所出入乾吻,但是大體框架上應(yīng)該是沒(méi)什么變化的髓梅。
本文檔不介紹ceph-mgr的安裝部署與使用,具體請(qǐng)參照官網(wǎng):Ceph Manager Daemon
本文檔代碼基于Luminous 12.2版本進(jìn)行解析绎签,由于社區(qū)存在代碼變動(dòng)枯饿,其它版本有可能對(duì)不上號(hào),但是整體架構(gòu)應(yīng)該不會(huì)有太大的變化诡必。

mgr的實(shí)現(xiàn)與用途
ceph-mgr是由C/C++奢方、python以及Cpython等共同編寫完成的,mgr的實(shí)現(xiàn)使用了大量的Extending Python with C or C++的語(yǔ)法爸舒,不熟悉這塊的可以先在python官網(wǎng)中科普一下蟋字。
由ceph-mgr的實(shí)現(xiàn)其實(shí)大概可以猜到,其將ceph的部分C/C++實(shí)現(xiàn)的接口python化(即以前只能通過(guò)調(diào)用c/c++接口發(fā)送msg獲取比如osdmap碳抄、monmap等集群狀態(tài)愉老,現(xiàn)通過(guò)mgr可以很方便地拿到。同時(shí)剖效,ceph-mgr支持用戶自定義的plugin(插件純python開(kāi)發(fā)嫉入,特別方便),用以實(shí)現(xiàn)特殊功能璧尸。
截至目前為止咒林,ceph-mgr的官方plugins包括:

Dashboard(WEB界面的管理)
Restful API(API方式獲取ceph信息僵井,應(yīng)該與之前的ceph-rest-api功能一致)官疲、
Zabbix掠拳、Prometheus崎淳、Influx(這三個(gè)實(shí)現(xiàn)了ceph的數(shù)據(jù)收集济炎、監(jiān)控等功能)
mgr組件
ceph-mgr的重要類或模塊包括:MgrStandy、Mgr敌完、DaemonServer碉纺、PyModules、ClusterState遣鼓、DaemonState等啸盏。其主要功能描述如下:

MgrStandby
所有mgr服務(wù)啟動(dòng)時(shí)身份都是standby,唯一作用是包含一個(gè)mgr的client端骑祟,獲取mgrmap及相關(guān)msg回懦。在獲取了mgr-map發(fā)現(xiàn)自己為當(dāng)前active時(shí),才會(huì)初始化mgr主服務(wù)進(jìn)程次企。當(dāng)mgrmap中變?yōu)榉莂ctive狀態(tài)怯晕,則shutdown mgr主服務(wù)進(jìn)程,釋放資源缸棵。

Mgr
主要工作是初始化daemonserver舟茶、pymodules、clusterstate等主要功能類蛉谜,并handle standby mgr client的非mgrmap的消息(osdmap稚晚、pgmap、fsmap等)型诚。執(zhí)行了monc->sub_want()函數(shù)客燕,注冊(cè)了定期獲取數(shù)據(jù)操作。

**DaemonServer **
為mgr主要的服務(wù)進(jìn)程狰贯,和osd也搓、mds等類似,初始化了一個(gè)mgr類型的Messenger涵紊,監(jiān)聽(tīng)有關(guān)mgr消息傍妒,主要是MSG_PGSTATS、MSG_MGR_REPORT摸柄、MSG_MGR_OPEN颤练、MSG_COMMAND。比如執(zhí)行‘ceph tell mgr {command}’時(shí)就被發(fā)送到daemonserver中handle_command函數(shù)進(jìn)行處理(包括了native命令和plugin的commands)

**PyModules **
包含ActivePyModule驱负、StandbyPyModules嗦玖、ActivePyModules、BaseMgrModules跃脊、BaseMgrStandbyModules宇挫、PyModulesRegistry、PyModuleRunner等類酪术,分別處理mgr處于active和standby時(shí)對(duì)plugins的處理器瘪,并在active時(shí)初始化python的運(yùn)行環(huán)境,將plugin模塊初始化并加載運(yùn)行。該類大量使用了python的c++擴(kuò)展接口橡疼。

**ClusterState **
保存了cluster的狀態(tài)援所,部分狀態(tài)在monc中,由mgr類定期更新?tīng)顟B(tài)(ms_dispatch)

**DaemonState **
保存了DaemonServer的狀態(tài)信息

這些類之間的關(guān)系如下圖所示:


vCeph-Mgr 啟動(dòng)過(guò)程**


image.png

圖片已經(jīng)很詳細(xì)了衰齐,文字就不多說(shuō)了任斋。
mgr基于Active-Standby模式继阻,大體流程就是mgr進(jìn)程啟動(dòng)時(shí)先以MgrStandby身份啟動(dòng)耻涛,即所有mgr啟動(dòng)時(shí)都是standby,然后通過(guò)map知道自己是Active的mgr后瘟檩,才會(huì)啟動(dòng)實(shí)際處理程序初始化DaemonServer抹缕,監(jiān)聽(tīng)有關(guān)mgr的消息,加載并運(yùn)行modules(一部分為用戶自定義plugins)墨辛。

Mgr的一些功能介紹
mgr提供了常用的幾種函數(shù)接口卓研,只要重載這些接口,就能開(kāi)發(fā)plugin實(shí)現(xiàn)特定功能睹簇。以下是官方的介紹奏赘,即實(shí)現(xiàn)服務(wù)器、消息通知太惠、自定義命令等功能:

serve: member function for server-type modules. This function should block forever. 
notify: member function if your module needs to take action when new cluster data is available. 
handle_command: member function if your module exposes CLI commands.

編寫自定義plugin
下面以簡(jiǎn)單的自定義命令為例磨淌,描述如何編寫mgr的plugin。其他功能可自行參考源碼中的ceph plugin凿渊,自行學(xué)習(xí)模仿編寫梁只。

步驟一:在src/pybind/mgr/ 目錄下新建一個(gè)plugin,名字為hello埃脏,然后在hello目錄下新建一個(gè)名為init.py和module.py(名字必須是module.py搪锣,這是mgr程序識(shí)別這個(gè)plugin的核心文件)。兩個(gè)文件內(nèi)容如下所示:

# __init__.py
from module import *  # NOQA


# module.py
from mgr_module import MgrModule

class Module(MgrModule):
    COMMANDS = [
        {
            "cmd": "hello",
            "desc": "Say Hello",
            "perm": "r"
        }
    ]

    def handle_hello(self, cmd):
        return 0, "", "Hello World"

    def handle_command(self, cmd):
        self.log.error("handle_command")

        if cmd['prefix'] == "hello":
            return self.handle_hello(cmd)
        else:
            raise NotImplementedError(cmd['prefix'])


步驟二:配置ceph.conf文件彩掐,讓ceph-mgr啟動(dòng)時(shí)加載hello這個(gè)plugin构舟,修改如下:

[mgr]
        mgr modules = restful dashboard hello
        mgr data = /home/hhd/github/ceph/build/dev/mgr.$id
        mgr module path = /home/hhd/github/ceph/src/pybind/mgr

步驟三:重啟ceph-mgr,然后執(zhí)行hello命令:ceph tell mgr hello 堵幽,那么狗超,CLI就會(huì)執(zhí)行并返回“Hello World”
handle_command 處理流程
只需要重載handle_command
通常情況下,我們都是使用ceph官方自帶的命令谐檀,如ceph -s抡谐、rbd create等等,通過(guò)ceph-mgr桐猬,我們可以自定義命令了麦撵。運(yùn)行命令如下所示:

ceph tell mgr <command | help> 

該CLI命令最終會(huì)將請(qǐng)求Message發(fā)送到DaemonServer中進(jìn)行處理。DaemonServer根據(jù)Comand的類型調(diào)用對(duì)應(yīng)native函數(shù)或者plugin的handle_command函數(shù)進(jìn)行處理。其handle_command的處理流程如下所示:

CommandContext為handle_command()函數(shù)內(nèi)部定義的一個(gè)內(nèi)部類免胃,主要作用是管理執(zhí)行command的上下文管理音五。
如下為DaemonServer的dispatch實(shí)現(xiàn),發(fā)現(xiàn)MSG類型為COMMAND時(shí)轉(zhuǎn)到handle_command函數(shù)進(jìn)行處理羔沙。

bool DaemonServer::ms_dispatch(Message *m)
{
  // Note that we do *not* take ::lock here, in order to avoid
  // serializing all message handling.  It's up to each handler
  // to take whatever locks it needs.
  switch (m->get_type()) {
    case MSG_PGSTATS:
      cluster_state.ingest_pgstats(static_cast<MPGStats*>(m));
      maybe_ready(m->get_source().num());
      m->put();
      return true;
    case MSG_MGR_REPORT:
      return handle_report(static_cast<MMgrReport*>(m));
    case MSG_MGR_OPEN:
      return handle_open(static_cast<MMgrOpen*>(m));
    case MSG_COMMAND:
      return handle_command(static_cast<MCommand*>(m));
    default:
      dout(1) << "Unhandled message type " << m->get_type() << dendl;
      return false;
  };
}

在handle_command()中躺涝,大量代碼是檢索prefix對(duì)應(yīng)的command prefix,然后做處理扼雏。如果沒(méi)有找到native command坚嗜,才搜尋用戶自定義的plugin command。

bool DaemonServer::handle_command(MCommand *m)
{
  ...
  // None of the special native commands, 
  ActivePyModule *handler = nullptr;
  auto py_commands = py_modules.get_py_commands();
  for (const auto &pyc : py_commands) {
    auto pyc_prefix = cmddesc_get_prefix(pyc.cmdstring);
    dout(1) << "pyc_prefix: '" << pyc_prefix << "'" << dendl;
    if (pyc_prefix == prefix) {
      handler = pyc.handler;
      break;
    }
  }

  if (handler == nullptr) {
    ss << "No handler found for '" << prefix << "'";
    dout(4) << "No handler found for '" << prefix << "'" << dendl;
    cmdctx->reply(-EINVAL, ss);
    return true;
  } else {
    // Okay, now we have a handler to call, but we must not call it
    // in this thread, because the python handlers can do anything,
    // including blocking, and including calling back into mgr.
    dout(4) << "passing through " << cmdctx->cmdmap.size() << dendl;
    finisher.queue(new FunctionContext([cmdctx, handler](int r_) {
      std::stringstream ds;
      std::stringstream ss;
      int r = handler->handle_command(cmdctx->cmdmap, &ds, &ss);
      cmdctx->odata.append(ds);
      cmdctx->reply(r, ss);
    }));
    return true;
  }

處理notify流程
編寫plugin時(shí)诗充,也可以重載notify函數(shù)苍蔬,目的是當(dāng)cluster狀態(tài)有變化時(shí),可以通知plugin做相應(yīng)的處理操作蝴蜓。其操作流程如下圖所示:
Mgr::ms_dispatch() --> PyModules::notify_all() --> PyMgrModules::motify()
Mgr定時(shí)獲取fsmap碟绑、osdmap等數(shù)據(jù),當(dāng)數(shù)據(jù)來(lái)臨時(shí)茎匠,一方面更新clusterState值格仲,一方面調(diào)用notify_all函數(shù)通知所有的Plugin模塊;notify_all函數(shù)會(huì)遍歷每個(gè)plugin诵冒,調(diào)用plugin的notify函數(shù)凯肋,以便plugin做相應(yīng)處理。
其代碼實(shí)現(xiàn)如下造烁,根據(jù)cluster狀態(tài)變化進(jìn)行notify:

bool Mgr::ms_dispatch(Message *m)
{
  dout(4) << *m << dendl;
  Mutex::Locker l(lock);

  switch (m->get_type()) {
    case MSG_MGR_DIGEST:
      handle_mgr_digest(static_cast<MMgrDigest*>(m));
      break;
    case CEPH_MSG_MON_MAP:
      py_module_registry->notify_all("mon_map", "");
      m->put();
      break;
    case CEPH_MSG_FS_MAP:
      py_module_registry->notify_all("fs_map", "");
      handle_fs_map((MFSMap*)m);
      return false; // I shall let this pass through for Client
      break;
    case CEPH_MSG_OSD_MAP:
      handle_osd_map();

      py_module_registry->notify_all("osd_map", "");

      // Continuous subscribe, so that we can generate notifications
      // for our MgrPyModules
      objecter->maybe_request_map();
      m->put();
      break;
    case MSG_SERVICE_MAP:
      handle_service_map((MServiceMap*)m);
      py_module_registry->notify_all("service_map", "");
      m->put();
      break;
    case MSG_LOG:
      handle_log(static_cast<MLog *>(m));
      break;

    default:
      return false;
  }
  return true;
}

根據(jù)消息向注冊(cè)的plugin進(jìn)行通知否过。

void ActivePyModules::notify_all(const std::string &notify_type,
                     const std::string &notify_id)
{
  Mutex::Locker l(lock);

  dout(10) << __func__ << ": notify_all " << notify_type << dendl;
  for (auto& i : modules) {
    auto module = i.second.get();
    // Send all python calls down a Finisher to avoid blocking
    // C++ code, and avoid any potential lock cycles.
    finisher.queue(new FunctionContext([module, notify_type, notify_id](int r){
      module->notify(notify_type, notify_id);
    }));
  }
}





ceph集群狀態(tài)處理
在寫Mgr的plugin時(shí),經(jīng)常會(huì)導(dǎo)入ceph_module這個(gè)模塊惭蟋,通過(guò)ceph_module獲取cluster的狀態(tài)苗桂,具體操作如下:

import ceph_module

fsmap = ceph_module.get("fs_map")

# MgrModule包含了ceph_state,集成它可以入func這樣使用
class Module(MgrModule):

    def func():
        ....
        self.get("fs_map")
        .... 


其原理如下所示:

定義BaseMgrModule告组,BaseMgrModule結(jié)構(gòu)體是python用戶接口的 plugin的基類煤伟,定義在mgr_module.py中。

// mgr/BaseMgrModule.cc 
typedef struct {
  PyObject_HEAD
  ActivePyModules *py_modules;
  ActivePyModule *this_module;
} BaseMgrModule;

python類MgrModule繼承于BaseMgrModule木缝,而所有的用戶plugin Module都需要繼承MgrModule便锨,其定義如下:

# pybind/mgr/mgr_module.py
class MgrModule(ceph_module.BaseMgrModule):
    pass

而B(niǎo)aseMgrModule的方法定義在此:

// mgr/BaseMgrModule.cc 
PyMethodDef BaseMgrModule_methods[] = {
  {"_ceph_get", (PyCFunction)ceph_state_get, METH_VARARGS,
   "Get a cluster object"},
  {"_ceph_get_server", (PyCFunction)ceph_get_server, METH_VARARGS,
   "Get a server object"},
  {"_ceph_get_metadata", (PyCFunction)get_metadata, METH_VARARGS,
   "Get a service's metadata"},
  ...



以我們經(jīng)常會(huì)使用到的get方法為例,其調(diào)用過(guò)程為:

MgrModule::get()-->BaseMgrModule::_ceph_get() --> ActiveModules::get_python()

在get_python()函數(shù)我碟,基于參數(shù)值進(jìn)行判斷放案,調(diào)用相應(yīng)的ceph C++庫(kù)函數(shù)向mon進(jìn)行數(shù)據(jù)獲取。其處理程序如下:

// ActiveModules.cc
PyObject *ActivePyModules::get_python(const std::string &what)
{
  ...
  if (what == "fs_map") {
    PyFormatter f;
    cluster_state.with_fsmap([&f](const FSMap &fsmap) {
      fsmap.dump(&f);
    });
    return f.get();
  } else if (what == "osdmap_crush_map_text") {
  ...  
  }

// ClusterState.h
template<typename Callback, typename...Args>
  void with_fsmap(Callback&& cb, Args&&...args) const
  {
    Mutex::Locker l(lock);
    std::forward<Callback>(cb)(fsmap, std::forward<Args>(args)...);
  }



上述大量使用了C++ 11的特性矫俺,包括lamda表達(dá)式吱殉、模板掸冤、decltype、forward友雳、尾置返回等語(yǔ)言特性稿湿。
PyFormatter類控制格式化輸出,fsmap為ClusterState的成員變量押赊,由Mgr在每次處理ms_dispatch時(shí)進(jìn)行更新饺藤。

原文:https://blog.csdn.net/hedongho/article/details/78875825

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市流礁,隨后出現(xiàn)的幾起案子涕俗,更是在濱河造成了極大的恐慌,老刑警劉巖崇棠,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咽袜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡枕稀,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門谜嫉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)萎坷,“玉大人,你說(shuō)我怎么就攤上這事沐兰《叩担” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵住闯,是天一觀的道長(zhǎng)瓜浸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)比原,這世上最難降的妖魔是什么插佛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮量窘,結(jié)果婚禮上雇寇,老公的妹妹穿的比我還像新娘。我一直安慰自己蚌铜,他們只是感情好锨侯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著冬殃,像睡著了一般囚痴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上审葬,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天深滚,我揣著相機(jī)與錄音骂束,去河邊找鬼。 笑死成箫,一個(gè)胖子當(dāng)著我的面吹牛展箱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蹬昌,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼混驰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了皂贩?” 一聲冷哼從身側(cè)響起栖榨,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎明刷,沒(méi)想到半個(gè)月后婴栽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辈末,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年愚争,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挤聘。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轰枝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出组去,到底是詐尸還是另有隱情鞍陨,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布从隆,位于F島的核電站诚撵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏键闺。R本人自食惡果不足惜寿烟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望艾杏。 院中可真熱鬧韧衣,春花似錦、人聲如沸购桑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)勃蜘。三九已至硕噩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缭贡,已是汗流浹背炉擅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工辉懒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谍失。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓眶俩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親快鱼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子颠印,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容