ceph mon选举-lease机制

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ceph mon选举-lease机制相关的知识,希望对你有一定的参考价值。

众所周知,OSD是通过心跳检测机制,确保OSD是在线的,即OSD Peer之间,其实形成了彼此监控的网络,每 6秒(osd_heartbeat_interval向Peer)发送心跳信息,如果超过20秒(osd_heartbeat_grace) 时间没收到Peer OSD的心跳信息,则send_failure,状告该OSD已经fail。

Ceph mon的lease机制原理

本章节主题:那么mon是靠什么来监控彼此存活的呢?

答案是lease机制。

  • 当集群中存在多个ceph-mon的时候,有leader,有peon,ceph-mon进程也可能因为某种原因异常死亡或者网络不通,也必须有机制报障及时发现。这个机制是lease。
  • monitor内部采用lease协议,保证副本数据在一定时间范围内可读写(写需要是leader节点),同时也用来发现monitor的异常,然后重新选举。
  • leader节点会定期发送lease消息,延长各个peon的时间,但是如果某个peon 节点挂掉,leader节点就无法收到lease_ack消息,超时之后,就会重新选举。
  • 同样道理,leader节点也可能会异常宕机,peon节点也要能监督leader节点。如果leader down掉,peon节点就收不到来自leader的lease更新消息,超时之后,也会选举。

以下从二个角度较少mon的lease机制。

每次延长租约多长时间:      mon_lease 5s
mon_lease超时因子:        mon_lease_ack_timeout_factor 2
最长多久发送一次lease消息因子: mon_lease_renew_interval_factor 0.6
mon选举超时时间:          mon_election_timeout 5s

1.超时重新选举的timeout时间是多久? //mon_lease_ack_timeout_factor * mon_lease:   2*5=10s
2.最长多久发送一次lease消息?          //mon_lease_renew_interval_factor * mon_lease:0.6*5=3s
  • mon_lease_ack_timeout(默认10s)对monitor leader节点和peon节点都是有效。
  • 对于monitor leader来说,如果在10s 的时间内,没有获取到所有peon的lease ack,就判定超时,调用bootstrap重新选举。
  • 对于peon节点,在10s 时间内,没有收到新的lease 信息,就判定超时,也会发起重新选举。

mon lease机制流程

技术图片

  • 1.mon 超时机制lease相关切入函数extend_lease,该函数会由 PaxosService的每1s (paxos_propose_interval)调用触发,这个会在其他章节细述。
  • 2.leader mon 会发送MMonPaxos::OP_LEASE信号到其他peon(非主mon),来延长peon端的租约。
  • 3.PEON端会调用函数Paxos::handle_lease进行处理,进行延长lease 到mon leader指定的时间,然后发送OP_LEASE_ACK消息到mon leader。
  • 4.PEON端通过reset_lease_timeout函数进行超时判断,如果该定时器超时了,表示在过去的10s时间内,没有收到任何的OP_LEASE消息,基本可以确定mon leader出问题了,peon端发起选举。
  • 5.leader mon 调用handle_lease_ack函数,如果所有的peon都ack了,那么说明所有mon都存活,结束,如果部分peon mon没有ack,那么等待下次调用handle_lease_ack函数,等待peon mon ack。
  • 6.函数lease_ack_timeout用来控制超时,如果在10s内没有收到都没有收到某个peon mon ack,说明该mon 应该异常了,leader端开始选举。
  • 7.在10s超时期间调用函数lease_renew_timeout超时函数,如果3s内没有发送OP_LEASE消息到其他所有非主mon,那么超时重新调用函数extend_lease发送OP_LEASE消息。正常情况下 PaxosService的每1s(paxos_propose_interval)调用extend_lease。

以下从二个方面介绍,分别是mon(leader)和mon(peon)角度。

leader角度解析

lease这个功能的切入点是extend_lease函数。

1.发送OP_LEASE消息,注册超时回调

//运行在mon的leader节点
void Paxos::extend_lease()
{
  assert(mon->is_leader());//leader
  //assert(is_active());

  lease_expire = ceph_clock_now();
  lease_expire += g_conf->mon_lease;//5s
  acked_lease.clear();
  acked_lease.insert(mon->rank);

  dout(7) << "extend_lease now+" << g_conf->mon_lease 
      << " (" << lease_expire << ")" << dendl;

  // bcast
  /*向所有的peon发送OP_LEASE消息,消息体中带上lease_expire */
  for (set<int>::const_iterator p = mon->get_quorum().begin();
      p != mon->get_quorum().end(); ++p) {

    if (*p == mon->rank) continue;
    MMonPaxos *lease = new MMonPaxos(mon->get_epoch(), MMonPaxos::OP_LEASE,
                     ceph_clock_now());
    lease->last_committed = last_committed;
    lease->lease_timestamp = lease_expire;
    lease->first_committed = first_committed;
    mon->messenger->send_message(lease, mon->monmap->get_inst(*p));
  }

  // set timeout event.
  //  if old timeout is still in place, leave it.
  if (!lease_ack_timeout_event) {//注册lease_ack_timeout_event 事件,超时时间为10s
    lease_ack_timeout_event = mon->timer.add_event_after(
      g_conf->mon_lease_ack_timeout_factor * g_conf->mon_lease,//2*5=10s
      new C_MonContext(mon, [this](int r) {
      if (r == -ECANCELED)
        return;
      lease_ack_timeout();// 超时回调函数
    }));
  }

  // set renew event
  utime_t at = lease_expire;
  at -= g_conf->mon_lease;
  at += g_conf->mon_lease_renew_interval_factor * g_conf->mon_lease;//0.6*5=3s
  lease_renew_event = mon->timer.add_event_at(//注册lease_renew_event 事件,超时时间为3s
    at, new C_MonContext(mon, [this](int r) {
    if (r == -ECANCELED)
      return;
    lease_renew_timeout();// 超时回调函数
    }));
}

2.超时回调

//mon leader端
void Paxos::lease_ack_timeout()
{
  dout(1) << "lease_ack_timeout -- calling new election" << dendl;
  assert(mon->is_leader());
  assert(is_active());
  logger->inc(l_paxos_lease_ack_timeout);
  lease_ack_timeout_event = 0;
  mon->bootstrap();//开始触发选举
}

void Paxos::lease_renew_timeout()
{
  lease_renew_event = 0;
  extend_lease();// 3s超时后,继续执行下一轮,正常情况下,每隔3s触发一次OP_LEASE事件。
}

//mon的启动函数,开始发起选举
void Monitor::bootstrap()
{
  // reset
  state = STATE_PROBING;// 设置状态为STATE_PROBING
  // probe monitors
  dout(10) << "probing other monitors" << dendl;
  for (unsigned i = 0; i < monmap->size(); i++) {//向其他mon发送OP_PROBE信号,之后会调用选举函数,这里不细说。
    if ((int)i != rank)
      messenger->send_message(new MMonProbe(monmap->fsid, MMonProbe::OP_PROBE, name, has_ever_joined),
                  monmap->get_inst(i));
  }  
}

总结:这里每隔1s(paxos_propose_interval)左右,由PaxosService::dispatch触发调用函数Paxos::extend_lease,发送OP_LEASE事件,如果超时lease_renew_timeout,即最长3s时间进行OP_LEASE事件,发到其他所有非主mon。如果超过10s主mon没有收到其他所有mon的ack,超时,开始进行mon选举。

3.处理非主mon返回的OP_LEASE_ACK信息

void Paxos::handle_lease_ack(MonOpRequestRef op)
{
  op->mark_paxos_event("handle_lease_ack");
  MMonPaxos *ack = static_cast<MMonPaxos*>(op->get_req());
  int from = ack->get_source().num();

  if (!lease_ack_timeout_event) {
    dout(10) << "handle_lease_ack from " << ack->get_source()
         << " -- stray (probably since revoked)" << dendl;

  } else if (acked_lease.count(from) == 0) {
    acked_lease.insert(from);
    if (ack->feature_map.length()) {
      auto p = ack->feature_map.begin();
      FeatureMap& t = mon->quorum_feature_map[from];
      ::decode(t, p);
    }
    if (acked_lease == mon->get_quorum()) {//最后一个peon的消息也收到了,那么没有超时,就取消掉lease_ack_timeout_event事件
      // yay!
      dout(10) << "handle_lease_ack from " << ack->get_source()
           << " -- got everyone" << dendl;
      mon->timer.cancel_event(lease_ack_timeout_event);
      lease_ack_timeout_event = 0;

    } else {//并不是所有的peon都返回了
      dout(10) << "handle_lease_ack from " << ack->get_source()
           << " -- still need "
           << mon->get_quorum().size() - acked_lease.size()
           << " more" << dendl;
    }
  } else {//重复ack 取消
    dout(10) << "handle_lease_ack from " << ack->get_source()
         << " dup (lagging!), ignoring" << dendl;
  }

  warn_on_future_time(ack->sent_timestamp, ack->get_source());
}

peon角度解析

对于peon节点而言,收到OP_LEASE消息,是讨论的起点:

1.peon端处理OP_LEASE 消息

// peon
void Paxos::handle_lease(MonOpRequestRef op)
{
  op->mark_paxos_event("handle_lease");
  MMonPaxos *lease = static_cast<MMonPaxos*>(op->get_req());
  // sanity
  if (!mon->is_peon() ||
      last_committed != lease->last_committed) {
    dout(10) << "handle_lease i‘m not a peon, or they‘re not the leader,"
         << " or the last_committed doesn‘t match, dropping" << dendl;
    op->mark_paxos_event("invalid lease, ignore");
    return;
  }

  warn_on_future_time(lease->sent_timestamp, lease->get_source());

  // extend lease
  if (lease_expire < lease->lease_timestamp) {//延长lease 到mon leader指定的时间*
    lease_expire = lease->lease_timestamp;

    utime_t now = ceph_clock_now();
    if (lease_expire < now) {//此时可能发生mon laggy或者时钟skew
      utime_t diff = now - lease_expire;
      derr << "lease_expire from " << lease->get_source_inst() << " is " << diff << " seconds in the past; mons are probably laggy (or possibly clocks are too skewed)" << dendl;
    }
  }

  state = STATE_ACTIVE;

  dout(10) << "handle_lease on " << lease->last_committed
       << " now " << lease_expire << dendl;

  // ack /*发送OP_LEASE_ACK消息到mon leader*/
  MMonPaxos *ack = new MMonPaxos(mon->get_epoch(), MMonPaxos::OP_LEASE_ACK,
                 ceph_clock_now());
  ack->last_committed = last_committed;
  ack->first_committed = first_committed;
  ack->lease_timestamp = ceph_clock_now();
  ::encode(mon->session_map.feature_map, ack->feature_map);
  lease->get_connection()->send_message(ack);

  // (re)set timeout event. 如果该定时器超时了,表示在过去的mon_lease_ack_timeout时间内,没有收到任何的OP_LEASE消息,基本可以确定mon leader出问题了
  reset_lease_timeout();
}

2.peon端OP_LEASE消息超时回调注册

//接上
void Paxos::reset_lease_timeout()
{
  dout(20) << "reset_lease_timeout - setting timeout event" << dendl;
  if (lease_timeout_event)
    mon->timer.cancel_event(lease_timeout_event);
  lease_timeout_event = mon->timer.add_event_after(
    g_conf->mon_lease_ack_timeout_factor * g_conf->mon_lease,//2*5s=10s
    new C_MonContext(mon, [this](int r) {
    if (r == -ECANCELED)
      return;
    lease_timeout();//注册回调函数
      }));
}
//mon peon端
void Paxos::lease_timeout()
{
  dout(1) << "lease_timeout -- calling new election" << dendl;
  assert(mon->is_peon());
  logger->inc(l_paxos_lease_timeout);
  lease_timeout_event = 0;
  mon->bootstrap();//发起选举
}

mon的 leader和peon是互相监督,peon对monitor leader的监督,体现在reset_lease_timeout函数。他会以收到OP_LEASE消息的时间为起点,注册一个超时时间为mon_lease_ack_timeout的定时事件。如果该定时器超时了,表示在过去的mon_lease_ack_timeout时间内,没有收到任何的OP_LEASE消息,基本可以确定mon leader出问题了,发起选举。

以上是关于ceph mon选举-lease机制的主要内容,如果未能解决你的问题,请参考以下文章

Raft & Lease机制

记一次ceph心跳机制异常的案例

Ceph添加/删除Mon(ceph.conf)

Ceph手动修复mon 集群

ceph mon无法启动-rocksdb数据损坏

ceph在扩展mon节点时,要注意的问题