天天看點

記一次資料恢複過程中,踩到osd由于快照資料不完整導緻無法啟動的bug

  在一次叢集資料恢複過程中遇到的,最終導緻osd無法全部啟動,資料恢複失敗

踩坑環境背景

  3osd,兩副本pool,三個osd同時離線,且有一個osd無法啟動,已經認為out和rm了,需要恢複資料(原叢集可能不是active+clean狀态,和兩副本叢集,壞一個副本的情況不一樣,可能認為因素導緻某些資料沒恢複完就删掉了主pg所在osd)

  • 無曆史pgmap記錄(由于沒有曆史pg map資訊,pg整正常狀态和原本主pg所在osd資訊無法擷取)
  • 異常osd已經auth del,和rm過,外加硬體錯誤,導緻該osd已無法使用ceph-objectstore-tool進行pg export-remove和import等操作

  是以隻有盡可能從另外兩個osd中盡量挽回資料。

恢複過程

  取一個卡在down狀态pg,在兩個osd進行比對後,使用ceph-objectstore-tool在其中一個osd上mark 該pg complete,并拉起osd,pg從down狀态進入active+degrade+undersize+backfilling等狀态,最終可以進入active+clean狀态。

  後續pg如法炮制,最終所有pg全部從down狀态進入active狀态,還需要等待backfill恢複。

  在恢複過程中,原本标記為完整副本的osd突然停止(如果是服務的話,應該是反複重新開機)。報錯堆棧如下

1: (SnapSet::get_clone_bytes(snapid_t) const+0xb6) [0x707b46]#012 
2: (ReplicatedPG::_scrub(ScrubMap&)+0x9e8) [0x7c0198]#012 
3: (PG::scrub_compare_maps()+0x5b6) [0x755306]#012 
4: (PG::chunky_scrub(ThreadPool::TPHandle&)+0x1d9) [0x758999]#012 
5: (PG::scrub(ThreadPool::TPHandle&)+0x19a) [0x75b96a]#012 
6: (OSD::ScrubWQ::_process(PG*, ThreadPool::TPHandle&)+0x19) [0x657309]#012 
7: (ThreadPool::worker(ThreadPool::WorkThread*)+0xaf1) [0xa56dd1]#012 
8: (ThreadPool::WorkThread::entry()+0x10) [0xa57cc0]#012 
9: (()+0x8182) [0x7f579ce6e182]#012 
10: (clone()+0x6d) [0x7f579b5e0fbd]
           

  檢視日志,指向osd_types.cc中如下代碼段。

oid SnapSet::from_snap_set(const librados::snap_set_t& ss, bool legacy)
{
  // NOTE: our reconstruction of snaps (and the snapc) is not strictly
  // correct: it will not include snaps that still logically exist
  // but for which there was no clone that is defined.  For all
  // practical purposes this doesn't matter, since we only use that
  // information to clone on the OSD, and we have already moved
  // forward past that part of the object history.

  seq = ss.seq;
  set<snapid_t> _snaps;
  set<snapid_t> _clones;
  for (vector<librados::clone_info_t>::const_iterator p = ss.clones.begin();
       p != ss.clones.end();
       ++p) {
    if (p->cloneid != librados::SNAP_HEAD) {
      _clones.insert(p->cloneid);
      _snaps.insert(p->snaps.begin(), p->snaps.end());
      clone_size[p->cloneid] = p->size;
      clone_overlap[p->cloneid];  // the entry must exist, even if it's empty.
      for (vector<pair<uint64_t, uint64_t> >::const_iterator q =
	     p->overlap.begin(); q != p->overlap.end(); ++q)
	clone_overlap[p->cloneid].insert(q->first, q->second);
      if (!legacy) {
	// p->snaps is ascending; clone_snaps is descending
	vector<snapid_t>& v = clone_snaps[p->cloneid];
	for (auto q = p->snaps.rbegin(); q != p->snaps.rend(); ++q) {
	  v.push_back(*q);
	}
      }
    }
  }

  // ascending
  clones.clear();
  clones.reserve(_clones.size());
  for (set<snapid_t>::iterator p = _clones.begin(); p != _clones.end(); ++p)
    clones.push_back(*p);

  // descending
  snaps.clear();
  snaps.reserve(_snaps.size());
  for (set<snapid_t>::reverse_iterator p = _snaps.rbegin();
       p != _snaps.rend(); ++p)
    snaps.push_back(*p);
}
           

  根據代碼中clone_overlap[p->cloneid];與q != p->snaps.rend(),某些clone條目已經不存在或者不完整時,此處就會出現問題。

  redhat中相關連結

Bug 1273127 - Backport tracker 12738 - OSD reboots every few minutes with FAILED assert(clone_size.count(clone))

  文中提到,複現此bug需要1.某種特定方式損壞快照集 2.正好觸發損壞部分的校驗恢複。實際上該osd可以單獨啟動,隻有啟動另一個osd需要進行pg的backfilling時才會報錯,情況類似。

記一次資料恢複過程中,踩到osd由于快照資料不完整導緻無法啟動的bug

可能的規避或者恢複手段

  本身是小機率bug,特定情況下損壞,此處pg所有osd同時離線且出現系統問題的幾率還是較小,人為因素較多,如果有曆史pgmap,已經保留原本叢集内所有osd,恢複難度應該小很多。與項目相關人員溝通,叢集可能之前很多pg就是單副本狀态運作,隻存在于個别osd上,未能進入clean的ok狀态。運維時發現叢集有降級或異常,需立即處理,避免僥幸心理。

  網上提供的方法(指clear-snapset clone_size方式),本身隻适合少量snap丢失部分内容的場景,而該叢集很多snap已經擷取不到,丢失程度嚴重,無法完成最終資料恢複。即使進行了全部的有損恢複,對塊存儲以及快照,虛拟機等業務基本也是緻命的,部分資料損壞對于這些業務來講,可能一小塊影響整段資料無法使用,與rgw可能隻是影響部分對象通路的情況不同。

繼續閱讀