ceph源码分析 --MonitorDB

本文深入分析Ceph MonitorDB的实现,包括MonitorDBStore如何维护一致性,数据变化过程,以及monmap的同步机制。在启动时,Monitor会通过bootstrap与集群其他节点同步,确保monmap等关键数据的一致。此外,介绍了monmap的管理操作,如停服、导出、查看和修改。
摘要由CSDN通过智能技术生成

1.概述

ceph monitor的一个主要功能是使用paxos分布式式协议维护一个key/value数据库的一致性(最主要的就是各个map的一致性,对于monitor而言,即monmap)。12.2.2版本所使用的数据库引擎从原来的leveldb转变为了rocksdb。

之前有一个疑惑,monitor在部署的时候,可以通过ceph.conf文件mon_host的ip获取,那么monmap是否根据静态的ceph.conf文件保持一致呢?
看过paxos算法才知道,答案是否定的,它依赖于monmap文件。(每次map文件变更前, 需要多数monitor节点同意, 确保多数或所有(quorum)节点的map文件一致, 并且map文件是增量更新的, 每次更新产生一个新的version, 所以当mon节点离线再加入时, 需要同步增量部分的信息)

2.MonitorDBStore类

说起维护monitor的数据库一致性,不得不说MonitorDBStore类是如何抽象对k/v数据库的操作的。从MonitorDBStore类的定义出发,一步一步探测它的具体实现:

//每一个op可以看做一个原子性的key/value批量更新
struct Op {
    uint8_t type;
    string prefix;//为了各个模块不相互干扰,每个模块会选择一个前缀    string key, endkey;//prefix+key构成了各个模块独立的key
    bufferlist bl;

这些op将形成事务被encode,decode。

//Transaction类来说明一个事务包含的所有操作(put erase compact)
 struct Transaction;
  typedef ceph::shared_ptr<Transaction> TransactionRef;
  struct Transaction {
    list<Op> ops;
    uint64_t bytes, keys;
    Transaction() : bytes(0), keys(0) {}
    enum {
      OP_PUT    = 1,//设置 key value
      OP_ERASE  = 2,//移除
      OP_COMPACT = 3,//适配??
    };

为了在服务器间的传送,所有的transaction都会经过encode,decode。

    void encode(bufferlist& bl) const {
      ENCODE_START(2, 1, bl);
      ::encode(ops, bl);
      ::encode(bytes, bl);
      ::encode(keys, bl);
      ENCODE_FINISH(bl);
    }

    void decode(bufferlist::iterator& bl) {
      DECODE_START(2, bl);
      ::decode(ops, bl);
      if (struct_v >= 2) {
    ::decode(bytes, bl);
    ::decode(keys, bl);
      }
      DECODE_FINISH(bl);
    }

接下来就是ceph 后端的处理:

int apply_transaction(MonitorDBStore::TransactionRef t) {
    KeyValueDB::Transaction dbt = db->get_transaction();
    ...
    //获取transaction中需要执行的op,同步操作所有compact的 key->value
        list<pair<string, pair<string,string> > > compact;
        //
    for (list<Op>::const_iterator it = t->ops.begin();
     it != t->ops.end();
     ++it) {
      const Op& op = *it;
      switch (op.type) {
      case Transaction::OP_PUT:
    dbt->set(op.prefix, op.key, op.bl);
    break;
      case Transaction::OP_ERASE:
    dbt->rmkey(op.prefix, op.key);
    break;
      case Transaction::OP_COMPACT:
    compact.push_back(make_pair(op.prefix, make_pair(op.key, op.endkey)));
    break;
      default:
    derr << __func__ << " unknown op type " << op.type << dendl;
    ceph_abort();
    break;
      }
    }
    //submit value
    int r = db->submit_transaction_sync(dbt);
    if (r >= 0) {
      while (!compact.empty()) {
    if (compact.front().second.first == string() &&
        compact.front().second.second == string())
      db->compact_prefix_async(compact.front().first);
    else
      db->compact_range_async(compact.front().first, compact.front().second.first, compact.front().second.second);
    compact.pop_front();
      }
    } else {
      assert(0 == "failed to write to db");
    }
    return r;
  }

不同prefix对应的transaction 间使用queue_transaction函数进行异步处理。

  void queue_transaction(MonitorDBStore::TransactionRef t,
             Context *oncommit) {
    io_work.queue(new C_DoTransaction(this, t, oncommit));
  }

而多种prefix对应的transaction的获取通过get_iterator函数,而每种transaction对应的key value则通过get函数。
其他的函数不再此一一解释。

3.MonitorDBstore数据变化

从monitor启动,我们来看一下MonitorDBstore发生的一系列基于key/value的更新。
每次monitor启动时都会按照monmap中的服务器地址去连接其他monitor服务器,并同步数据。这里有两种情况,一种是db中不存在monmap(需要执行mkfs重新产生),另一种是已经添加过的mon节点由于网络或者其他原因异常,恢复正常后的重启(直接从db中读取)。
很明显所有的节点都会从无到有,因此这里的两种情况其实只是单存的区分获取monmap的方式而已。注意一点:monmap很重要,一旦产生,之后的启动不会再重新配置,因此一定要确保配置的正确性。

接下来先讲一下mkfs的大致流程:

if (mkfs) {

        // resolve public_network -> public_addr
    pick_addresses(g_ceph_context, CEPH_PICK_ADDRESS_PUBLIC);

    common_init_finish(g_ceph_context);

    bufferlist monmapbl, osdmapbl;
    std::string error;
    MonMap monmap;
// load or generate monmap
...
          try {
    monmap.decode(monmapbl);
    // always mark seed/mkfs monmap as epoch 0
    monmap.set_epoch(0);
      }
      ...
            ostringstream oss;
       //根据ceph.conf对该节点的monmap进行build init
      int err = monmap.build_initial(g_ceph_context, oss);
     ...
   }

具体的实现过程如下:

int MonMap::build_initial(CephContext *cct, ostream& errout)
{
  const md_config_t *conf = cct->_conf;
  ...
  // -m foo?根据ceph.conf中noname的mon_host生成加入的mon的ip
  if (!conf->mon_host.empty()) {
    int r = build_from_host_list(conf->mon_host, "noname-");
      // What monitors are in the config file?
  std::vector <std::string> sections;
  int ret = conf->get_all_sections(sections);
  if (ret) {
    errout << "Unable to find any monitors in the configuration "
         << "file, because there was an error listing the sections. error "
     << ret << std::endl;
    return -ENOENT;
  }
  std::vector <std::string> mon_names;
  for (std::vector <std::string>::const_iterator s = sections.begin();
       s != sections.end(); ++s) {
    if ((s->substr(0, 4) == "mon.") && (s->size() > 4)) {
      mon_names.push_back(s->substr(4));
    }
  }

  // Find an address for each monit
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值