代码基于ceph nautilus版本
MDS关键概念
想要理解MDS切换过程,首先需要理清一些基本概念。
MDSMAP
- 包含整个ceph集群的所有mds的状态信息:fs个数、fs名称、各mds状态、数据池、元数据池信息,等等
- 包含当前的MDS map epoch(纪元),即地图创建的时间,以及它最后一次改变的时间。它还包含用于储存元数据的池,元数据服务器的列表,以及哪些元数据服务器up和in。要查看MDS map,请执行ceph fs dump。
RANK
- rank定义了多mds直接对元数据负载的划分,每个mds最多只能持有一个rank,每个rank对应一个目录子树,rank id从0开始。
- Ranks定义了元数据工作负载在 多个元数据服务器守护进程(multiple Metadata Server (MDS) daemons)之间的共享方式。ranks的数量是MDS守护进程的最大数量,可以在同一时间活动。每个MDS守护进程 handles分配给该rank的Ceph文件系统元数据的一个子集(a subset of the Ceph File System metadata。
- 每个MDS守护程序最初启动时都没有rank。Monitor会给守护进程分配一个rank。一个MDS守护进程在同一时间只能拥有一个rank。守护进程只有在它们被停止时才会失去rank。
这里注意一种特殊情况,即stand-replay状态下的mds也是持有rank的,s且 id和其follow的active mds的rank id相同,如下图所示
MDS JOURNAL
- cephfs的journal是用于记录元数据事件的日志
- journal以event形式存放在rados的metadata pool中
- 在处理每个io行为时,先写journal再执行实际的io操作
- 每个active mds维护自己的journal
- journal被分割为多个object
- mds会修剪(trim)不需要的journal条目
- journal event 查看:cephfs-journal-tool --rank=<fs>:<rank> event get list
(cephfs-journal-tool — Ceph Documentation 中文:https://www.bookstack.cn/read/ceph-10-zh/8a5dc6d87f084b2a.md)
下面是一个journal event示例(中间还有很多内容被折叠了):
CAPS
即cephfs实现的分布式锁,详见https://docs.ceph.com/docs/master/cephfs/client-auth/
MDS状态机
理解mds的切换首先需要认识清除mds有哪些状态,以及可以进行哪些状态跃迁。有关mds状态机的知识在官网上有详细的介绍:https://docs.ceph.com/docs/master/cephfs/mds-states/
MDS类图
参考下图:注意图中仅列出了mds部分架构和核心类,实际的组成更复杂,涉及的类和逻辑也更多。
冷备OR热备
-
冷备:默认配置下,除active mds外其余MDS均处于standby状态,除了保持和mon的心跳,其他什么都不做,cache为空,无rank。
-
热备:配置allow_standby_replay为true,每个active mds都会有一个专属standby-replay mds在follow,持有和active mds相同的rank id,不断从rados中读取journal加载到cache中以尽可能和actvie mds保持同步。
显然在热备状态下会有一个standby-replay的mds一直在更新cache,这样在切换发生时其切换流程会更快。
切换流程分析
首先分析切换过程之前,先明确MDS切换的两个核心思想:
- 新的active的mds的选举,是由mon来决定的,整个切换过程涉及mon和mds的多次交互
- 所有切换过程均是通过mdsmap来驱动的,mdsmap中标记了对当前集群中各mds状态的规划,mds每处理完一个阶段的规划后会主动向mon请求下一个阶段
上文中已介绍了mds的消息分发处理,而在切换流程中,mon和mds的交互都是mdsmap,下面先看一下mdsmap的处理流程:
MDSDaemon::handle_mds_map:
MDSRankDispatcher::handle_mds_map:
逻辑太多,只截取了部分代码
void MDSRankDispatcher::handle_mds_map(
const MMDSMap::const_ref &m,
const MDSMap &oldmap)
{
// I am only to be passed MDSMaps in which I hold a rank
ceph_assert(whoami != MDS_RANK_NONE);
// 当前状态为oldstate,从mds map中获取新的状态为state,
// 如果两者不相等,则更新last_state和incarnation,incarnation表示rank当前在哪个dameon?
MDSMap::DaemonState oldstate = state;
mds_gid_t mds_gid = mds_gid_t(monc->get_global_id());
state = mdsmap->get_state_gid(mds_gid);
if (state != oldstate) {
last_state = oldstate;
incarnation = mdsmap->get_inc_gid(mds_gid);
}
version_t epoch = m->get_epoch();
// note source's map version
// 当前mds集群状态已经准备变更,进入了新的epoch,那么需要更新其他mds的epoch值
if (m->get_source().is_mds() &&
peer_mdsmap_epoch[mds_rank_t(m->get_source().num())] < epoch) {
dout(15) << " peer " << m->get_source()
<< &#