借助K8s的init-mon-fs容器特性,完成第一次启动。
初始化命令:--mkfs 第一次启动
init-mon-fs:
Command:
ceph-mon
Args:
--fsid=36e2eb13-f04c-4248-9efb-0fd4476cfae0
--keyring=/etc/ceph/keyring-store/keyring
--log-to-stderr=true
--err-to-stderr=true
--mon-cluster-log-to-stderr=true
--log-stderr-prefix=debug
--default-log-to-file=false
--default-mon-cluster-log-to-file=false
--mon-host=$(ROOK_CEPH_MON_HOST)
--mon-initial-members=$(ROOK_CEPH_MON_INITIAL_MEMBERS)
--id=b
--setuser=ceph
--setgroup=ceph
--public-addr=172.22.164.xx6
--mkfs
Monitor的初始化启动流程:
main函数的主体逻辑:
(1)设置当前线程名称为"ceph-mon"
(2)将命令行参数全部保存到vector args中,解析命令行参数。
(3)全局初始化:global_init(&defaults, args, CEPH_ENTITY_TYPE_MON, CODE_ENVIRONMENT_DAEMON, flags);
(3.1)在CephContext *cct = common_preinit()中构建CephContext *cct = new CephContext()对象,将其保存至全局变量g_ceph_context = cct,CephContext实例主要是创建各种组件实例并注册,比如:
创建logging::Log实例,将一个LogObs对象作为空间日志观察者注册到_conf对象中,在配置发生改变时,接收到通知,并更新logging::Log。
创建CephContextObs对象作为Ceph配置的观察者。
创建LockdepObs对象作为锁依赖观察者,在系统进入死锁时给出帮助信息。
创建PerfCountersCollection对象
创建AdminSocket对象,用于处理Ceph管理命令,并注册了一堆命令。
创建HeartbeatMap对象,用于处理心跳。
创建PluginRegistry对象,用于管理各种插件。
创建MempoolObs实例注册为单例对象,观察Ceph系统中内存池mempools使用情况。
(3.2)设置配置信息变量的各项值auto& conf = cct->_conf;
(3.3)解析配置文件、环境变量和命令行参数
conf.parse_config_files(c_str_or_null(conf_file_list), &cerr, flags); 解析所有的schema值,并从配置文件中读取配置值。
conf.parse_env(cct->get_module_type()); 从环境变量中读取配置值。
conf.parse_argv(args); 从命令行参数中读取配置值。
(3.4)CrushMap的初始化:初始化CrushLocation类,g_ceph_context->crush_location.init_on_startup(),如果配置中设置了,优先使用配置文件中的参数进行初始化;否则,根据本地主机名信息设置默认的CrushLocation对象,其中host键保存主机名,root键保存默认的根位置信息,即default。
mkfs分支的主体逻辑:
(4)mkfs分支:第一次要执行mkfs操作
(4.1)检查指定的monitor数据目录是否存在:mon_data目录/var/lib/ceph/mon/ceph-${id}/,不存在,则创建;存在,则返回,不再进行mkfs流程,即:mkfs只执行一次。
(4.2)获取入参的public_addr,进而获取public_network、public_addrv:pick_addresses(g_ceph_context, CEPH_PICK_ADDRESS_PUBLIC);并将其设置为新的Monitor地址。
(4.3)common初始化完成:在common_init_finish(g_ceph_context);中设置cct->_finished = true;cct->_finished = true;
创建CephContext 中AdminSocket对象对应的线程,Service_thread启动一些服务线程。
(4.4)接下来处理IP地址,加载或生成monmap数据,如果入参--monmap指定的,就从指定文件中读取,用monmap中的Monitor信息;如果--mon_host指定的,就要解析并初始化monmap,使用本地数据生成的Monitor信息。
(4.5)保存monmap.fsid = fsid
(4.6)如果osdmap也在入参中指定了,就读取出来。
(4.7)实例化存储数据库,用于monmap等信息的存储:MonitorDBStore store(g_conf()->mon_data),这是LevelDB Store, 目录是/var/lib/ceph/ceph-${id}/store.db,打开LevelDB数据库store.create_and_open(oss);
(4.8)实例化Monitor对象:Monitor mon(g_ceph_context, g_conf()->name.get_id(), &store, 0, 0, &monmap),
(4.8.1)设置rank(-1),rank代表在monmap中的位置,设置初始rank值为-1,表示还未在monmap中(此值会在选举中用到)
(4.8.2)实例化Paxos对象,并定义并初始化多个Paxos_service:MDSMonitor/MonmapMonitor/OSDMonitor/MgrMonitor/HealthMonitor/ConfigMonitor等。
paxos = std::make_unique<Paxos>(*this, "paxos");
//定义多个paxos_service服务
paxos_service[PAXOS_MDSMAP].reset(new MDSMonitor(*this, *paxos, "mdsmap"));
paxos_service[PAXOS_MONMAP].reset(new MonmapMonitor(*this, *paxos, "monmap"));
paxos_service[PAXOS_OSDMAP].reset(new OSDMonitor(cct, *this, *paxos, "osdmap"));
paxos_service[PAXOS_LOG].reset(new LogMonitor(*this, *paxos, "logm"));
paxos_service[PAXOS_AUTH].reset(new AuthMonitor(*this, *paxos, "auth"));
paxos_service[PAXOS_MGR].reset(new MgrMonitor(*this, *paxos, "mgr"));
paxos_service[PAXOS_MGRSTAT].reset(new MgrStatMonitor(*this, *paxos, "mgrstat"));
paxos_service[PAXOS_HEALTH].reset(new HealthMonitor(*this, *paxos, "health"));
paxos_service[PAXOS_CONFIG].reset(new ConfigMonitor(*this, *paxos, "config"));
(4.9)Monitor::mkfs():将magic、支持的特性、monmap、osdmap(如果有)、keyring、fsid、cluster uuid等编码后放进LevalDB事务中,然后提交到数据库store->apply_transaction(t)。mkfs流程完成,函数退出。
第二次启动执行命令:
Containers:
mon:
Command:
ceph-mon
Args:
--fsid=36e2eb13-f04c-4248-9efb-0fd4476cfae0
--keyring=/etc/ceph/keyring-store/keyring
--log-to-stderr=true
--err-to-stderr=true
--mon-cluster-log-to-stderr=true
--log-stderr-prefix=debug
--default-log-to-file=false
--default-mon-cluster-log-to-file=false
--mon-host=$(ROOK_CEPH_MON_HOST)
--mon-initial-members=$(ROOK_CEPH_MON_INITIAL_MEMBERS)
--id=b
--setuser=ceph
--setgroup=ceph
--foreground
--public-addr=172.22.164.xx6
--setuser-match-path=/var/lib/ceph/mon/ceph-b/store.db
--public-bind-addr=$(ROOK_POD_IP)
在host上看ceph-mon进程,可以看到:
ceph-mon --fsid=36e2eb13-f04c-4248-9efb-0fd4476cfae0
--keyring=/etc/ceph/keyring-store/keyring
--log-to-stderr=true
--err-to-stderr=true
--mon-cluster-log-to-stderr=true
--log-stderr-prefix=debug
--default-log-to-file=false
--default-mon-cluster-log-to-file=false
--mon-host=[v2:172.22.152.xx4:3300,v1:172.22.152.xx4:6789],[v2:172.22.164.xx6:3300,v1:172.22.xx4.xx6:6789],[v2:172.22.247.xx7:3300,v1:172.22.xx7.xx7:6789]
--mon-initial-members=d,b,c
--id=b
--setuser=ceph
--setgroup=ceph
--foreground
--public-addr=172.22.164.xx6
--setuser-match-path=/var/lib/ceph/mon/ceph-b/store.db
--public-bind-addr=172.21.8.xx1 //Pod IP
非mkfs启动主体流程:
(4)非mkfs分支:第二次启动执行
(4.1)初始化Preforker,预加载纠删码
(4.2)设置信号处理函数
(4.3)实例化LevelDB数据库MonitorDBStore *store = new MonitorDBStore(g_conf()->mon_data),用于monmap等信息的读取和存储。
(4.4)打开数据库,读取magic值,校验特性,表示数据库是可用的。
(4.5)命令行是否有新注入的monmap,有,则从DB中读取monmap,并将新注入的monmap添加进去,保存到数据库。
(4.6)从DB中读出monmap并解码。获取Monitor的IP地址,判断当前Monitor的ID是否存在于Monmap中,如果存在,则调用monmap.get_addrs()函数从Monmap中获取本地Monitor的IP地址;如果不存在,则说明本Monitor正在尝试加入到一个已经存在的集群中,会调用pick_addresses()选择IP地址,优先选择公网IP地址,并保存到ipaddrs变量中。如果没有公网IP地址可用,则调用MonMap::build_initial()生成一个初始化的Monmap,并将本地Monitor的IP地址信息保存到ipaddrs中。
if (monmap.contains(g_conf()->name.get_id()))
(4.7)创建和配置Monitor的通信模块Messenger,用于Ceph的消息传递,调用monmap.get_rank()获取本地Monitor的排名rank值,从配置文件中获取公开的Messenger类型public_msgr_type,如果配置文件中没有设置公开Messenger类型,则使用默认的Messenger类型ms_type。然后调用Messenger::create()创建一个名为mon的Messenger,并设置其类型为public_msgr_type,本地实体ID为MON(rank)。
Messenger *msgr = Messenger::create(g_ceph_context, public_msgr_type, entity_name_t::MON(rank), "mon", 0);该通信模块是一个异步消息对象,包含:分发队列DispatchQueue、网络工作栈RDMA或DPDK、工作者线程Worker。
(4.8)设置通信模块Messenger的协议、策略等配置。
(4.9)构建新的Monitor对象:mon = new Monitor(g_ceph_context, g_conf()->name.get_id(), store, msgr, mgr_msgr, &monmap);
(4.9.1)设置rank(-1),rank代表在monmap中的位置,设置初始rank值为-1,表示还未在monmap中(此值会在选举中用到)
(4.9.2)实例化Paxos对象,并定义并初始化多个Paxos_service:MDSMonitor/MonmapMonitor/OSDMonitor/MgrMonitor/HealthMonitor/ConfigMonitor等。
paxos = std::make_unique<Paxos>(*this, "paxos");
//定义多个paxos_service服务
paxos_service[PAXOS_MDSMAP].reset(new MDSMonitor(*this, *paxos, "mdsmap"));
paxos_service[PAXOS_MONMAP].reset(new MonmapMonitor(*this, *paxos, "monmap"));
paxos_service[PAXOS_OSDMAP].reset(new OSDMonitor(cct, *this, *paxos, "osdmap"));
paxos_service[PAXOS_LOG].reset(new LogMonitor(*this, *paxos, "logm"));
paxos_service[PAXOS_AUTH].reset(new AuthMonitor(*this, *paxos, "auth"));
paxos_service[PAXOS_MGR].reset(new MgrMonitor(*this, *paxos, "mgr"));
paxos_service[PAXOS_MGRSTAT].reset(new MgrStatMonitor(*this, *paxos, "mgrstat"));
paxos_service[PAXOS_HEALTH].reset(new HealthMonitor(*this, *paxos, "health"));
paxos_service[PAXOS_CONFIG].reset(new ConfigMonitor(*this, *paxos, "config"));
(4.10)Monitor对象的预初始化:mon->preinit():
(4.10.1)创建quorum:遍历当前的mon_info信息,剔除不在mon_initial_members中的成员;遍历mon_initial_members中的成员,不在当前mon_info中的,添加进来。
(4.10.2)初始化Paxos协议:init_paxos();
// load paxos variables from stable storage
last_pn = get_store()->get(get_name(), "last_pn");
accepted_pn = get_store()->get(get_name(), "accepted_pn");
last_committed = get_store()->get(get_name(), "last_committed");
first_committed = get_store()->get(get_name(), "first_committed");
初始化所有的Paxos_Service
(4.10.3)从DB中读取keyring,并缓存。
(4.10.4)绑定Monitor的通信地址:通信系统绑定监听地址msgr->bindv(bind_addrs),等待与其他monitor建立connect连接。
(4.11)Monitor对象的初始化:mon->init();
(4.11.1)初始化定时器:timer.init(),创建safe_timer线程
(4.11.2)添加一个Messenger到dispatcher:AsyncMessenger::ready(),启动所有的工作者线程和分发队列线程,准备就绪。
(4.11.3)设置当前Monitor状态为:state = STATE_PROBING,开始探测probing
(4.11.4)正式进入bootstrap();
MonMap结构体:
class MonMap {
public:
epoch_t epoch; // what epoch/version of the monmap
uuid_d fsid;
utime_t last_changed;
utime_t created;
std::map<std::string, mon_info_t> mon_info;
std::map<entity_addr_t, std::string> addr_mons;
std::vector<std::string> ranks;
std::set<int> removed_ranks;
mon_feature_t persistent_features;
mon_feature_t optional_features;
ceph_release_t min_mon_release{ceph_release_t::unknown};
election_strategy strategy = CLASSIC;
std::set<std::string> disallowed_leaders;
bool stretch_mode_enabled = false;
string tiebreaker_mon;
set<string> stretch_marked_down_mons;
}
void MonMap::calc_legacy_ranks()
{
ranks.resize(mon_info.size());
set<mon_info_t, rank_cmp> tmp;//rank_cmp是个比较函数
for (auto p = mon_info.begin(); p != mon_info.end(); ++p) {
mon_info_t &m = p->second;
tmp.insert(m);
}
// map the set to the actual ranks etc
unsigned i = 0;
for (auto p = tmp.begin(); p != tmp.end(); ++p, ++i) {
ranks[i] = p->name;//这里ranks里面按由小到大排列的值
}
}
monmap dump信息
[core@master2]$ oc exec -n openshift-storage $(oc get pods -n openshift-storage -l app=rook-ceph-tools -o name) -- ceph mon dump -f json | python3 -m json.tool
dumped monmap epoch 5
{
"epoch": 5,
"fsid": "36e2eb13-f04c-4248-9efb-0fd4476cfae0",
"modified": "2023-04-26T03:21:42.244469Z",
"created": "2023-04-19T09:30:19.763700Z",
"min_mon_release": 16,
"min_mon_release_name": "pacific",
"election_strategy": 1,
"disallowed_leaders: ": "",
"stretch_mode": false,
"tiebreaker_mon": "",
"removed_ranks: ": "0",
"features": {
"persistent": [
"kraken",
"luminous",
"mimic",
"osdmap-prune",
"nautilus",
"octopus",
"pacific",
"elector-pinging"
],
"optional": []
},
"mons": [
{
"rank": 0,
"name": "b",
"public_addrs": {
"addrvec": [
{
"type": "v2",
"addr": "172.22.164.xx6:3300",
"nonce": 0
},
{
"type": "v1",
"addr": "172.22.164.xx6:6789",
"nonce": 0
}
]
},
"addr": "172.22.164.xx6:6789/0",
"public_addr": "172.22.164.xx6:6789/0",
"priority": 0,
"weight": 0,
"crush_location": "{}"
},
{
"rank": 1,
"name": "c",
"public_addrs": {
"addrvec": [
{
"type": "v2",
"addr": "172.22.247.xx7:3300",
"nonce": 0
},
{
"type": "v1",
"addr": "172.22.247.xx7:6789",
"nonce": 0
}
]
},
"addr": "172.22.247.xx7:6789/0",
"public_addr": "172.22.247.xx7:6789/0",
"priority": 0,
"weight": 0,
"crush_location": "{}"
},
{
"rank": 2,
"name": "d",
"public_addrs": {
"addrvec": [
{
"type": "v2",
"addr": "172.22.152.xx4:3300",
"nonce": 0
},
{
"type": "v1",
"addr": "172.22.152.xx4:6789",
"nonce": 0
}
]
},
"addr": "172.22.152.xx4:6789/0",
"public_addr": "172.22.152.xx4:6789/0",
"priority": 0,
"weight": 0,
"crush_location": "{}"
}
],
"quorum": [
0,
1,
2
]
}
[core@master2]$