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