راهنمای جامع Ceph
Englishشروع کنید
در این صفحه
فصل ۱۷

عیب‌یابی ‏Ceph‏: از ‏HEALTH_WARN‏ تا ماشین حالت ‏Peering‏

۳۹ دقیقه مطالعهویرایش این صفحه
حوصله ندارم این فصل در چند خط
  • اول وضعیت را ثبت کنید (‏ceph -s‏، ‏ceph health detail‏، ‏ceph osd tree‏ و ‏ceph pg dump_stuck‏)، سپس از ‏MON‏ها به ‏OSD‏ها و بعد ‏PG‏ها بروید.
  • ‏MON‏ بدون ‏quorum‏ هیچ کاری نمی‌کند؛ پرچم‌هایی مثل ‏noout‏ و ‏nodown‏ ابزار موقت‌اند و باید بعداً برداشته شوند.
  • ‏ceph pg <pgid> query‏ مسیر ‏PG‏ در ماشین حالت ‏peering‏ و چیزی را که مانع آن است نشان می‌دهد.
  • ‏slow op‏ها را با ‏dump_ops_in_flight‏ و ‏dump_historic_ops‏ دنبال کنید؛ ‏ceph-objectstore-tool‏ و فرمان‌های اشیای ‏unfound‏ را فقط با احتیاط و پس از پشتیبان‌گیری به کار ببرید.

۱۷-۱ پیش از شروع#

۱۷-۱-۱ این فصل به چه سؤال‌هایی جواب می‌دهد؟#

عیب‌یابی ‏Ceph‏ در لحظه‌ی حادثه بیشتر از هر چیز به روش نیاز دارد تا حافظه: اینکه از کجا شروع کنیم، کدام فرمان کدام لایه را نشان می‌دهد و کی باید دست نگه داشت. این فصل یک روش منظم ارائه می‌دهد و سپس به درون کد می‌رود تا ببینیم وضعیت‌هایی مثل ‏down‏، ‏incomplete‏ یا ‏peering‏ دقیقاً از کجای ماشین حالت ‏PG‏ می‌آیند.

  • روش گام‌به‌گام عیب‌یابی از ‏ceph -s‏ تا ریشه‌ی مشکل چیست؟
  • مشکلات ‏MON‏، ‏OSD‏ و شبکه را چگونه جدا کنیم؟
  • وضعیت‌های ‏PG‏ چه معنایی دارند و ماشین حالت ‏Peering‏ چگونه کار می‌کند؟
  • ‏slow ops‏ را چگونه تا رویداد دقیق در مسیر عملیات دنبال کنیم؟
  • ‏scrub‏ چگونه ناسازگاری را پیدا می‌کند و تعمیر کی بی‌خطر است؟
  • کدام فرمان‌ها خطرناکاند و فقط در آخرین مرحله به کار می‌روند؟

۱۷-۲ روش عیب‌یابی#

شکل ۱۷-۱: روش عیب‌یابی لایه‌ای: از خلاصه‌ی ceph - s و ceph health detail شروع کنید، لایه‌ی درگیر ( MON ، OSD ، PG یا کارایی) را تشخیص دهید، با فرمان‌های همان لایه شواهد جمع کنید و سپس با چرخه‌ی فرضیه، آزمون و اصلاح کوچک پیش بروید و همه‌چیز را در لاگ حادثه ثبت کنید
شکل ۱۷-۱: روش عیب‌یابی لایه‌ای: از خلاصه‌ی ‏ceph‏ -‏s‏ و ‏ceph health detail‏ شروع کنید، لایه‌ی درگیر (‏MON‏، ‏OSD‏، ‏PG‏ یا کارایی) را تشخیص دهید، با فرمان‌های همان لایه شواهد جمع کنید و سپس با چرخه‌ی فرضیه، آزمون و اصلاح کوچک پیش بروید و همه‌چیز را در لاگ حادثه ثبت کنید
bashReef+
ceph -s                           # نمای کلی: health، mon، osd، pgs، io
ceph health detail                # کد هر هشدار با جزئیات
ceph health detail --format json-pretty | jq '.checks | keys'
ceph versions                     # ناهمگونی نسخه پس از ارتقای ناتمام
ceph crash ls-new                 # crashهای تازه (RECENT_CRASH)
ceph log last 100                 # log کلاستر در MON
💡 نکته

پیش از هر اقدام، وضعیت را ثبت کنید: خروجی ‏ceph -s‏، ‏ceph health detail‏، ‏ceph osd tree‏ و ‏ceph pg dump_stuck‏ را در فایلی با زمان ذخیره کنید. در بازبینی پس از حادثه، این داده‌ها از همه چیز ارزشمندترند.

⚠ هشدار

در لحظه‌ی بحران وسوسه‌ی «یک کاری بکن» شدید است. ‏Ceph‏ در بیشتر موارد خودش را ترمیم می‌کند (‏recovery‏ و ‏backfill‏). مداخله‌ی شتاب‌زده، مثل ‏ceph osd out‏ گروهی یا راه‌اندازی دوباره‌ی همه‌ی ‏OSD‏ها، معمولاً وضعیت را بدتر می‌کند. قاعده: یک تغییر در هر زمان، و مشاهده‌ی اثر آن.

۱۷-۲-۱ هشدارهای پرتکرار#

کد سلامتمعنای کوتاهنخستین فرمان
‏OSD_DOWN‏یک یا چند ‏OSD‏ پایین‌اند‏ceph osd tree down‏
‏PG_AVAILABILITY‏‏PG‏هایی غیرفعال‌اند و ‏I/O‏ ندارند‏ceph pg dump_stuck inactive‏
‏PG_DEGRADED‏نسخه‌های کمتر از ‏size‏‏ceph pg dump_stuck degraded‏
‏SLOW_OPS‏عملیات بیش از ‏osd_op_complaint_time‏ در جریان‏ceph daemon osd.N dump_historic_slow_ops‏
‏OSD_NEARFULL‏ / ‏OSD_FULL‏پر شدن ‏OSD‏‏ceph osd df tree‏
‏OSD_SCRUB_ERRORS‏ / ‏PG_DAMAGED‏ناسازگاری یافته‌شده در ‏scrub‏‏rados list-inconsistent-obj PGID‏
‏OBJECT_UNFOUND‏نسخه‌ی معتبر شیء پیدا نمی‌شود‏ceph pg PGID list_unfound‏
‏MON_CLOCK_SKEW‏اختلاف ساعت ‏MON‏ها‏chronyc tracking‏
‏LARGE_OMAP_OBJECTS‏شیء با ‏omap‏ بسیار بزرگ‏ceph log last‏ و ‏grep Large omap‏
‏BLUEFS_SPILLOVER‏‏RocksDB‏ از ‏block.db‏ به دیسک کند سرریز کرده‏ceph daemon osd.N bluefs stats‏
‏BLUESTORE_SLOW_OP_ALERT‏عملیات کند در لایه‌ی ‏BlueStore‏‏ceph health detail‏
‏RECENT_CRASH‏‏crash‏ تازه‌ی ‏daemon‏‏ceph crash info ID‏
✎ یادداشت

همه‌ی کدهای جدول بالا در صفحه‌ی ‏Health Checks‏ مستندات ‏latest‏ ثبت شده‌اند. پیوست «کتاب‌چه‌ی عیب‌یابی» فهرست کامل‌تر را دارد.

۱۷-۳ مشکلات ‏MON‏#

  • از دست رفتن ‏quorum‏: هیچ فرمانی پاسخ نمی‌دهد. روی هر میزبان ‏MON‏ با سوکت مدیریت بررسی کنید: ‏ceph daemon mon.$(hostname -s) mon_status‏ (در ‏cephadm‏: ‏cephadm shell --name mon.<host>‏ و سپس همان فرمان). فیلدهای ‏state‏، ‏quorum‏ و ‏outside_quorum‏ را ببینید.
  • ‏clock skew‏: ‏Paxos‏ به ‏lease‏ زمانی وابسته است؛ ‏chronyc sources‏ و ‏chronyc tracking‏ روی همه‌ی ‏MON‏ها.
  • رشد ‏store.db‏: در دوره‌های طولانی ناسالم، ‏MON‏ نقشه‌های قدیمی را ‏trim‏ نمی‌کند. ‏ceph tell mon.* compact‏ و رفع علت ناسالمی.
◆ عمیق‌تر

چرا ‏MON‏ بدون ‏quorum‏ هیچ کاری نمی‌کند؟ هر تغییر نقشه یک پیشنهاد ‏Paxos‏ است که باید به تأیید اکثریت برسد (‏src/mon/Paxos.cc‏). حتی خواندن‌ها از ‏MON‏ی که ‏lease‏ معتبر ندارد سرویس داده نمی‌شوند تا کلاینت نقشه‌ی کهنه نگیرد. پس با سه ‏MON‏، از دست رفتن دو ‏MON‏ یعنی توقف کامل مدیریت کلاستر، هرچند ‏OSD‏ها با آخرین نقشه به سرویس ‏I/O‏ ادامه می‌دهند تا وقتی که به تغییر نقشه نیاز باشد.

۱۷-۴ مشکلات ‏OSD‏#

bashReef+
ceph osd tree down
ceph osd find 12                         # میزبان و مسیر
cephadm logs --name osd.12 -- -n 200     # روی میزبان
ceph crash ls | grep osd.12
ceph daemon osd.12 status                # اگر بالا است
ceph osd dump | grep -E "^osd.12 "
نشانهعلت محتملاقدام
‏OSD‏ مدام ‏down/up‏ می‌شود (‏flapping‏)شبکه‌ی ‏cluster‏، ‏heartbeat‏، بار سنگینبررسی شبکه، ‏ceph osd set nodown‏ فقط موقت
‏OSD‏ بالا نمی‌آید با خطای ‏BlueStore‏خرابی دیسک یا فراداده‏ceph-bluestore-tool fsck‏، بررسی ‏SMART‏
‏OSD‏ با ‏OOM‏ کشته می‌شود‏osd_memory_target‏ بیش از ‏RAM‏کاهش هدف حافظه، بررسی ‏cache‏
‏OSD‏ «‏wrongly marked me down‏»تأخیر ‏heartbeat‏شبکه، ‏osd_heartbeat_grace‏ (پیش‌فرض ۲۰ ثانیه)
⚠ هشدار

‏nodown‏ و ‏noout‏ ابزارهای موقت‌اند. ‏nodown‏ روی ‏OSD‏ واقعاً مرده باعث می‌شود کلاینت‌ها منتظر ‏OSD‏ی بمانند که هرگز پاسخ نمی‌دهد. پس از رفع مشکل حتماً ‏ceph osd unset‏ کنید؛ هشدار ‏OSDMAP_FLAGS‏ یادآور شماست. ‏OSD‏ی که ‏down‏ است پس از ‏mon_osd_down_out_interval‏ (پیش‌فرض ۱۰ دقیقه) به‌طور خودکار ‏out‏ می‌شود و ‏backfill‏ آغاز می‌شود.

۱۷-۵ وضعیت‌های ‏PG‏#

وضعیتمعنانگرانی
‏active+clean‏سالم: همه‌ی نسخه‌ها هم‌گامندارد
‏peering‏‏OSD‏ها در حال توافق بر سر تاریخچه‌ی ‏PG‏اگر طولانی شود
‏activating‏‏peering‏ تمام شده، در حال فعال‌سازیمعمولاً کوتاه
‏degraded‏برخی اشیا نسخه‌ی کمتر از ‏size‏ دارندمتوسط
‏undersized‏تعداد ‏OSD‏های ‏acting‏ کمتر از ‏size‏متوسط
‏remapped‏‏acting‏ با ‏up‏ متفاوت است (‏pg_temp‏)معمولاً گذرا
‏backfill_wait‏ / ‏backfilling‏انتظار برای رزرو یا کپی کامل ‏PG‏گذرا
‏recovery_wait‏ / ‏recovering‏انتظار یا بازیابی بر پایه‌ی ‏log‏گذرا
‏down‏‏OSD‏های لازم برای ‏peering‏ در دسترس نیستندبالا: ‏I/O‏ متوقف
‏incomplete‏هیچ ‏OSD‏ی تاریخچه‌ی کافی نداردبالا
‏stale‏‏MON‏ مدتی از ‏primary‏ خبری نگرفتهبالا
‏inconsistent‏‏scrub‏ ناسازگاری یافتهبالا
‏recovery_unfound‏ / ‏backfill_unfound‏شیء بدون نسخه‌ی معتبربحرانی
bashReef+
ceph pg stat
ceph pg dump_stuck inactive
ceph pg dump_stuck unclean
ceph pg dump_stuck stale
ceph pg ls down
ceph pg 2.1f query | jq '.state, .recovery_state[0]'
💡 نکته

خروجی ‏ceph pg <pgid> query‏ بخشی به نام ‏recovery_state‏ دارد که مسیر ماشین حالت ‏PG‏ را نشان می‌دهد؛ مثلاً ‏Started/Primary/Peering/GetInfo‏ یا ‏Started/Primary/Peering/Down‏ همراه با ‏blocked‏ و ‏down_osds_we_would_probe‏. این همان نام‌هایی است که در ‏PeeringState.h‏ تعریف شده‌اند.

۱۷-۶ از درون کد: ماشین حالت ‏Peering‏#

۱۷-۶-۱ ساختار سلسله‌مراتبی#

‏src/osd/PeeringState.h‏ ماشین حالت ‏PG‏ را با ‏boost::statechart‏ تعریف می‌کند. هر حالت یک ‏struct‏ است که والدش را مشخص می‌کند؛ مثلاً ‏struct GetInfo : boost::statechart::state<GetInfo, Peering>‏. بنابراین نام کامل حالت یک مسیر است: ‏Started/Primary/Peering/GetInfo‏. رویدادها (مانند ‏AdvMap‏ برای نقشه‌ی جدید، ‏MNotifyRec‏ برای دریافت اطلاعات از ‏OSD‏ دیگر، ‏GotLog‏، ‏Activate‏) بین حالت‌ها جابه‌جا می‌کنند.

شکل ۱۷-۲: ماشین حالت Peering در سه ردیف: مسیر اصلی GetInfo ، GetLog ، GetMissing و WaitUpThru زیر Started/Primary/Peering ؛ شاخه‌های شکست Down ، Incomplete و WaitActingChange ؛ و پس از فعال‌سازی، مسیر Active شامل رزرو و Recovering ، رزرو و Backfilling ، و در نهایت Recovered و Clean
شکل ۱۷-۲: ماشین حالت ‏Peering‏ در سه ردیف: مسیر اصلی ‏GetInfo‏، ‏GetLog‏، ‏GetMissing‏ و ‏WaitUpThru‏ زیر ‏Started/Primary/Peering‏؛ شاخه‌های شکست ‏Down‏، ‏Incomplete‏ و ‏WaitActingChange‏؛ و پس از فعال‌سازی، مسیر ‏Active‏ شامل رزرو و ‏Recovering‏، رزرو و ‏Backfilling‏، و در نهایت ‏Recovered‏ و ‏Clean‏
حالتکار
‏GetInfo‏از همه‌ی ‏OSD‏های ‏prior set‏ (OSDهایی که ممکن است از آخرین ‏interval‏ فعال، نوشتن داشته باشند) ‏pg_info‏ می‌خواهد
‏GetLog‏با ‏choose_acting‏ بهترین ‏log‏ را انتخاب و در صورت نیاز از آن ‏OSD‏ می‌گیرد
‏GetMissing‏‏log‏ هر ‏replica‏ را با ‏log‏ معتبر مقایسه و فهرست ‏missing‏ را می‌سازد
‏WaitUpThru‏منتظر می‌ماند تا ‏MON‏ ثبت ‏up_thru‏ این ‏OSD‏ را در نقشه منتشر کند
‏Down‏اطلاعات کافی از ‏prior set‏ نیست؛ منتظر بازگشت ‏OSD‏ها
‏Incomplete‏‏log‏ کافی برای ادامه نیست؛ نیاز به مداخله‌ی دستی
‏WaitActingChange‏‏primary‏ درخواست ‏pg_temp‏ داده و منتظر نقشه‌ی جدید است

۱۷-۶-۲ ‏prior set‏ و ‏up_thru‏: چرا ‏PG‏ «‏down‏» می‌شود#

مسئله‌ی اصلی ‏peering‏ این است: آیا ممکن است نوشتنی تأییدشده وجود داشته باشد که ما از آن خبر نداریم؟ هر بار که ‏acting set‏ عوض می‌شود، یک ‏interval‏ جدید آغاز می‌شود. ‏PG‏ برای هر ‏interval‏ گذشته از زمان آخرین ‏active‏ شدن، بررسی می‌کند آیا در آن ‏interval‏ ممکن بوده ‏PG‏ فعال شده و نوشتن پذیرفته باشد. این «ممکن بودن» با ‏up_thru‏ سنجیده می‌شود: ‏primary‏ پیش از ‏active‏ شدن باید منتظر بماند تا ‏MON‏ در ‏OSDMap‏ ثبت کند که این ‏OSD‏ تا این ‏epoch‏ زنده بوده است (حالت ‏WaitUpThru‏). اگر ‏interval‏ی وجود داشته باشد که در آن ‏up_thru‏ ثبت شده (پس شاید نوشتن رخ داده) و هیچ‌یک از ‏OSD‏های آن ‏interval‏ اکنون در دسترس نباشند، ‏PG‏ نمی‌تواند مطمئن شود و به ‏Down‏ می‌رود.

text
pseudocode: ساختن prior set (خلاصه‌ی این کتاب از PastIntervals در osd_types)
  for interval in past_intervals since last_epoch_started:
      if not interval.maybe_went_rw: continue        # up_thru ثبت نشده → نوشتن ممکن نبوده
      up_now = [osd for osd in interval.acting if osd is up]
      # تکثیری: اگر هیچ OSD زنده‌ای از آن interval نمانده باشد → down
      if not recoverable(up_now):             pg_down = true            # EC: کمتر از k shard (ساده‌شده؛ predicate واقعی IsPGRecoverablePredicate)
      probe += up_now
  return (probe, pg_down, down_osds_we_would_probe)
◆ عمیق‌تر

به همین دلیل خروجی ‏pg query‏ در وضعیت ‏down‏ فیلد ‏down_osds_we_would_probe‏ را نشان می‌دهد: ‏OSD‏هایی که اگر برگردند، ‏PG‏ می‌تواند ‏peering‏ را کامل کند. راه درست تقریباً همیشه برگرداندن همان ‏OSD‏ها است، نه علامت زدن آن‌ها به عنوان ‏lost‏. فرمان ‏ceph osd lost‏ به ‏PG‏ اجازه می‌دهد بدون آن ‏OSD‏ ادامه دهد، اما هر نوشتن تأییدشده‌ای که فقط روی آن ‏OSD‏ بوده برای همیشه از دست می‌رود.

۱۷-۶-۳ ‏find_best_info‏: کدام ‏log‏ معتبر است؟#

در ‏GetLog‏، تابع ‏PeeringState::find_best_info()‏ از میان ‏pg_info‏های دریافت‌شده بهترین را انتخاب می‌کند. منطق آن (بازنویسی این کتاب):

text
pseudocode: find_best_info(infos)
  (max_les, min_lu) = calculate_maxles_and_minlua(infos)   # بیشینه‌ی last_epoch_started
  if min_lu == max: return none                              # هیچ نامزد قابل قبولی نیست
  best = none
  for (shard, info) in infos:
      skip if info.last_update < min_lu
      skip if info.last_epoch_started < max_les
      skip if info.is_incomplete()
      if best is none: best = info; continue
      if pool.require_rollback():          # EC: کمترین last_update ایمن است (rollback ممکن)
          prefer smaller last_update
      else:                                # تکثیری: بیشترین last_update
          prefer larger last_update
      then prefer smaller log_tail          # log بلندتر → recovery بر پایه‌ی log برای بیشتر replicaها
      then prefer complete (no missing)
      then prefer current primary / up set
  return best
✎ یادداشت

تفاوت تکثیری و ‏EC‏ آموزنده است: در ‏pool‏ تکثیری، ‏replica‏ با جدیدترین نوشتن مرجع می‌شود و بقیه با ‏log‏ به آن می‌رسند. در ‏EC‏، نوشتن ناقص روی چند ‏shard‏ قابل بازسازی نیست؛ بنابراین ‏Ceph‏ نسخه‌ای را مرجع می‌گیرد که همه‌ی ‏shard‏ها بتوانند به آن برگردند (‏rollback‏). این همان دلیلی است که ‏EC‏ به ‏rollback info‏ در ‏log‏ نیاز دارد (‏require_rollback()‏).

⚠ هشدار

گزینه‌ی ‏osd_find_best_info_ignore_history_les‏ (که در ‏PeeringState.cc‏ بررسی می‌شود) فقط برای بازیابی اضطراری ‏PG‏های ‏incomplete‏ است و می‌تواند داده‌ی تأییدشده را دور بریزد. آن را فقط با درک کامل، روی یک ‏OSD‏، به‌طور موقت و ترجیحاً با راهنمایی متخصص به کار ببرید و بلافاصله برگردانید.

۱۷-۷ سه الگوریتم عیب‌یابی با عدد#

عیب‌یاب خوب باید بداند خروجی‌هایی مثل «‏PG down‏ است»، «‏slow request 61 seconds old‏» یا «‏deep scrub‏ عقب افتاده» دقیقاً از کدام محاسبه بیرون می‌آیند. این بخش سه محاسبه را با داده‌ی نمونه دنبال می‌کند.

۱۷-۷-۱ کدام ‏OSD log‏ معتبر را دارد؟#

∑ الگوریتم: انتخاب ‏info‏ معتبر در ‏peering‏

منبع در کد: src/osd/PeeringState.cc :: PeeringState::calculate_maxles_and_minlua، PeeringState::find_best_info

پیچیدگی: ‏O‏(R) برای ‏R info‏ دریافتی، دو گذر خطی

text
pseudocode
max_les = 0
for info: max_les = max(max_les, info.history.les)            # مگر osd_find_best_info_ignore_history_les
          if !incomplete: max_les = max(max_les, info.les)
min_lua = min(info.last_update | info.les ≥ max_les)          # اگر هیچ: PG بدون info معتبر
best = ∅
for info (فقط up/acting اگر restrict):
    رد شو اگر lu < min_lua یا les < max_les یا incomplete
    replicated: lu بزرگ‌تر بهتر ؛ EC (require_rollback): lu کوچک‌تر بهتر
    در تساوی: log_tail کوچک‌تر (log بلندتر) ؛ سپس بدون missing ؛ سپس primary فعلی

مثال عددی. یک ‏PG replicated‏ چهار ‏info‏ می‌گیرد (‏eversion‏ به شکل ‏epoch'version‏):

‏OSD‏‏les‏‏last_update‏‏log_tail‏
‏osd.1‏ (primary)۴۰۴۱'۱۲۰۴۱'۲۰
‏osd.4‏۴۰۴۱'۱۱۸۴۰'۱۰
‏osd.7‏۳۸۳۸'۳۰۰۳۷'۵۰
‏osd.9‏۴۰۴۱'۱۲۰۴۰'۹۰

مراحل انتخاب:

  1. ۱. ‏max_les‏ = ۴۰.
  2. ۲. ‏min_lua‏ از میان ‏les‏ ≥ ۴۰ برابر ۴۱'۱۱۸ است. مقایسه‌ی ‏eversion‏ نخست ‏epoch‏ را می‌سنجد، پس ۳۸'۳۰۰ از ۴۱'۱۱۸ کوچک‌تر است، هرچند ‏version‏ آن بزرگ‌تر است.
  3. ۳. ‏osd.7‏ به خاطر ‏les 38‏ حذف می‌شود.
  4. ۴. از میان سه نامزد باقی‌مانده، ‏osd.1‏ و ‏osd.9‏ جدیدترین ‏last_update‏ را دارند.
  5. ۵. ‏tail‏ کوچک‌تر، یعنی ۴۰'۹۰ < ۴۱'۲۰، برنده است، پس ‏osd.9‏ مرجع می‌شود، نه ‏primary‏.

‏primary‏ پس از این مرحله ‏log‏ معتبر را از ‏osd.9‏ می‌گیرد (‏GetLog‏). اگر همه‌ی ‏OSD‏های ‏les‏ = ۴۰ از کار افتاده باشند، ‏min_lua‏ تعریف‌نشده می‌ماند و ‏PG‏ در حالت ‏down/incomplete‏ گیر می‌کند. این همان جایی است که ‏ceph pg query‏ در بخش ‏recovery_state‏ فهرست ‏down_osds_we_would_probe‏ را نشان می‌دهد.

شکل ۱۷-۳: اجرای find_best_info روی چهار info نمونه. ردیف قرمز به خاطر last_epoch_started قدیمی‌تر حذف می‌شود. از میان دو ردیف با جدیدترین last_update ، آنکه log_tail کوچک‌تر و در نتیجه log بلندتری دارد (سبز) برنده است. ترجیح primary فعلی فقط وقتی اعمال می‌شود که همه‌ی معیارهای قبلی برابر باشند. در pool EC جهت مقایسه‌ی last_update برعکس است، چون نسخه‌های جلوتر باید rollback شوند
شکل ۱۷-۳: اجرای ‏find_best_info‏ روی چهار ‏info‏ نمونه. ردیف قرمز به خاطر ‏last_epoch_started‏ قدیمی‌تر حذف می‌شود. از میان دو ردیف با جدیدترین ‏last_update‏، آنکه ‏log_tail‏ کوچک‌تر و در نتیجه ‏log‏ بلندتری دارد (سبز) برنده است. ترجیح ‏primary‏ فعلی فقط وقتی اعمال می‌شود که همه‌ی معیارهای قبلی برابر باشند. در ‏pool EC‏ جهت مقایسه‌ی ‏last_update‏ برعکس است، چون نسخه‌های جلوتر باید ‏rollback‏ شوند

۱۷-۷-۲ زمان‌بندی هشدار ‏slow request‏#

∑ الگوریتم: شمارش و گزارش عملیات کند با عقب‌نشینی نمایی

منبع در کد: src/common/TrackedOp.cc :: OpTracker::with_slow_ops_in_flight، OpTracker::check_ops_in_flight، visit_ops_in_flight

پیچیدگی: ‏O‏(تعداد ‏op‏ در حال اجرا) در هر بررسی؛ ‏ops‏ هر شارد به ترتیب زمان آغاز پیمایش و در اولین ‏op‏ جوان متوقف می‌شود

text
pseudocode
too_old = now − osd_op_complaint_time (30 s)
visit ops by initiated (قدیمی به جدید):
    if op.initiated ≥ too_old: stop                # بقیه جوان‌ترند
    if continuous یا multiplier == 0: skip
    slow++                                         # در SLOW_OPS و health شمرده می‌شود
    if warned ≥ osd_op_log_threshold (5): continue # فقط شمردن، نه لاگ
    if op.initiated + 30 × multiplier ≥ now: continue
    warned++ ; log "slow request …" ; multiplier ×= 2

مثال عددی. یک ‏op‏ در ‏t‏ = ۰ گیر کرده است و ‏OSD‏ هر ثانیه بررسی می‌کند.

  • ‏t‏ = ۳۱ ‏s: op‏ کند است و ‏multiplier‏ = ۱، پس ۰ + ۳۰ < ۳۱ و لاگ می‌شود. ‏multiplier‏ به ۲ می‌رسد.
  • لاگ بعدی وقتی است که ۰ + ۶۰ < ‏now‏، یعنی ‏t‏ = ۶۱. پس از آن ‏t‏ = ۱۲۱ و ‏t‏ = ۲۴۱ (‏multiplier 4‏ و ۸).
  • در کل در ۴۰۰ ثانیه چهار خط لاگ برای این ‏op‏ نوشته می‌شود، اما در همه‌ی این مدت در شمارش ‏SLOW_OPS‏ هست.

اگر ۵۰ ‏op‏ هم‌زمان کند باشند، هر بررسی حداکثر ۵ خط لاگ می‌دهد، اما «۵۰ ‏slow requests‏» گزارش می‌شود. پس خلوت بودن لاگ به معنای کم بودن ‏op‏های کند نیست. برای دیدن همه، از ‏ceph daemon osd.N dump_ops_in_flight‏ و ‏dump_historic_slow_ops‏ استفاده کنید.

شکل ۱۷-۴: خط زمانی یک op که ۴۰۰ ثانیه گیر مانده است. از ثانیه‌ی ۳۰ به بعد در شمار op های کند است. اما خط لاگ « slow request » فقط وقتی نوشته می‌شود که سن op از ۳۰ × multiplier بگذرد و پس از هر لاگ ضریب دو برابر می‌شود. پس بازه‌ی لاگ‌ها ۳۰، ۶۰ و ۱۲۰ ثانیه است و لاگ OSD در یک رخداد طولانی سیل‌آسا نمی‌شود
شکل ۱۷-۴: خط زمانی یک ‏op‏ که ۴۰۰ ثانیه گیر مانده است. از ثانیه‌ی ۳۰ به بعد در شمار ‏op‏های کند است. اما خط لاگ «‏slow request‏» فقط وقتی نوشته می‌شود که سن ‏op‏ از ۳۰ × ‏multiplier‏ بگذرد و پس از هر لاگ ضریب دو برابر می‌شود. پس بازه‌ی لاگ‌ها ۳۰، ۶۰ و ۱۲۰ ثانیه است و لاگ ‏OSD‏ در یک رخداد طولانی سیل‌آسا نمی‌شود

۱۷-۷-۳ زمان ‏scrub‏ بعدی#

∑ الگوریتم: تصادفی‌سازی زمان ‏scrub‏ سطحی و عمیق

منبع در کد: src/osd/scrubber/scrub_job.cc :: ScrubJob::adjust_shallow_schedule، ScrubJob::adjust_deep_schedule، ScrubJob::guaranteed_offset؛ src/osd/scrubber/pg_scrubber.cc :: PgScrubber::populate_config_params

پیچیدگی: ‏O‏(۱) برای هر ‏PG‏ پس از هر ‏scrub‏

text
pseudocode
shallow (اگر فوری نیست): target = last + min_interval + min_interval × randomize_ratio × U[0,1]
                         not_before = max(last, target)
deep:  sdv = deep_interval × cv
       target = last + clamp(N(deep_interval, sdv), deep_interval − 2·sdv, deep_interval + 2·sdv)
فوری (operator/repair): زمان = scrub_must_stamp ، بدون تصادف
guaranteed_offset: shallow = min × (2 + ratio) ، deep = deep + 3·sdv + 10 s

مثال عددی (پیش‌فرض‌ها: ‏min 1‏ روز، ‏randomize 0.5‏، ‏deep 7‏ روز، ‏cv 0.2‏).

  • ‏scrub‏ سطحی: بعدی در بازه‌ی یکنواخت ۱ تا ۱.۵ روز پس از آخرین ‏scrub‏ است.
  • ‏scrub‏ عمیق: ‏sdv‏ = ۱.۴ روز و نمونه به بازه‌ی ۴.۲ تا ۹.۸ روز بریده می‌شود. در شبیه‌سازی ۱۰۰۰۰۰ نمونه، حدود ۶۸٪ در بازه‌ی ۵.۶ تا ۸.۴ روز (۰.۸ تا ۱.۲ برابر) افتادند و حدود ۲.۳٪ دقیقاً روی دو مرز قرار گرفتند.

متن ‏long_desc‏ گزینه‌ی ‏osd_deep_scrub_interval_cv‏ می‌گوید ۹۵٪ در [۰.۸، ۱.۲] هستند. اما با ‏cv‏ = ۰.۲ این بازه فقط یک انحراف معیار است. ۹۵٪ در ±۲σ، یعنی [۰.۶، ۱.۴] قرار می‌گیرد. این ناسازگاری در متن راهنمای گزینه است، نه در رفتار. برای کلاستری با ۴۰۹۶ ‏PG‏، این پخش‌شدگی یعنی به‌طور متوسط حدود ۵۸۵ ‏deep scrub‏ در روز (۴۰۹۶/۷)، بدون اینکه همه با هم در یک روز شروع شوند.

شکل ۱۷-۵: توزیع زمان scrub بعدی با پیش‌فرض‌ها، از شبیه‌سازی همان فرمول‌های کد. سمت چپ scrub سطحی است: توزیع یکنواخت در یک تا یک‌ونیم روز. سمت راست scrub عمیق است: توزیع نرمال حول هفت روز با انحراف معیار ۱.۴ روز که در ±۲σ بریده شده است. دو ستون بلند لبه‌ها، نمونه‌های بریده‌شده هستند. ناحیه‌ی زرد بازه‌ی ۰.۸ تا ۱.۲ برابر است
شکل ۱۷-۵: توزیع زمان ‏scrub‏ بعدی با پیش‌فرض‌ها، از شبیه‌سازی همان فرمول‌های کد. سمت چپ ‏scrub‏ سطحی است: توزیع یکنواخت در یک تا یک‌ونیم روز. سمت راست ‏scrub‏ عمیق است: توزیع نرمال حول هفت روز با انحراف معیار ۱.۴ روز که در ±۲σ بریده شده است. دو ستون بلند لبه‌ها، نمونه‌های بریده‌شده هستند. ناحیه‌ی زرد بازه‌ی ۰.۸ تا ۱.۲ برابر است
⚠ هشدار

وابسته به نسخه. گزینه‌ی ‏osd_deep_scrub_interval_cv‏ و زمان‌بندی نرمال ‏deep scrub‏ از ‏v19.2.x‏ (Squid) به بعد وجود دارد (در ‏v19.2.3‏، ‏v20.2.0‏ و ‏v21.2.0‏ بررسی شد). در ‏v18.2.4‏ (Reef) این گزینه وجود ندارد و، ‏deep scrub‏ با ‏osd_deep_scrub_randomize_ratio‏ و احتمال ارتقای ‏scrub‏ سطحی به عمیق تصادفی می‌شد. پیش از تنظیم، ‏ceph config help osd_deep_scrub_interval_cv‏ را روی نسخه‌ی خودتان بررسی کنید.

۱۷-۸ عیب‌یابی با داده‌ی اندازه‌گیری‌شده#

دو شکل این بخش از داده‌ی واقعی ساخته شده‌اند و حدس نیستند. اولی خط زمانی حالت ‏PG‏ها را روی یک کلاستر آزمایشی واقعی نشان می‌دهد. دومی اثر از دست رفتن میزبان را با شمارش دقیق روی نگاشت ‏CRUSH‏ نشان می‌دهد.

۱۷-۸-۱ خط زمانی حالت ‏PG‏ها#

آزمایش روی یک کلاستر سه‌‏OSD‏ی ‏BlueStore‏ با ‏Ceph 18.2.7‏ (بسته‌های ‏Debian‏) انجام شد. ‏pool‏ ‏‏t17‏ با ۳۲ ‏PG‏ و ‏size‏ = ۲ داشت و ۳۰۰ شیء ۴ ‏MiB‏ در آن نوشته شده بود. هر ثانیه ‏ceph pg dump pgs_brief -f json‏ گرفته و حالت‌ها شمرده شدند. چهار رخداد پشت سر هم اجرا شد: ‏ceph osd out 0‏، سپس ‏ceph osd in 0‏، سپس توقف فرایند ‏osd.1‏ و در پایان راه‌اندازی دوباره‌ی آن.

∑ الگوریتم: شمارش حالت ‏PG‏ها در طول یک رخداد

منبع در کد: ceph pg dump pgs_brief (PGMap در mgr)؛ رشته‌ی حالت از pg_state_string در src/osd/osd_types.cc

پیچیدگی: ‏O‏(pg_num) در هر نمونه

text
pseudocode
هر 1 ثانیه:
    pgs = ceph pg dump pgs_brief -f json
    count[state] += 1 برای هر pg                 # state مثل "active+recovery_wait+degraded"
    گروه‌بندی: clean ‏/ recovering ‏/ recovery_wait ‏/ peering ‏/ stale ‏/ undersized
رخداد بعدی فقط وقتی اجرا می‌شود که هر 32 PG دوباره active+clean باشند

آنچه دیده شد.

  • ‏out‏ کردن ‏osd.0‏: حدود ۱۶ ‏PG‏ به ‏peering‏ رفتند. سپس ‏PG‏ها به‌جای ‏backfill‏ در حالت ‏recovery_wait+degraded‏ و ‏recovering‏ دیده شدند. دلیلش این است که در این ‏pool‏ هر ‏PG‏ فقط حدود ۹ شیء دارد و ‏log‏ هر ‏PG‏، که دست‌کم ‏osd_min_pg_log_entries‏ = ۲۵۰ ورودی نگه می‌دارد، همه‌ی تغییرها را پوشش می‌دهد. پس ‏OSD‏ جدید از روی ‏log‏ بازیابی می‌شود. در کلاستر واقعی با هزاران شیء در هر ‏PG‏، همین رخداد ‏backfill_wait‏ و ‏backfilling‏ می‌سازد.
  • ‏in‏ کردن دوباره: ۲۲ ‏PG‏ چند ثانیه در ‏recovery_wait+degraded‏ ماندند و صف شدند. این همان سقف ‏osd_max_backfills‏ و ‏osd_recovery_max_active‏ زیر ‏mClock‏ است.
  • توقف ‏osd.1‏: چند ثانیه ‏stale‏ دیده شد، چون ‏mgr‏ هنوز گزارش تازه‌ای نداشت. سپس حدود ۶ ثانیه ‏peering‏ طول کشید و در پایان دقیقاً ۲۴ ‏PG‏، یعنی همه‌ی ‏PG‏هایی که ‏osd.1‏ در ‏up set‏ آن‌ها بود، ‏active+undersized+degraded‏ شدند. ‏I/O‏ ادامه یافت، چون ‏min_size‏ = ۱ بود. این تنظیم فقط برای آزمایش است و در محیط واقعی خطرناک است.
  • بالا آمدن ‏osd.1‏: ‏PG‏ها با ‏recovery‏ از ‏log‏ در چند ثانیه ‏clean‏ شدند. مکث نشان‌داده‌شده بیشتر تأخیر گزارش آمار است تا خود ‏recovery‏.
شکل ۱۷-۶: حالت ۳۲ PG در pool t17 (size ۲) روی کلاستر آزمایشی سه‌ OSD ی BlueStore با Ceph 18.2.7 ، نمونه‌برداری‌شده هر حدود یک ثانیه. خط‌چین‌ها رخدادها را نشان می‌دهند: out و in کردن osd.0 ، و توقف و راه‌اندازی دوباره‌ی osd.1 . سبز active+clean است، آبی recovery ، زرد peering ، خاکستری stale و قرمز undersized+degraded
شکل ۱۷-۶: حالت ۳۲ ‏PG‏ در ‏pool‏ ‏‏t17‏ (size ۲) روی کلاستر آزمایشی سه‌‏OSD‏ی ‏BlueStore‏ با ‏Ceph 18.2.7‏، نمونه‌برداری‌شده هر حدود یک ثانیه. خط‌چین‌ها رخدادها را نشان می‌دهند: ‏out‏ و ‏in‏ کردن ‏osd.0‏، و توقف و راه‌اندازی دوباره‌ی ‏osd.1‏. سبز ‏active+clean‏ است، آبی ‏recovery‏، زرد ‏peering‏، خاکستری ‏stale‏ و قرمز ‏undersized+degraded‏

۱۷-۸-۲ از دست رفتن یک، دو یا سه میزبان#

∑ الگوریتم: اثر از دست رفتن ‏k‏ میزبان پیش از ‏out‏ شدن ‏OSD‏ها

منبع در کد: نگاشت up از osdmaptool --test-map-pgs-dump؛ معیار min_size در PeeringState (فصل 17)

پیچیدگی: ‏O(C‏(H,‏k‏) × ‏pg_num‏ × ‏size‏)

text
pseudocode
برای هر ترکیب F از k میزبان (همه‌ی C(10, k) ترکیب):
    برای هر PG: n = تعداد replicaهای روی میزبان‌های F
        degraded اگر n ≥ 1 ؛ inactive اگر size − n < min_size ؛ lost اگر n = size
میانگین، کمینه و بیشینه روی ترکیب‌ها
P(کلاینتی که N شیء را لمس می‌کند گیر کند) = 1 − (1 − p_inactive)^N      # شیء→PG با hash یکنواخت

نتیجه (۱۲۰ ‏OSD‏، ‏size‏ = ۳، ‏min_size‏ = ۲، ۸۱۹۲ ‏PG‏).

میزبان‌های ازکارافتاده‏PG‏ ‏‏degraded‏‏PG‏ ‏‏inactive‏‏PG‏ بی‌نسخه
۱۳۰.۰۰٪۰۰
۲۵۳.۳۳٪۶.۶۷٪ (۵۴۶ ‏PG‏، بازه‌ی ۵۰۹ تا ۵۹۲)۰
۳۷۰.۸۳٪۱۸.۳۳٪۰.۸۳٪ (۶۸ ‏PG‏)

میانگین‌ها با فرمول بسته می‌خوانند. برای یک میزبان ۳/۱۰ است. برای ‏PG‏های ‏inactive‏ با دو میزبان ‏C‏(۸,۱)/‏C‏(۱۰,۳) = ۸/۱۲۰ است. برای ‏PG‏های بی‌نسخه با سه میزبان ۱/‏C‏(۱۰,۳) = ۱/۱۲۰ است.

پیام عملی در ستون ‏inactive‏ است. با دو میزبان ازکارافتاده فقط ۶.۶۷٪ ‏PG‏ها ‏I/O‏ را متوقف می‌کنند. اما یک ‏image‏ ‏‏RBD‏ یک‌گیگابایتی (۲۵۶ شیء) با احتمال ‏1−0.9333256≈99.999998%1 - 0.9333^{256} \approx 99.999998\%‏ دست‌کم یک شیء روی آن‌ها دارد. حتی کلاینتی که فقط ۱۰ شیء را لمس می‌کند در ۵۰٪ موارد گیر می‌کند. پس «فقط ۷٪ ‏PG‏ها» در عمل یعنی تقریباً همه‌ی ماشین‌های مجازی متوقف شده‌اند.

تا وقتی ‏OSD‏ها ‏out‏ نشده‌اند (‏mon_osd_down_out_interval‏، فصل ۷)، ‏CRUSH‏ جای تازه‌ای انتخاب نمی‌کند و این وضعیت ادامه دارد. اگر دو ‏failure domain‏ هم‌زمان در خطرند، چاره بالا بردن ‏size‏ یا انتخاب ‏failure domain‏ بزرگ‌تر است، نه پایین آوردن ‏min_size‏.

شکل ۱۷-۷: چپ: درصد PG های degraded ، inactive و بی‌نسخه برای از کار افتادن ۱، ۲ یا ۳ میزبان از ۱۰، با شمارش دقیق روی همه‌ی ترکیب‌ها از نگاشت osdmaptool (ستون میانگین است و خط‌ها کمینه تا بیشینه). راست: احتمال اینکه کلاینتی که N شیء را لمس می‌کند در وضعیت دو میزبان ازکارافتاده به PG inactive برخورد کند
شکل ۱۷-۷: چپ: درصد ‏PG‏های ‏degraded‏، ‏inactive‏ و بی‌نسخه برای از کار افتادن ۱، ۲ یا ۳ میزبان از ۱۰، با شمارش دقیق روی همه‌ی ترکیب‌ها از نگاشت ‏osdmaptool‏ (ستون میانگین است و خط‌ها کمینه تا بیشینه). راست: احتمال اینکه کلاینتی که ‏N‏ شیء را لمس می‌کند در وضعیت دو میزبان ازکارافتاده به ‏PG‏ ‏‏inactive‏ برخورد کند

۱۷-۹ ‏slow ops‏: دنبال کردن یک عملیات#

۱۷-۹-۱ ‏TrackedOp‏ و رویدادها#

هر درخواست کلاینت در ‏OSD‏ یک ‏OpRequest‏ (زیرکلاس ‏TrackedOp‏) است که در طول مسیر «رویداد» ثبت می‌کند. اگر عملیاتی بیش از ‏osd_op_complaint_time‏ (پیش‌فرض ۳۰ ثانیه) طول بکشد، در هشدار ‏SLOW_OPS‏ شمرده می‌شود.

شکل ۱۷-۸: چرخه‌ی عمر یک عملیات در OSD در سه ردیف: ورود و قرار گرفتن در صف شارد و رسیدن به PG ؛ انتظارهای رایج که با رویدادهای waiting for ثبت می‌شوند؛ و اجرا، ارسال زیرعملیات به replica ها، دریافت ack و ارسال پاسخ. آخرین رویداد ثبت‌شده‌ی یک عملیات کند نشان می‌دهد کجا گیر کرده است
شکل ۱۷-۸: چرخه‌ی عمر یک عملیات در ‏OSD‏ در سه ردیف: ورود و قرار گرفتن در صف شارد و رسیدن به ‏PG‏؛ انتظارهای رایج که با رویدادهای ‏waiting for‏ ثبت می‌شوند؛ و اجرا، ارسال زیرعملیات به ‏replica‏ها، دریافت ‏ack‏ و ارسال پاسخ. آخرین رویداد ثبت‌شده‌ی یک عملیات کند نشان می‌دهد کجا گیر کرده است
bashReef+
ceph health detail | grep -A3 SLOW_OPS
ceph daemon osd.7 dump_ops_in_flight | jq '.ops[] | {age, description, last: .type_data.events[-1].event}'
ceph daemon osd.7 dump_blocked_ops
ceph daemon osd.7 dump_historic_slow_ops | jq '.ops[0].type_data.events'
ceph daemon osd.7 dump_historic_ops_by_duration | jq '.ops[0:3][] | {duration, description}'
آخرین رویدادتفسیرجای بعدی برای نگاه
‏queued_for_pg‏در صف شارد مانده؛ ‏OSD‏ یا ‏PG‏ پربار‏mclock‏، ‏CPU‏، ‏ceph osd perf‏
‏reached_pg‏ و ‏waiting for peered/active‏‏PG‏ فعال نیست‏ceph pg PGID query‏
‏waiting for degraded/missing object‏شیء در حال بازیابیاولویت ‏recovery‏، ‏ceph pg force-recovery‏
‏sub_op_sent‏ بدون ‏sub_op_commit_rec‏‏replica‏ کند استهمان عملیات روی ‏OSD replica‏، دیسک یا شبکه‌ی آن
‏op_commit‏ یا تأخیر ‏BlueStore‏دیسک محلی کند‏ceph osd perf‏، ‏iostat‏، ‏BLUESTORE_SLOW_OP_ALERT‏
✎ یادداشت

نام رویدادهای پایه (‏queued_for_pg‏، ‏reached_pg‏، ‏started‏، ‏commit_sent‏) در ‏src/osd/OpRequest.h‏ ثبت می‌شوند و رویدادهای «‏waiting for‏ ...» در جاهای مختلف ‏PrimaryLogPG.cc‏ و ‏OSD.cc‏. چون این رشته‌ها بخشی از رابط پایدار نیستند، در ابزارهای خودکار روی آن‌ها تکیه‌ی سخت نکنید.

💡 نکته

یک ‏slow op‏ روی ‏primary‏ اغلب علامت است نه علت. اگر آخرین رویداد ‏sub_op_sent‏ است، همان شناسه‌ی درخواست را روی ‏OSD‏های ‏replica‏ بگردید؛ معمولاً یک دیسک کند یا یک لینک شبکه‌ی خراب پشت ده‌ها ‏slow op‏ روی ‏OSD‏های مختلف است. ‏ceph osd perf‏ (ستون‌های ‏commit_latency‏ و ‏apply_latency‏) سریع‌ترین راه یافتن ‏OSD‏ کند است.

۱۷-۱۰ ‏scrub‏ و ناسازگاری#

۱۷-۱۰-۱ ماشین حالت ‏scrub‏#

‏scrub‏ دوره‌ای (پیش‌فرض ‏osd_scrub_min_interval=1_day‏) فراداده‌ی اشیا را میان نسخه‌ها مقایسه می‌کند و ‏deep scrub‏ (‏osd_deep_scrub_interval=7_day‏) داده را هم می‌خواند و ‏checksum‏ را بررسی می‌کند. کد جدید ‏scrub‏ در ‏src/osd/scrubber/‏ است و ماشین حالت آن در ‏scrub_machine.h‏ با ‏boost::statechart‏ تعریف شده.

شکل ۱۷-۹: ماشین حالت scrub در سه بخش: primary پس از رزرو replica ها (محدود به osd_max_scrubs ) اشیا را تکه‌به‌تکه انتخاب، منتظر نوشتن‌های در جریان می‌ماند، نقشه‌ی محلی می‌سازد و نقشه‌ی replica ها را دریافت می‌کند؛ replica نقشه‌ی خود را می‌سازد و می‌فرستد؛ scrub_backend نسخه‌ی معتبر را انتخاب و ناسازگاری‌ها را در ScrubStore ثبت می‌کند
شکل ۱۷-۹: ماشین حالت ‏scrub‏ در سه بخش: ‏primary‏ پس از رزرو ‏replica‏ها (محدود به ‏osd_max_scrubs‏) اشیا را تکه‌به‌تکه انتخاب، منتظر نوشتن‌های در جریان می‌ماند، نقشه‌ی محلی می‌سازد و نقشه‌ی ‏replica‏ها را دریافت می‌کند؛ ‏replica‏ نقشه‌ی خود را می‌سازد و می‌فرستد؛ ‏scrub_backend‏ نسخه‌ی معتبر را انتخاب و ناسازگاری‌ها را در ‏ScrubStore‏ ثبت می‌کند

‏scrub‏ بر اساس تکه (‏chunk‏) پیش می‌رود: ‏primary‏ یک بازه از اشیا را انتخاب می‌کند (‏NewChunk‏)، اگر نوشتنی روی آن بازه در جریان است منتظر می‌ماند (‏RangeBlocked‏، ‏WaitPushes‏، ‏WaitLastUpdate‏)، سپس نقشه‌ی محلی (‏ScrubMap‏) می‌سازد و از ‏replica‏ها هم نقشه می‌خواهد. ‏scrub_backend‏ نقشه‌ها را مقایسه می‌کند: اندازه، ‏xattr‏ها، ‏digest‏ داده و ‏omap‏. ناسازگاری‌ها در ‏ScrubStore‏ (یک شیء موقت) ثبت می‌شوند و با ‏rados list-inconsistent-obj‏ قابل خواندن‌اند.

bashReef+
ceph health detail | grep -E "inconsistent|scrub errors"
rados list-inconsistent-pg rbd
rados list-inconsistent-obj 2.1f --format=json-pretty | jq '.inconsistents[] | {object: .object.name, errors, shards: [.shards[] | {osd, errors}]}'
ceph pg deep-scrub 2.1f          # تأیید دوباره پیش از تعمیر
ceph pg repair 2.1f
⚠ هشدار

پیش از ‏ceph pg repair‏ خروجی ‏list-inconsistent-obj‏ را بخوانید. اگر خطا روی یک ‏shard‏ یا یک ‏OSD‏ است (مثلاً ‏read_error‏ یا ‏data_digest_mismatch‏ فقط روی ‏osd.12‏)، تعمیر نسخه‌ی سالم را جایگزین می‌کند و باید دیسک ‏osd.12‏ را بررسی کنید (‏SMART‏ و ‏dmesg‏). اگر چند نسخه با هم ناسازگارند و نسخه‌ی معتبر روشن نیست، تعمیر ممکن است نسخه‌ی اشتباه را انتخاب کند. افزایش شمارنده‌ی ‏OSD_TOO_MANY_REPAIRS‏ نشانه‌ی خرابی تدریجی دیسک است.

۱۷-۱۱ اشیای ‏unfound‏ و ابزارهای خطرناک#

bashReef+
ceph pg 2.5 list_unfound
ceph pg 2.5 query | jq '.recovery_state[] | select(.name|test("Active")) | .might_have_unfound'
# آخرین راه، پس از اطمینان از اینکه OSD دارای نسخه برنمی‌گردد:
ceph pg 2.5 mark_unfound_lost revert     # یا delete
ابزارکاربردخطر
‏ceph osd lost N‏اجازه‌ی ‏peering‏ بدون ‏OSD‏ مردهاز دست رفتن نوشتن‌های تأییدشده‌ی منحصر به آن ‏OSD‏
‏mark_unfound_lost revert/delete‏پایان دادن به انتظار برای شیء ‏unfound‏بازگشت به نسخه‌ی قبلی یا حذف شیء
‏ceph-objectstore-tool‏خواندن، صدور و ورود ‏PG‏ از ‏OSD‏ متوقفتغییر مستقیم داده‌ی ‏OSD‏؛ فقط با ‏OSD‏ خاموش
‏ceph-bluestore-tool fsck/repair‏بررسی و تعمیر فراداده‌ی ‏BlueStore‏‏repair‏ ممکن است فراداده را تغییر دهد
‏osd_find_best_info_ignore_history_les‏بازیابی ‏PG incomplete‏دور ریختن داده‌ی تأییدشده
✚ عیب‌یابی

برای نجات یک ‏PG‏ از ‏OSD‏ی که دیگر بالا نمی‌آید ولی دیسکش خوانا است: ‏OSD‏ را متوقف کنید، با ‏ceph-objectstore-tool --data-path /var/lib/ceph/osd/ceph-12 --op list-pgs‏ ‏PG‏ها را ببینید، با ‏--op export --pgid 2.5 --file /backup/2.5.export‏ صادر کنید و روی یک ‏OSD‏ سالم (متوقف) با ‏--op import‏ وارد کنید. در ‏cephadm‏ این کار را درون ‏cephadm shell --name osd.12‏ انجام دهید. فصل ۱۸ این روال را کامل‌تر شرح می‌دهد.

نکات ‏SRE‏ این فصل
  • همیشه از ‏ceph health detail‏ و کد هشدار شروع کنید.
  • پیش از تغییر، وضعیت را ثبت کنید؛ یک تغییر در هر زمان.
  • ‏recovery_state‏ در ‏pg query‏ نام دقیق حالت ‏PeeringState‏ را نشان می‌دهد.
  • ‏PG down‏ یعنی «‏OSD‏های این فهرست را برگردان»، نه «‏lost‏ کن».
  • ‏slow op‏ را تا آخرین رویداد و تا ‏OSD replica‏ دنبال کنید.
  • پیش از ‏repair‏، ناسازگاری را بخوانید و دیسک مقصر را پیدا کنید.

۱۷-۱۲ تکمیلی و به‌روز#

  • کد ‏scrub‏ از ‏Pacific‏ به بعد بازنویسی و به ‏src/osd/scrubber/‏ منتقل شده و ماشین حالت جدا دارد؛ زمان‌بندی آن با ‏mclock‏ یکپارچه است.
  • هشدارهای تازه‌تری مانند ‏BLUESTORE_SLOW_OP_ALERT‏ و ‏DB_DEVICE_STALLED_READ_ALERT‏ در مستندات ‏Health Checks‏ آمده‌اند و کندی لایه‌ی ذخیره‌سازی را زودتر نشان می‌دهند.
  • در شاخه‌ی ‏main‏، حالت‌های تازه‌ای مانند ‏MigratingSource‏، ‏MigratingTarget‏ و ‏WaitLocalPoolMigrationReserved‏ برای مهاجرت ‏pool‏ به ‏PeeringState‏ افزوده شده‌اند؛ این حالت‌ها در برچسب‌های ‏v20.2.0‏ و ‏v21.2.0‏ وجود ندارند و مربوط به نسخه‌های آینده‌اند.
  • ‏ceph crash‏ (ماژول ‏mgr‏) جایگزین جست‌وجوی دستی ‏core dump‏ها شده است.

۱۷-۱۳ تمرین عملی#

  1. ۱. در کلاستر آزمایشی با ‏size=3‏ دو ‏OSD‏ از سه ‏OSD‏ یک ‏PG‏ را متوقف کنید و ‏recovery_state‏ را در ‏pg query‏ دنبال کنید.
  2. ۲. با ‏ceph osd set noout‏ یک میزبان را برای نگه‌داری خاموش کنید؛ وضعیت ‏PG‏ها را ثبت و پس از بازگشت مقایسه کنید.
  3. ۳. با ‏tc qdisc add dev eth1 root netem delay 200ms‏ روی یک میزبان، ‏slow ops‏ بسازید و آن را تا ‏OSD‏ مقصر دنبال کنید.
  4. ۴. یک شیء را مستقیماً روی یک ‏OSD‏ (با ‏ceph-objectstore-tool‏) خراب کنید، ‏deep scrub‏ بگیرید و خروجی ‏list-inconsistent-obj‏ را تحلیل کنید.
  5. ۵. ماشین حالت ‏PeeringState.h‏ را بخوانید و مسیر از ‏Reset‏ تا ‏Clean‏ را برای یک ‏PG‏ سالم بنویسید.
  6. ۶. یک ‏PG‏ را با ‏ceph-objectstore-tool‏ صادر و روی ‏OSD‏ دیگری وارد کنید.

۱۷-۱۴ پرسش‌های مرور#

  1. ۱. نخستین دو فرمان در هر حادثه کدام‌اند و چرا؟
  2. ۲. تفاوت ‏degraded‏، ‏undersized‏ و ‏remapped‏ چیست؟
  3. ۳. حالت ‏WaitUpThru‏ چه مشکلی را حل می‌کند؟
  4. ۴. چرا ‏PG‏ به حالت ‏down‏ می‌رود و فیلد ‏down_osds_we_would_probe‏ چه می‌گوید؟
  5. ۵. ‏find_best_info‏ در ‏pool‏ تکثیری و ‏EC‏ چه تفاوتی دارد و چرا؟
  6. ۶. آخرین رویداد ‏sub_op_sent‏ در یک ‏slow op‏ چه معنایی دارد؟
  7. ۷. ‏scrub‏ چگونه با نوشتن‌های در جریان هماهنگ می‌شود؟
  8. ۸. پیش از ‏ceph pg repair‏ چه چیزی را باید بررسی کنید؟
  9. ۹. ‏ceph osd lost‏ چه خطری دارد؟
  10. ۱۰. چرا ‏nodown‏ و ‏noout‏ باید موقت باشند؟

پاسخ این پرسش‌ها در پیوست ه: پاسخ پرسش‌ها آمده است. پاسخ پرسش‌های این فصل →

۱۷-۱۵ منابع این فصل#

راهنمای جامع Ceph · ۱٫۶ · آبان ۱۴۰۵
© 2026 Ramin Najarbashi · CC BY-NC-SA 4.0

حریم خصوصی: این سایت کوکی نمی‌گذارد. برای شمارش بازدیدها از Cloudflare Web Analytics استفاده می‌شود که کوکی ندارد و افراد را دنبال نمی‌کند.