Ceph之Monitor分析2:启动和初始化流程

344 阅读3分钟

借助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]$