一次严重的librbd客户端不可用

我的周一,安静~~

Posted by 鱼鱼 on May 8, 2018

前阵子,一个OSD线程的max_open_files达到设置的最大值,导致在simple message通信的accept线程退出,OSD不在监听任何,任何客户端重连都会失败,可复现代码如下:

 void *Accepter::entry()
 {
  int errors = 0;

  struct pollfd pfd;
  pfd.fd = listen_sd;
  pfd.events = POLLIN | POLLERR | POLLNVAL | POLLHUP;
  
  while (!done) {
    int r = poll(&pfd, 1, -1);
    if (r < 0)
      break;
    ldout(msgr->cct,20) << "accepter poll got " << r << dendl;

    if (pfd.revents & (POLLERR | POLLNVAL | POLLHUP))
      break;

    ldout(msgr->cct,10) << "pfd.revents=" << pfd.revents << dendl;
    if (done) break;

    // accept
    entity_addr_t addr;
    socklen_t slen = sizeof(addr.ss_addr());
    int sd = ::accept(listen_sd, (sockaddr*)&addr.ss_addr(), &slen);
    
    //下面的if语句是我添加的,人为制作错误情况,真实情况很难复现
    if (msgr->cct->_conf->ms_tcp_inject_error){
	  if (sd >= 0){
		ldout(msgr->cct,0) << "Inject error by yuyu " << sd << dendl;
		sd = -1;
	  }
    }

    if (sd >= 0) {
      errors = 0;
      ldout(msgr->cct,10) << "accepted incoming on sd " << sd << dendl;
  
      msgr->add_accept_pipe(sd);
    } 
    else {
      ldout(msgr->cct,0) << "accepter no incoming connection?  sd = " << sd
         << " errno " << errno << " " << cpp_strerror(errno) << dendl;
    //如果真的出现4次以上的错误,在这里直接跳出循环,整个osd监听线程就退出了,没有任何
    //错误处理机制!!!
	if (++errors > 4)
	    break;
    }
  }

  ldout(msgr->cct,20) << "accepter closing" << dendl;
  
  if (listen_sd >= 0) {
    ::close(listen_sd);
    listen_sd = -1;
  }
  
  ldout(msgr->cct,10) << "accepter stopping" << dendl;
  return 0;
}

这个错误,会引起什么问题呢?

新的连接或者网络突然断开,需要重新连接时,建立连接失败,但是已经连接上的,还继续保持,也就是说心跳机制还是运行的,OSD之间不会把accept线程退出的OSD上报给Monitor,让Monitor Mark down这个有问题的OSD, 也就是说这个OSD实际不工作了,一个假死状态。

针对这个问题,目前我能想到的,只影响OSD,其他MON, MDS,连接不上,总有备用的,但是OSD没有备 用的,我已经给社区提交PR, 由于社区大咖要求在OSD建立连接时,直接针对OSD修改,而不是放在一个 公共模块去修改,Simle msg 和async模块,还不熟悉,我贴个链接,有兴趣的可以尝试去修改:

https://github.com/ceph/ceph/pull/21348

更新:

新的patch,该问题已合入主线:

https://github.com/ceph/ceph/pull/23306