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

ذخیره‌سازی شیء چندسایتی با ‏RGW Multi-Site‏

۴۴ دقیقه مطالعهویرایش این صفحه
حوصله ندارم این فصل در چند خط
  • ‏realm‏ فضای نام سراسری است، ‏zonegroup‏ واحد تکثیر داده، ‏zone‏ یک کلاستر و ‏period‏ یک نسخه‌ی ‏commit‏‌شده از پیکربندی.
  • ‏metadata‏ از ‏zone‏ اصلی (‏master‏) پخش می‌شود؛ داده‌ی اشیا بین ‏zone‏های یک ‏zonegroup‏ به‌صورت ناهمگام و ‏active-active‏ تکثیر می‌شود.
  • راه‌اندازی دستی با ‏radosgw-admin‏ یا خودکار با ‏ceph rgw realm bootstrap‏ است؛ پایش با ‏sync status‏، ‏bucket sync status‏ و ‏sync error list‏ انجام می‌شود.
  • ‏failover‏ یعنی ‏master‏ کردن ‏zone‏ دیگر و ‏commit‏ کردن ‏period‏؛ در قطعی کوتاه شبکه با عجله ‏promote‏ نکنید.

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

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

یک کلاستر ‏Ceph‏، هر قدر هم بزرگ، در یک مرکز داده قرار دارد. برای بازیابی از سانحه، نزدیک کردن داده به کاربران در مناطق مختلف، یا مهاجرت بین مراکز داده، باید داده‌ی شیء را بین چند کلاستر مستقل تکثیر کنیم. ‏RGW Multi-Site‏ پاسخ ‏Ceph‏ به این نیاز است. در این فصل می‌بینیم:

  • ‏realm‏، ‏zonegroup‏، ‏zone‏ و ‏period‏ چه هستند و چطور با هم یک فضای نام سراسری می‌سازند؟
  • ‏multisite‏ امروزی (نسخه‌ی ۲، از ‏Jewel‏) چه فرقی با پیکربندی ‏federated‏ قدیمی ‏Hammer‏ دارد؟
  • پیش‌نیازهای شبکه، زمان و ظرفیت برای یک استقرار چندسایتی چیست؟
  • چطور دو ‏zone‏ را، هم به روش دستی با ‏radosgw-admin‏ و هم با ماژول خودکار ‏ceph rgw‏، پیکربندی کنیم؟
  • چطور وضعیت همگام‌سازی را بخوانیم، مشکلات را پیدا کنیم و هنگام سانحه ‏master‏ را جابه‌جا کنیم؟
  • ‏sync policy‏، ‏archive zone‏ و ‏resharding‏ در ‏multisite‏ چگونه کار می‌کنند؟

۵-۱-۲ پیش‌نیاز#

دو کلاستر ‏Ceph‏ (برای آزمایشگاه دو کلاستر تک‌نودی کافی است) که ‏RGW‏هایشان از طریق ‏HTTP‏ به هم دسترسی دارند. مفاهیم فصل ۴ را باید بدانید.

۵-۲ مفاهیم ‏multisite‏#

۵-۲-۱ سلسله‌مراتب#

شکل ۵-۱: سلسله‌مراتب multisite: realm شامل zonegroup ها و هر zonegroup شامل zone هایی است که هر کدام در یک کلاستر Ceph جداگانه قرار دارند
شکل ۵-۱: سلسله‌مراتب ‏multisite: realm‏ شامل ‏zonegroup‏ها و هر ‏zonegroup‏ شامل ‏zone‏هایی است که هر کدام در یک کلاستر ‏Ceph‏ جداگانه قرار دارند
مفهومتعریفنکته
‏zone‏مجموعه‌ای از ‏pool‏ها و نمونه‌های ‏RGW‏ در یک کلاستر ‏Ceph‏هر ‏zone‏ در یک کلاستر است؛ یک کلاستر می‌تواند چند ‏zone‏ داشته باشد
‏zonegroup‏گروهی از ‏zone‏ها که داده‌ی ‏bucket‏ها بین آن‌ها تکثیر می‌شودقبلاً «‏region‏» نامیده می‌شد؛ یک ‏zone‏ آن ‏master‏ است
‏realm‏ظرف سراسری شامل ‏zonegroup‏ها؛ فضای نام یکتای کاربران و ‏bucket‏هایک ‏zonegroup‏ آن ‏master‏ است
‏period‏نسخه‌ای از پیکربندی ‏realm‏ با شناسه و ‏epoch‏هر تغییر ساختاری با ‏period update --commit‏ منتشر می‌شود
‏master zone‏‏zone‏ی که فراداده (کاربران، ‏bucket‏ها) را می‌پذیرد و به بقیه پخش می‌کندساخت کاربر و ‏bucket‏ فقط از طریق آن

۵-۲-۲ چه چیزی و چطور تکثیر می‌شود؟#

  • فراداده (کاربران، ‏bucket‏ها، سیاست‌ها) در سطح ‏realm‏ تکثیر می‌شود و همیشه از ‏master zone‏ در ‏master zonegroup‏ منشأ می‌گیرد. درخواست ساخت ‏bucket‏ به ‏zone‏ غیر ‏master‏ به‌طور خودکار به ‏master‏ ‏forward‏ می‌شود.
  • داده‌ی اشیاء فقط بین ‏zone‏های یک ‏zonegroup‏ تکثیر می‌شود. ‏zonegroup‏های مختلف فضای نام مشترک دارند (نام ‏bucket‏ یکتاست) اما داده‌ی هر ‏bucket‏ فقط در ‏zonegroup‏ خودش است.
  • همگام‌سازی ناهمگام و ‏active-active‏ است: کلاینت‌ها می‌توانند در هر ‏zone‏ بنویسند و تغییرات با تأخیری کوتاه به ‏zone‏های دیگر می‌رسند. در تعارض نوشتن هم‌زمان روی یک شیء، آخرین نوشتن (بر اساس زمان) برنده است.
⚠ هشدار

چون همگام‌سازی ناهمگام است، ‏multisite‏ جایگزین سازگاری قوی بین سایت‌ها نیست. اگر برنامه‌ای در سایت ‏A‏ می‌نویسد و بلافاصله در سایت ‏B‏ می‌خواند، ممکن است نسخه‌ی قدیمی یا هیچ نسخه‌ای نبیند. برنامه‌ها را طوری طراحی کنید که هر کلاینت «سایت خانگی» داشته باشد، یا وابستگی به خواندن فوری بین‌سایتی نداشته باشند.

۵-۲-۳ تغییرات از پیکربندی ‏federated‏ در ‏Hammer‏#

شکل ۵-۲: از federated در Hammer به multisite نسخه‌ی ۲: همگام‌سازی از یک agent بیرونی به درون خود radosgw منتقل شد و همه‌ی zone ها نوشتنی شدند
شکل ۵-۲: از ‏federated‏ در ‏Hammer‏ به ‏multisite‏ نسخه‌ی ۲: همگام‌سازی از یک ‏agent‏ بیرونی به درون خود ‏radosgw‏ منتقل شد و همه‌ی ‏zone‏ها نوشتنی شدند

در ‏Hammer‏ و پیش از آن، تکثیر با یک فرایند ‏Python‏ بیرونی به نام ‏radosgw-agent‏ انجام می‌شد، ‏zone‏ ثانوی فقط‌خواندنی بود و مفهوم ‏realm‏ و ‏period‏ وجود نداشت. از ‏Jewel‏، ‏multisite‏ بازطراحی شد:

  • همگام‌سازی درون خود ‏radosgw‏ انجام می‌شود (نیازی به ‏agent‏ نیست).
  • همه‌ی ‏zone‏ها می‌توانند داده بپذیرند (‏active-active‏).
  • ‏realm‏ و ‏period‏ امکان تغییر پیکربندی سازگار و نسخه‌دار را فراهم کرده‌اند.
  • ‏failover‏ با جابه‌جایی ‏master zone‏ و ‏commit‏ یک ‏period‏ جدید انجام می‌شود.

از آن زمان قابلیت‌های بسیاری اضافه شده است: ‏sync policy‏ با کنترل در سطح ‏bucket‏ (Octopus)، ‏archive zone‏، ‏resharding‏ بدون توقف همگام‌سازی (از ‏Reef‏ با ‏zone feature‏ به نام ‏resharding‏) و در ‏Umbrella‏ اصلاح‌های متعدد در درستی همگام‌سازی.

۵-۳ پیش‌نیازهای ‏multisite‏#

۵-۳-۱ شبکه و زمان#

  • دسترسی ‏HTTP‏(S) بین ‏RGW‏ها: ‏RGW‏های هر ‏zone‏ باید ‏endpoint‏های ‏zone‏های دیگر را ببینند. معمولاً ‏endpoint‏ هر ‏zone‏ نشانی ‏load balancer‏ آن است.
  • پهنای باند: نرخ تغییر داده (نه حجم کل) تعیین‌کننده است. همگام‌سازی اولیه (‏full sync‏) کل داده‌ی موجود را منتقل می‌کند؛ برای داده‌ی چندصد ترابایتی، روزها طول می‌کشد.
  • تأخیر: ‏multisite‏ برای ‏WAN‏ طراحی شده و تأخیر بالا را تحمل می‌کند، اما تأخیر زیاد، عقب‌ماندگی را بالا می‌برد.
  • همگامی ساعت: حل تعارض و امضای درخواست‌ها به زمان وابسته است. همه‌ی ‏node‏ها باید با ‏NTP‏ همگام باشند.

۵-۳-۲ کاربر ‏system‏ و ‏endpoint‏ها#

‏zone‏ها برای خواندن لاگ‌ها و اشیای یکدیگر از یک کاربر ‏system‏ با کلید مشترک استفاده می‌کنند. این کاربر در ‏master zone‏ ساخته می‌شود و کلیدش در پیکربندی همه‌ی ‏zone‏ها قرار می‌گیرد.

⚠ هشدار

کاربر ‏system‏ به همه‌ی داده‌ها و فراداده‌ی ‏realm‏ دسترسی کامل دارد. کلیدش را مثل کلید مدیر کلاستر حفاظت کنید، ‏endpoint‏های همگام‌سازی را فقط روی شبکه‌ی خصوصی یا ‏VPN‏ بین سایت‌ها باز کنید و از ‏TLS‏ استفاده کنید.

۵-۳-۳ ظرفیت و ‏pool‏ها#

هر ‏zone pool‏های خودش را دارد که نامشان با نام ‏zone‏ شروع می‌شود (مثلاً ‏eu-1.rgw.buckets.data‏). اگر ‏pool‏ها را پیش از راه‌اندازی بسازید، می‌توانید قاعده‌ی ‏CRUSH‏، ‏EC‏ و ‏target_size_ratio‏ را از ابتدا درست تعیین کنید؛ وگرنه ‏RGW‏ آن‌ها را با پیش‌فرض‌ها می‌سازد. علاوه بر داده، ‏pool‏ لاگ (‏<zone>.rgw.log‏) در ‏multisite‏ پرکارتر است و باید روی رسانه‌ی سریع باشد.

💡 نکته

هر دو سایت را از نظر ظرفیت متقارن طراحی کنید. ‏zone‏ ثانوی که زودتر پر شود، همگام‌سازی را متوقف می‌کند و بعد از آزاد کردن فضا باید عقب‌ماندگی زیادی را جبران کند.

۵-۴ پیکربندی ‏multisite‏ به روش دستی#

شکل ۵-۳: گام‌های راه‌اندازی: ساخت realm ، zonegroup و master zone در کلاستر اصلی، سپس pull کردن realm و ساخت zone ثانوی در کلاستر دوم
شکل ۵-۳: گام‌های راه‌اندازی: ساخت ‏realm‏، ‏zonegroup‏ و ‏master zone‏ در کلاستر اصلی، سپس ‏pull‏ کردن ‏realm‏ و ساخت ‏zone‏ ثانوی در کلاستر دوم

فرض‌ها: کلاستر اصلی با ‏RGW‏ در دسترس روی ‏http://rgw-eu1.example.com:80‏ و کلاستر ثانوی روی ‏http://rgw-eu2.example.com:80‏. ‏realm‏ به نام ‏gold‏، ‏zonegroup‏ به نام ‏europe‏، ‏zone‏ها ‏eu-1‏ و ‏eu-2‏.

۵-۴-۱ گام ۱: ساختار در کلاستر اصلی#

bashReef+
radosgw-admin realm create --rgw-realm=gold --default
radosgw-admin zonegroup create --rgw-zonegroup=europe \
  --endpoints=http://rgw-eu1.example.com:80 --rgw-realm=gold --master --default
radosgw-admin zone create --rgw-zonegroup=europe --rgw-zone=eu-1 \
  --endpoints=http://rgw-eu1.example.com:80 --master --default

اگر پیش‌تر ‏RGW‏ تک‌سایتی با ‏zone‏ و ‏zonegroup‏ پیش‌فرض (‏default‏) داشته‌اید و داده دارد، به‌جای ساختن ‏zone‏ جدید، ‏zone‏ و ‏zonegroup‏ پیش‌فرض را تغییر نام دهید و به ‏realm‏ اضافه کنید (مستندات «‏Migrating a Single-Site Deployment to Multi-Site‏»). در غیر این صورت داده‌ی موجود در ‏zone‏ قدیمی بی‌صاحب می‌ماند.

۵-۴-۲ گام ۲: کاربر ‏system‏#

bashReef+
radosgw-admin user create --uid=sync-user --display-name="Multisite sync" --system
# access_key و secret_key را از خروجی بردارید
radosgw-admin zone modify --rgw-zone=eu-1 --access-key=<AK> --secret=<SK>
radosgw-admin period update --commit

۵-۴-۳ گام ۳: ‏RGW‏ برای ‏zone‏ اصلی#

با ‏cephadm‏، سرویس ‏RGW‏ را با ‏realm‏ و ‏zone‏ مشخص مستقر کنید:

bashReef+
ceph orch apply rgw gold-eu1 --realm=gold --zone=eu-1 \
  --placement="label:rgw count-per-host:1" --port=80

۵-۴-۴ گام ۴: کلاستر ثانوی#

bashReef+
radosgw-admin realm pull --url=http://rgw-eu1.example.com:80 \
  --access-key=<AK> --secret=<SK>
radosgw-admin realm default --rgw-realm=gold
radosgw-admin period pull --url=http://rgw-eu1.example.com:80 --access-key=<AK> --secret=<SK>
radosgw-admin zone create --rgw-zonegroup=europe --rgw-zone=eu-2 \
  --endpoints=http://rgw-eu2.example.com:80 --access-key=<AK> --secret=<SK>
radosgw-admin period update --commit
ceph orch apply rgw gold-eu2 --realm=gold --zone=eu-2 \
  --placement="label:rgw count-per-host:1" --port=80
✚ عیب‌یابی

اگر ‏period update --commit‏ در کلاستر ثانوی با خطای «‏failed to commit period‏» یا «‏connection refused‏» شکست خورد، از کلاستر ثانوی با ‏curl http://rgw-eu1.example.com:80‏ دسترسی را بسنجید و مطمئن شوید کلیدهای ‏system‏ در ‏zone modify‏ درست قرار گرفته‌اند (‏radosgw-admin zone get --rgw-zone=eu-1‏). یک خطای رایج، نام میزبانی است که فقط در یکی از سایت‌ها قابل‌حل است.

۵-۵ پیکربندی خودکار با ماژول ‏rgw‏#

از ‏Reef‏ به بعد، ماژول ‏MGR‏ به نام ‏rgw‏ (فرمان ‏ceph rgw‏) همه‌ی گام‌های بالا را در دو دستور خلاصه می‌کند و با ارکستراتور هماهنگ است:

yaml
# primary.yaml روی کلاستر اصلی
rgw_realm: gold
rgw_zonegroup: europe
rgw_zone: eu-1
placement:
  label: rgw
  count_per_host: 1
spec:
  rgw_frontend_port: 80
bashReef+
ceph mgr module enable rgw
ceph rgw realm bootstrap -i primary.yaml
ceph rgw realm tokens            # توکن base64 شامل realm، endpoint و کلیدها
yaml
# secondary.yaml روی کلاستر ثانوی
rgw_zone: eu-2
rgw_realm_token: <token>
placement:
  label: rgw
spec:
  rgw_frontend_port: 80
bashReef+
ceph mgr module enable rgw
ceph rgw zone create -i secondary.yaml
💡 نکته

روش خودکار برای ساختارهای ساده (یک ‏zonegroup‏ و دو یا چند ‏zone‏) بسیار سریع‌تر و کم‌خطاتر است. برای ساختارهای پیچیده (چند ‏zonegroup‏، ‏placement‏های سفارشی، مهاجرت از تک‌سایتی) روش دستی کنترل بیشتری می‌دهد. داشبورد ‏Ceph‏ هم از ‏Tentacle‏ یک ‏wizard‏ برای خودکارسازی ‏multisite‏ دارد.

⚠ هشدار

توکن ‏realm‏ حاوی کلید کاربر ‏system‏ است. همان مراقبتی که برای توکن ‏bootstrap‏ در ‏RBD mirroring‏ گفتیم، این‌جا هم لازم است.

۵-۶ بررسی وضعیت همگام‌سازی#

۵-۶-۱ ‏sync status‏#

bashReef+
radosgw-admin sync status

خروجی نمونه در ‏zone‏ ثانوی:

text
          realm 1c2d... (gold)
      zonegroup 7a8b... (europe)
           zone 9e0f... (eu-2)
   current time 2026-11-15T08:12:44Z
zonegroup features enabled: resharding
  metadata sync syncing
                full sync: 0/64 shards
                incremental sync: 64/64 shards
                metadata is caught up with master
      data sync source: 3d4e... (eu-1)
                        syncing
                        full sync: 0/128 shards
                        incremental sync: 128/128 shards
                        data is caught up with source
شکل ۵-۴: سازوکار همگام‌سازی: zone مقصد datalog و bilog مبدأ را می‌خواند و اشیای تغییرکرده را با GET از endpoint مبدأ می‌کشد؛ فراداده از metadata log در master zone می‌آید
شکل ۵-۴: سازوکار همگام‌سازی: ‏zone‏ مقصد ‏datalog‏ و ‏bilog‏ مبدأ را می‌خواند و اشیای تغییرکرده را با ‏GET‏ از ‏endpoint‏ مبدأ می‌کشد؛ فراداده از ‏metadata log‏ در ‏master zone‏ می‌آید
عبارت در خروجیمعنااقدام
‏metadata is caught up with master‏فراداده همگام است—
‏data is caught up with source‏داده‌ی همه‌ی ‏shard‏ها همگام است—
‏behind shards‏: [..]برخی ‏shard‏های داده عقب‌انداگر ماندگار است، ‏sync error list‏ و پهنای باند را بررسی کنید
‏oldest incremental change not applied‏قدیمی‌ترین تغییر اعمال‌نشدهاختلاف زمانی را با ‏now‏ مقایسه کنید (‏RPO‏ واقعی)
‏full sync: N/M shards‏همگام‌سازی کامل در جریان استطبیعی پس از راه‌اندازی یا ‏resync‏

۵-۶-۲ دستورهای تکمیلی#

bashReef+
radosgw-admin sync status --rgw-zone=eu-2
radosgw-admin metadata sync status
radosgw-admin data sync status --source-zone=eu-1
radosgw-admin bucket sync status --bucket=photos
radosgw-admin sync error list | head -50
radosgw-admin sync error trim
radosgw-admin period get | jq '.period_map.zonegroups[].zones[].name'
✚ عیب‌یابی

سه مشکل پرتکرار:

عقب‌ماندگی ماندگار در چند ‏shard‏: معمولاً یک ‏bucket‏ خیلی پرتغییر یا شیء خراب. ‏bucket sync status‏ روی ‏bucket‏های پرترافیک و ‏sync error list‏ را ببینید.

خطاهای ۴۰۳ در لاگ همگام‌سازی: کلید ‏system‏ در یکی از ‏zone‏ها با کاربر واقعی یکی نیست یا کاربر ‏system‏ حذف یا تعلیق شده است.

همگام‌سازی شروع نمی‌شود: ‏endpoint‏های ‏zone‏ (‏zone get‏) به نشانی‌ای اشاره می‌کنند که از سایت دیگر در دسترس نیست (مثلاً ‏localhost‏ یا نام داخلی).

۵-۷ آزمون همگام‌سازی کاربر، ‏bucket‏ و شیء#

bashReef+
# روی کلاستر اصلی (master zone)
radosgw-admin user create --uid=tester --display-name="Multisite tester"
# روی کلاستر ثانوی، پس از چند ثانیه:
radosgw-admin user info --uid=tester        # کاربر همگام شده است
# با s3cmd یا aws-cli
aws --endpoint-url http://rgw-eu1.example.com s3 mb s3://ms-test
aws --endpoint-url http://rgw-eu1.example.com s3 cp ./report.pdf s3://ms-test/
aws --endpoint-url http://rgw-eu2.example.com s3 ls s3://ms-test/
# نوشتن در zone ثانوی و دیدن در اصلی (active-active)
aws --endpoint-url http://rgw-eu2.example.com s3 cp ./notes.txt s3://ms-test/
aws --endpoint-url http://rgw-eu1.example.com s3 ls s3://ms-test/
radosgw-admin bucket sync status --bucket=ms-test
▶ دست‌به‌کار شوید

زمان رسیدن شیء به ‏zone‏ دیگر را اندازه بگیرید: در یک حلقه هر ثانیه یک شیء کوچک با زمان فعلی در نام در ‏eu-1‏ بنویسید و در ‏eu-2‏ فهرست بگیرید. سپس با ابزار ‏tc‏ روی میزبان ‏RGW‏ ثانوی ۲۰۰ میلی‌ثانیه تأخیر شبیه‌سازی کنید (‏tc qdisc add dev eth0 root netem delay 200ms‏) و اثرش را ببینید. یادتان نرود در پایان ‏tc qdisc del dev eth0 root‏ اجرا کنید.

۵-۸ از درون کد: لاگ‌ها و ‏coroutine‏های همگام‌سازی ‏multisite‏#

۵-۸-۱ سه لاگ، سه لایه#

همگام‌سازی ‏multisite‏ در ‏RGW‏ کششی (‏pull‏) است. هر ‏zone‏ مقصد با ‏REST‏، لاگ‌های ‏zone‏ مبدأ را می‌خواند و تغییرات را خودش اعمال می‌کند. سه نوع لاگ در این کار نقش دارند:

لاگکجا ذخیره می‌شودچه چیزی را ثبت می‌کندپیش‌فرض‌ها (‏main‏)
‏bilog‏کنار هر شارد ‏bucket index‏ (cls_rgw)هر تغییر شیء در آن شاردهمراه ‏complete_op‏
‏datalog‏‏pool log zone‏«این ‏bucket shard‏ تغییر کرده است» (نه خود شیء)‏rgw_data_log_num_shards=128‏، ‏rgw_data_log_window=30‏
‏mdlog‏اشیای ‏meta.log.*‏ (‏rgw_mdlog.h‏)تغییر فراداده: کاربر، ‏bucket‏، ‏bucket instance‏‏rgw_md_log_max_shards=64‏
شکل ۵-۵: جریان کامل multisite . در zone مبدأ، هر PUT یا DELETE یک ورودی bilog در شارد index می‌سازد و یک ورودی datalog اعلام می‌کند که «آن شارد bucket تغییر کرده است». پنجره‌ی ۳۰ ثانیه‌ای datalog تغییرات پیاپی روی یک شارد را ادغام می‌کند تا لاگ منفجر نشود. ساخت bucket یا کاربر در mdlog ثبت می‌شود که فقط master zone آن را تولید می‌کند. در zone مقصد سه لایه coroutine کار می‌کنند: meta sync ، data sync برای هر شارد datalog ، و bucket sync برای هر شارد bucket که bilog را می‌خواند. لایه‌ی پایانی شیء را با fetch_remote_obj از مبدأ می‌کشد، محلی می‌نویسد و marker را در شیء وضعیت ذخیره می‌کند
شکل ۵-۵: جریان کامل ‏multisite‏. در ‏zone‏ مبدأ، هر ‏PUT‏ یا ‏DELETE‏ یک ورودی ‏bilog‏ در شارد ‏index‏ می‌سازد و یک ورودی ‏datalog‏ اعلام می‌کند که «آن شارد ‏bucket‏ تغییر کرده است». پنجره‌ی ۳۰ ثانیه‌ای ‏datalog‏ تغییرات پیاپی روی یک شارد را ادغام می‌کند تا لاگ منفجر نشود. ساخت ‏bucket‏ یا کاربر در ‏mdlog‏ ثبت می‌شود که فقط ‏master zone‏ آن را تولید می‌کند. در ‏zone‏ مقصد سه لایه ‏coroutine‏ کار می‌کنند: ‏meta sync‏، ‏data sync‏ برای هر شارد ‏datalog‏، و ‏bucket sync‏ برای هر شارد ‏bucket‏ که ‏bilog‏ را می‌خواند. لایه‌ی پایانی شیء را با ‏fetch_remote_obj‏ از مبدأ می‌کشد، محلی می‌نویسد و ‏marker‏ را در شیء وضعیت ذخیره می‌کند

۵-۸-۲ ماشین حالت ‏data sync‏#

‏rgw_data_sync_info‏ در ‏src/rgw/driver/rados/rgw_data_sync.h‏ سه حالت دارد: ‏StateInit = 0‏، ‏StateBuildingFullSyncMaps = 1‏ و ‏StateSync = 2‏. پیاده‌سازی بر پایه‌ی چارچوب ‏coroutine‏ خود ‏RGW‏ (‏RGWCoroutine‏) است. کلاس‌های اصلی در ‏rgw_data_sync.cc‏ این‌ها هستند: ‏RGWDataSyncShardCR‏ (حلقه‌ی هر شارد)، ‏RGWDataFullSyncShardCR‏، ‏RGWDataIncSyncShardCR‏، ‏RGWListRemoteDataLogShardCR‏ و برای لایه‌ی ‏bucket‏، ‏RGWBucketShardIncrementalSyncCR‏.

شکل ۵-۶: ماشین حالت همگام‌سازی داده. ابتدا اشیای وضعیت ساخته می‌شوند ( Init )، سپس همه‌ی bucket های مبدأ در full-sync map فهرست می‌شوند ( BuildingFullSyncMaps )، و پس از آن هر شارد datalog مستقل از بقیه ابتدا full sync و بعد incremental sync را با poll بیست‌ثانیه‌ای اجرا می‌کند. خطاها در error repo ثبت و بعداً دوباره تلاش می‌شوند. اگر چند RGW در zone مقصد باشند، هر شارد با یک lease دودقیقه‌ای فقط به یکی از آن‌ها سپرده می‌شود. trim لاگ‌های مبدأ هر ۲۰ دقیقه و فقط تا کمترین marker ی انجام می‌شود که همه‌ی peer ها به آن رسیده‌اند
شکل ۵-۶: ماشین حالت همگام‌سازی داده. ابتدا اشیای وضعیت ساخته می‌شوند (‏Init‏)، سپس همه‌ی ‏bucket‏های مبدأ در ‏full-sync map‏ فهرست می‌شوند (‏BuildingFullSyncMaps‏)، و پس از آن هر شارد ‏datalog‏ مستقل از بقیه ابتدا ‏full sync‏ و بعد ‏incremental sync‏ را با ‏poll‏ بیست‌ثانیه‌ای اجرا می‌کند. خطاها در ‏error repo‏ ثبت و بعداً دوباره تلاش می‌شوند. اگر چند ‏RGW‏ در ‏zone‏ مقصد باشند، هر شارد با یک ‏lease‏ دودقیقه‌ای فقط به یکی از آن‌ها سپرده می‌شود. ‏trim‏ لاگ‌های مبدأ هر ۲۰ دقیقه و فقط تا کمترین ‏marker‏ی انجام می‌شود که همه‌ی ‏peer‏ها به آن رسیده‌اند
text
pseudocode: یک شارد datalog در zone مقصد (خلاصه‌ی این کتاب از rgw_data_sync.cc)
  loop:
    acquire lease(shard, rgw_sync_lease_period)          # 2min؛ فقط یک RGW
    if shard.state == full_sync:
        for bucket_shard in full_sync_map[shard] from marker:
            run bucket sync (full) ; persist marker
        shard.state = incremental
    else:
        entries = remote.list_datalog(shard, after=marker)
        for e in entries (dedup by bucket_shard):
            spawn bucket_incremental_sync(e.bucket_shard)  # حداکثر rgw_bucket_sync_spawn_window=20 هم‌زمان
              → read remote bilog after bucket marker
              → for op in bilog: fetch_remote_obj / remove
        persist marker ; if no entries: sleep rgw_data_sync_poll_interval (20s)
    on error: add to error repo for retry
گزینهپیش‌فرضنکته‌ی ‏SRE‏
‏rgw_run_sync_thread‏‏true‏روی ‏RGW‏های سمت کاربر می‌توان ‏false‏ کرد تا ‏sync‏ فقط روی ‏RGW‏های مخصوص اجرا شود
‏rgw_data_sync_poll_interval‏۲۰حداقل تأخیر حالت آرام
‏rgw_meta_sync_poll_interval‏۲۰برای فراداده
‏rgw_bucket_sync_spawn_window‏۲۰موازی‌سازی ‏bucket sync‏ در هر شارد
‏rgw_sync_lease_period‏۲_‏min‏اگر ‏RGW‏ بمیرد، شارد پس از انقضا به ‏RGW‏ دیگری می‌رسد
‏rgw_sync_log_trim_interval‏۲۰_‏min‏‏peer‏ عقب‌مانده جلوی ‏trim‏ را می‌گیرد و ‏log pool‏ رشد می‌کند
⚠ هشدار

یک ‏zone‏ مقصد خاموش یا عقب‌مانده جلوی ‏trim‏ لاگ‌های مبدأ را می‌گیرد، چون ‏trim‏ فقط تا کمترین ‏marker‏ پیش می‌رود. نتیجه رشد ‏omap‏ در ‏pool log‏ و هشدار ‏LARGE_OMAP_OBJECTS‏ است. ‏zone‏ حذف‌شده را حتماً از ‏zonegroup‏ هم حذف و ‏period‏ را ‏commit‏ کنید.

✚ عیب‌یابی

فرمان‌های بررسی:

bashReef+
radosgw-admin sync status
radosgw-admin data sync status --source-zone=us-east --shard-id=7
radosgw-admin bucket sync status --bucket=photos
radosgw-admin sync error list | jq '.[0].entries[:5]'
radosgw-admin datalog status ; radosgw-admin mdlog status
radosgw-admin bilog list --bucket=photos --max-entries=5

۵-۹ ‏multisite‏ با عدد: ‏shard‏های ‏datalog‏ و ‏mdlog‏، پنجره‌ی ادغام و ‏trim‏#

این بخش چهار تابع کوچک را بررسی می‌کند که بار و تأخیر همگام‌سازی ‏multisite‏ را تعیین می‌کنند. ارجاع‌ها به کد ‏main‏ در نوامبر ۲۰۲۶ است. عددها با بازپیاده‌سازی پایتونی ‏ceph_str_hash_linux‏ (از ‏src/common/ceph_hash.cc‏) حساب شده‌اند. نکته‌ی «بیت‌های پایین ‏hash‏» در زیر نتیجه‌ی تحلیل و شبیه‌سازی خود ما است، نه گزاره‌ای از مستندات ‏Ceph‏.

۵-۹-۱ کدام ‏shard‏ از ‏datalog‏؟#

∑ الگوریتم: انتخاب ‏shard‏ ‏‏datalog‏ برای یک ‏bucket shard‏

منبع در کد: src/rgw/driver/rados/rgw_datalog.cc :: RGWDataChangesLog::choose_oid

پیچیدگی: ‏O‏(طول نام ‏bucket‏)

text
pseudocode
shift = max(bucket_shard_id, 0)
index = (ceph_str_hash_linux(bucket.name) + shift) mod rgw_data_log_num_shards     # پیش‌فرض 128
# بدون XOR و بدون عدد اول؛ shardهای یک bucket روی shardهای پشت‌سرهم datalog می‌افتند

مثال عددی. ‏hash‏ نام ‏photos‏ برابر ۳۴۸۴۷۹۹۲۶۳ است و ۳۴۸۴۷۹۹۲۶۳ ‏mod 128‏ = ۳۱. اگر ‏bucket‏ ‏۱۱ ‏shard‏ ایندکس داشته باشد، تغییرات آن در ‏shard‏های ۳۱ تا ۴۱ از ‏datalog‏ ثبت می‌شوند. این پخش عمدی است و ‏Casey Bodley‏ در طراحی ‏reshard‏ چندسایتی آن را «خاصیت مهم مقیاس‌پذیری» نامیده است، چون چند ‏RGW‏ مقصد می‌توانند بار یک ‏bucket‏ پرتغییر را بین خود تقسیم کنند (هر ‏shard‏ ‏‏datalog‏ با ‏lease‏ دودقیقه‌ای فقط در اختیار یک ‏RGW‏ است).

نکته‌ی بیت‌های پایین. در هر گام ‏hash‏، مقدار ‏c<<4‏ مضرب ۱۶ است. پس ‏hash mod 16‏ فقط به نیبل بالای نویسه‌ها بستگی دارد (برای همه‌ی رقم‌های ‏ASCII‏ برابر ۳، برای ‏a‏ تا ‏o‏ برابر ۶ و برای ‏p‏ تا ‏z‏ برابر ۷). بنابراین همه‌ی نام‌های هم‌طولی که فقط در رقم‌ها با هم فرق دارند، ‏hash mod 16‏ یکسانی دارند. برای ۱۰۰۰ ‏bucket‏ با نام‌های ‏bucket-0000‏ تا ‏bucket-0999‏، ‏hash mod 16‏ همیشه ۸ است و فقط ۸ مقدار پایه (۸، ۲۴، …، ۱۲۰) از ۱۲۸ ممکن است. با ۱۱ ‏shard‏ برای هر ‏bucket‏، فقط ۸۸ ‏shard‏ از ۱۲۸ ‏shard‏ ‏‏datalog‏ کار دارند و ۴۰ ‏shard‏ همیشه خالی می‌مانند. همین تعداد ‏bucket‏ با نام تصادفی ۱۲ نویسه‌ای هر ۱۲۸ ‏shard‏ را پر می‌کند. ایندکس ‏bucket‏ این مشکل را ندارد، چون پیش از ‏mod‏، بایت پایین را با ‏XOR‏ به بالا می‌برد و بر عدد اول ۷۸۷۷ تقسیم می‌کند. این نکته روی درستی همگام‌سازی اثری ندارد. فقط موازی‌سازی و پخش بار بین ‏RGW‏های مقصد را کم می‌کند.

شکل ۵-۷: بالا: سهم shard های datalog از ۱۱۰۰۰ bucket shard (۱۰۰۰ bucket با نام شماره‌دار و ۱۱ shard برای هر کدام). فقط ۸۸ shard از ۱۲۸ shard کار دارند، در دسته‌هایی ۱۱تایی که از ۸، ۲۴، …، ۱۲۰ شروع می‌شوند. پایین: همین تعداد bucket با نام تصادفی که همه‌ی ۱۲۸ shard را می‌پوشاند. خط‌چین میانگین ایده‌آل، یعنی حدود ۸۶ bucket shard برای هر shard datalog است. شبیه‌سازی ما، بر پایه‌ی بازپیاده‌سازی choose_oid
شکل ۵-۷: بالا: سهم ‏shard‏های ‏datalog‏ از ۱۱۰۰۰ ‏bucket shard‏ (۱۰۰۰ bucket با نام شماره‌دار و ۱۱ ‏shard‏ برای هر کدام). فقط ۸۸ ‏shard‏ از ۱۲۸ ‏shard‏ کار دارند، در دسته‌هایی ۱۱تایی که از ۸، ۲۴، …، ۱۲۰ شروع می‌شوند. پایین: همین تعداد ‏bucket‏ با نام تصادفی که همه‌ی ۱۲۸ ‏shard‏ را می‌پوشاند. خط‌چین میانگین ایده‌آل، یعنی حدود ۸۶ ‏bucket shard‏ برای هر ‏shard‏ ‏‏datalog‏ است. شبیه‌سازی ما، بر پایه‌ی بازپیاده‌سازی ‏choose_oid‏

۵-۹-۲ پنجره‌ی ادغام ‏datalog‏#

∑ الگوریتم: افزودن ورودی ‏datalog‏ با پنجره و ‏renew‏

منبع در کد: src/rgw/driver/rados/rgw_datalog.cc :: RGWDataChangesLog::add_entry، renew_entries، renew_run

پیچیدگی: ‏O‏(۱) برای هر نوشتن و ‏O‏(تعداد ‏bucket shard‏ فعال) برای هر ‏renew‏

text
pseudocode
add_entry(bs):
    if watch روی sem-set سالم نیست: push(entry) ; return        # مسیر ایمن، بدون بهینه‌سازی
    status = change_status[bs]
    if now < status.cur_expiration:                       # در 30 ثانیه‌ی اخیر ثبت شده
        cur_cycle.add(bs) ; sem_set[index] += key ; return
    if status.pending: منتظر push جاری بمان ; return
    status.pending = true ; sent = now
    push(entry{key=bs, gen, timestamp=now})
    status.cur_expiration = sent + rgw_data_log_window      # از زمان شروع، نه پایان
renew_run: هر window × 3/4 ثانیه (با 30 → 22 ثانیه، تقسیم صحیح)
    برای هر bs در cur_cycle: push(entry) ; cur_expiration = now + window ; sem_set −= key

مثال عددی. فرض کنید از ثانیه‌ی ۰ تا ۱۲۰ هر ۱۰ میلی‌ثانیه یک ‏PUT‏ روی یک ‏bucket shard‏ انجام شود (۱۲۰۰۰ نوشتن). ورودی‌های ‏datalog‏ فقط در ثانیه‌های ۰، ۲۲، ۴۴، ۶۶، ۸۸ و ۱۱۰ ثبت می‌شوند: شش ورودی به جای ۱۲۰۰۰. اگر نوشتن‌ها پراکنده باشند (ثانیه‌های ۰، ۵، ۴۰، ۴۱ و ۱۰۰)، ورودی‌ها در ۰، ۲۲ (‏renew‏ برای نوشتن ثانیه‌ی ۵)، ۴۴ (‏renew‏ برای ۴۰ و ۴۱) و ۱۰۰ ثبت می‌شوند. چون بین ۴۴ و ۱۰۰ نوشتنی نیست، چیزی برای ‏renew‏ در ۶۶ و ۸۸ نمانده است. هر ورودی ‏datalog‏ فقط یعنی «این ‏bucket shard‏ را دوباره نگاه کن». جزئیات تغییر در ‏bilog‏ هست و مقصد آن را می‌خواند. پس ادغام چیزی را گم نمی‌کند، فقط حداکثر حدود یک پنجره به تأخیر کشف تغییر اضافه می‌کند. مجموعه‌ی شمارنده‌ی ‏sem-set‏ و مسیر «‏watch‏ سالم نیست» در کد ‏main‏ وجود دارند و هدفشان این است که اگر ‏RGW‏ پیش از ‏renew‏ بمیرد، کلیدهای در انتظار گم نشوند. نسخه‌های قدیمی‌تر این سازوکار را نداشتند. پیش از تکیه بر آن، کد نسخه‌ی خود را بررسی کنید.

شکل ۵-۸: بالا: یک bucket shard پرکار با ۱۲۰۰۰ نوشتن در ۱۲۰ ثانیه فقط شش ورودی datalog تولید می‌کند. اولی در لحظه‌ی نخستین نوشتن ثبت می‌شود و بقیه در تیک‌های ۲۲ ثانیه‌ای renew . پایین: با نوشتن پراکنده، renew فقط وقتی انجام می‌شود که در پنجره‌ی قبل نوشتنی مانده باشد. نوشتن ثانیه‌ی ۱۰۰ پس از انقضای پنجره بلافاصله ورودی می‌سازد
شکل ۵-۸: بالا: یک ‏bucket shard‏ پرکار با ۱۲۰۰۰ نوشتن در ۱۲۰ ثانیه فقط شش ورودی ‏datalog‏ تولید می‌کند. اولی در لحظه‌ی نخستین نوشتن ثبت می‌شود و بقیه در تیک‌های ۲۲ ثانیه‌ای ‏renew‏. پایین: با نوشتن پراکنده، ‏renew‏ فقط وقتی انجام می‌شود که در پنجره‌ی قبل نوشتنی مانده باشد. نوشتن ثانیه‌ی ۱۰۰ پس از انقضای پنجره بلافاصله ورودی می‌سازد
شکل ۵-۹: درخت تصمیم add_entry در main . ردیف اول مسیر ایمن را نشان می‌دهد: اگر watch روی sem-set قطع باشد، ورودی بی‌درنگ نوشته می‌شود. ردیف دوم ادغام در پنجره و انتظار برای push در جریان است. ردیف سوم push واقعی و چرخه‌ی renew است که پنجره را تمدید می‌کند و شمارنده‌ها را کم می‌کند
شکل ۵-۹: درخت تصمیم ‏add_entry‏ در ‏main‏. ردیف اول مسیر ایمن را نشان می‌دهد: اگر ‏watch‏ روی ‏sem-set‏ قطع باشد، ورودی بی‌درنگ نوشته می‌شود. ردیف دوم ادغام در پنجره و انتظار برای ‏push‏ در جریان است. ردیف سوم ‏push‏ واقعی و چرخه‌ی ‏renew‏ است که پنجره را تمدید می‌کند و شمارنده‌ها را کم می‌کند

۵-۹-۳ کدام ‏shard‏ از ‏mdlog‏؟#

∑ الگوریتم: انتخاب ‏shard‏ ‏‏mdlog‏ برای تغییر فراداده

منبع در کد: src/rgw/driver/rados/rgw_sal_rados.cc (hash_key = section + ":" + key)؛ src/rgw/driver/rados/rgw_metadata.cc :: rgw_shard_name، RGWMetadataLog::add_entry

پیچیدگی: ‏O‏(طول کلید)

text
pseudocode
hash_key = section + ":" + key            # مثلاً "user:alice" یا "bucket:photos"
shard = ceph_str_hash_linux(hash_key) mod rgw_md_log_max_shards     # پیش‌فرض 64
oid = "meta.log." + period_id + "." + shard

مثال عددی. ‏user:alice‏ به ‏shard‏ ‏۵۲ می‌رود، ‏bucket:photos‏ به ۵۰ و ‏bucket.instance:photos:a1b2c3d4.4567.1‏ به ۲۸. ‏mdlog‏ را فقط ‏master zone‏ تولید می‌کند و هر ‏period‏ لاگ خودش را دارد. همان نکته‌ی بیت‌های پایین اینجا شدیدتر است، چون ۶۴ هم توان ۲ است و جابه‌جایی ‏shard‏ هم وجود ندارد. ۱۰۰۰ کلید ‏bucket:bucket-0000‏ تا ‏bucket:bucket-0999‏ فقط در ۴ ‏shard‏ (۹، ۲۵، ۴۱ و ۵۷) می‌نشینند، هر کدام حدود ۲۵۰ کلید. در عمل نتیجه این است که ‏meta sync‏ اولیه‌ی چنین نام‌هایی روی ۴ ‏shard‏ متمرکز می‌شود، به جای اینکه بین ۶۴ ‏shard‏ پخش شود.

شکل ۵-۱۰: چپ: shard mdlog برای ۱۰۰۰ کلید bucket با نام شماره‌دار که فقط چهار shard را اشغال می‌کنند. راست: ۱۰۰۰ نام تصادفی که هر ۶۴ shard را می‌پوشانند، با ۷ تا ۳۱ کلید در هر shard . محاسبه با بازپیاده‌سازی rgw_shard_name
شکل ۵-۱۰: چپ: ‏shard‏ ‏‏mdlog‏ برای ۱۰۰۰ کلید ‏bucket‏ با نام شماره‌دار که فقط چهار ‏shard‏ را اشغال می‌کنند. راست: ۱۰۰۰ نام تصادفی که هر ۶۴ ‏shard‏ را می‌پوشانند، با ۷ تا ۳۱ کلید در هر ‏shard‏. محاسبه با بازپیاده‌سازی ‏rgw_shard_name‏

۵-۹-۴ ‏trim‏ لاگ‌ها: کمینه‌ی ‏marker‏ها روی همه‌ی ‏peer‏ها#

∑ الگوریتم: ‏trim‏ ‏‏datalog‏ بر پایه‌ی کمترین ‏marker‏ پایدار

منبع در کد: src/rgw/driver/rados/rgw_trim_datalog.cc :: DataLogTrimCR، take_min_markers، get_stable_marker

پیچیدگی: ‏O‏(peers × ‏shards‏)

text
pseudocode
هر rgw_sync_log_trim_interval (20 دقیقه):
    status[p] = REST GET /admin/log/?type=data&status از هر peer
    اگر حتی یک peer پاسخ ندهد: این دور trim نکن
    for shard i:
        stable(p) = state == FullSync ? next_step_marker : marker
        min_i = min_p stable(p)
        if min_i > last_trim[i]: trim(shard i, up to min_i) ; last_trim[i] = min_i

مثال نمادین. فرض کنید در ‏shard‏ ‏۵، ‏peer‏ ‏‏A‏ تا ‏marker‏ ‏‏m120‏ و ‏peer‏ ‏‏B‏ (که هنوز در ‏full sync‏ است) تا ‏next_step_marker‏ برابر ‏m087‏ جلو رفته‌اند و ‏trim‏ قبلی تا ‏m050‏ بوده است. پس ‏shard‏ ‏۵ تا ‏m087‏ کوتاه می‌شود. ورودی‌های ‏m088‏ تا ‏m120‏ می‌مانند تا ‏B‏ هم به آن‌ها برسد. ‏marker‏های واقعی به نوع ‏backend‏ ‏(‏cls_log‏ یا ‏FIFO‏) بستگی دارند و ‏m120‏ فقط نماد است. پیامد عملی این است که «همه‌ی ‏peer‏ها باید پاسخ دهند». اگر یک ‏zone‏ مدت طولانی از دسترس خارج باشد، ‏datalog‏ و ‏bilog‏های ‏zone‏ مبدأ کوتاه نمی‌شوند و ‏pool‏ لاگ رشد می‌کند. پیش از حذف دائمی یک ‏zone‏، آن را از ‏zonegroup‏ بیرون بیاورید و ‏period‏ را ‏commit‏ کنید.

شکل ۵-۱۱: چرخه‌ی trim datalog . هر ۲۰ دقیقه وضعیت همگام‌سازی از همه‌ی peer ها گرفته می‌شود. اگر یکی پاسخ ندهد، آن دور trim انجام نمی‌شود. برای هر shard کمترین marker پایدار حساب می‌شود (در full sync ، next_step_marker ) و فقط وقتی trim انجام می‌شود که از trim قبلی جلوتر باشد. ردیف پایین یک مثال نمادین است
شکل ۵-۱۱: چرخه‌ی ‏trim‏ ‏‏datalog‏. هر ۲۰ دقیقه وضعیت همگام‌سازی از همه‌ی ‏peer‏ها گرفته می‌شود. اگر یکی پاسخ ندهد، آن دور ‏trim‏ انجام نمی‌شود. برای هر ‏shard‏ کمترین ‏marker‏ پایدار حساب می‌شود (در ‏full sync‏، ‏‏next_step_marker‏) و فقط وقتی ‏trim‏ انجام می‌شود که از ‏trim‏ قبلی جلوتر باشد. ردیف پایین یک مثال نمادین است

۵-۱۰ ‏failover‏ و ‏failback‏#

شکل ۵-۱۲: تصمیم‌گیری هنگام از دست رفتن master zone : در خرابی کوتاه صبر کنید؛ در خرابی طولانی zone دیگری را master کنید
شکل ۵-۱۲: تصمیم‌گیری هنگام از دست رفتن ‏master zone‏: در خرابی کوتاه صبر کنید؛ در خرابی طولانی ‏zone‏ دیگری را ‏master‏ کنید

۵-۱۰-۱ وقتی ‏master zone‏ از دسترس خارج می‌شود#

در خرابی ‏master zone‏، ‏zone‏های دیگر همچنان داده را می‌پذیرند و سرویس ‏S3‏ برای ‏bucket‏های موجود ادامه دارد. اما عملیات فراداده‌ای که به ‏master‏ نیاز دارند (ساخت کاربر یا ‏bucket‏، تغییر سیاست) با خطا مواجه می‌شوند. اگر خرابی طولانی است، ‏zone‏ ثانوی را ‏master‏ کنید:

bashReef+
# روی کلاستر eu-2
radosgw-admin zone modify --rgw-zone=eu-2 --master --default
radosgw-admin period update --commit
ceph orch restart rgw.gold-eu2

۵-۱۰-۲ بازگشت ‏master‏ قدیمی#

وقتی ‏eu-1‏ برگشت، باید ‏period‏ جدید را از ‏master‏ فعلی بگیرد تا بفهمد دیگر ‏master‏ نیست:

bashReef+
# روی کلاستر eu-1
radosgw-admin realm pull --url=http://rgw-eu2.example.com:80 --access-key=<AK> --secret=<SK>
radosgw-admin period pull --url=http://rgw-eu2.example.com:80 --access-key=<AK> --secret=<SK>
ceph orch restart rgw.gold-eu1
radosgw-admin sync status
# اگر می‌خواهید master را برگردانید، پس از همگام شدن:
radosgw-admin zone modify --rgw-zone=eu-1 --master --default
radosgw-admin period update --commit
⚠ هشدار

اگر هر دو ‏zone‏ هم‌زمان خودشان را ‏master‏ بدانند (مثلاً به دلیل قطع شبکه بین سایت‌ها و ارتقای عجولانه)، دو تاریخچه‌ی ‏period‏ متفاوت ساخته می‌شود. پیش از ارتقای ‏zone‏ ثانوی، مطمئن شوید ‏master‏ قدیمی واقعاً از دسترس خارج است یا ‏RGW‏هایش متوقف شده‌اند، و پس از بازگشت آن قبل از هر تغییری ‏period‏ را ‏pull‏ کنید.

۵-۱۰-۳ یک ‏runbook‏ حداقلی#

  1. ۱. تأیید خرابی ‏master‏ از دو مسیر مستقل (پایش و تماس با تیم سایت).
  2. ۲. اعلام وضعیت و توقف تغییرات فراداده‌ای غیرضروری.
  3. ۳. ارتقای ‏zone‏ ثانوی به ‏master‏ و ‏commit period‏.
  4. ۴. به‌روزرسانی ‏DNS‏ یا ‏GSLB‏ تا کلاینت‌ها به ‏endpoint‏ سالم بروند.
  5. ۵. پس از بازگشت سایت اصلی: ‏pull period‏، بررسی ‏sync status‏ تا ‏caught up‏، و تصمیم درباره‌ی بازگرداندن ‏master‏.
  6. ۶. ثبت زمان‌ها و درس‌ها در لاگ حادثه.

۵-۱۱ ‏sync policy‏: کنترل دقیق همگام‌سازی#

از ‏Octopus‏، ‏sync policy‏ امکان می‌دهد به‌جای «همه‌ی ‏bucket‏ها در همه‌ی ‏zone‏ها»، تعیین کنید چه چیزی از کجا به کجا برود. سیاست‌ها در قالب گروه، ‏flow‏ (جریان بین ‏zone‏ها) و ‏pipe‏ (کدام ‏bucket‏ها) تعریف می‌شوند و در سطح ‏zonegroup‏ یا ‏bucket‏ اعمال می‌شوند.

bashReef+
# همه‌ی bucketها به‌طور پیش‌فرض «مجاز» اما غیرفعال
radosgw-admin sync group create --group-id=global --status=allowed
radosgw-admin sync group flow create --group-id=global --flow-id=eu-mesh \
  --flow-type=symmetrical --zones=eu-1,eu-2
radosgw-admin sync group pipe create --group-id=global --pipe-id=all \
  --source-zones='*' --source-bucket='*' --dest-zones='*' --dest-bucket='*'
radosgw-admin period update --commit
# فعال کردن همگام‌سازی فقط برای یک bucket
radosgw-admin sync group create --bucket=critical-data --group-id=critical --status=enabled
radosgw-admin sync group pipe create --bucket=critical-data --group-id=critical \
  --pipe-id=p1 --source-zones='*' --dest-zones='*'
radosgw-admin sync info --bucket=critical-data
وضعیت گروهمعنا
‏enabled‏همگام‌سازی انجام می‌شود
‏allowed‏مجاز است اما فقط اگر سیاست سطح پایین‌تر (‏bucket‏) فعالش کند
‏forbidden‏ممنوع؛ سیاست‌های سطح ‏bucket‏ هم نمی‌توانند فعالش کنند
💡 نکته

یک الگوی رایج: در سطح ‏zonegroup‏ وضعیت ‏allowed‏ و در سطح ‏bucket‏ فقط برای ‏bucket‏های حساس ‏enabled‏. این کار پهنای باند بین‌سایتی را برای ‏bucket‏های موقت یا قابل بازتولید هدر نمی‌دهد.

۵-۱۲ ‏archive zone‏#

‏archive zone‏ یک ‏zone‏ ویژه است که همه‌ی نسخه‌های همه‌ی اشیاء را نگه می‌دارد، حتی اگر در ‏zone‏های دیگر حذف یا بازنویسی شوند. ‏bucket‏ها در ‏archive zone‏ خودکار ‏versioning‏ دارند. این راهی برای حفاظت در برابر حذف تصادفی یا مخرب بدون تحمیل ‏versioning‏ به کاربران است.

bashReef+
radosgw-admin zone create --rgw-zonegroup=europe --rgw-zone=eu-archive \
  --endpoints=http://rgw-arc.example.com:80 --tier-type=archive \
  --access-key=<AK> --secret=<SK>
radosgw-admin period update --commit

۵-۱۳ ‏resharding‏ در ‏multisite‏#

پیش از ‏Reef‏، ‏resharding‏ پویا در ‏multisite‏ پشتیبانی نمی‌شد و ‏reshard‏ دستی هم همگام‌سازی ‏bucket‏ را مختل می‌کرد. از ‏Reef‏، ‏zonegroup feature‏ به نام ‏resharding‏ این امکان را می‌دهد که هر ‏zone‏ مستقل ‏bucket‏هایش را ‏reshard‏ کند بدون قطع تکثیر. طبق مستندات ‏zone features‏، این قابلیت فقط وقتی باید فعال شود که همه‌ی ‏zone‏ها به نسخه‌ی پشتیبان ارتقا یافته باشند:

bashReef+
radosgw-admin zonegroup modify --rgw-zonegroup=europe --enable-feature=resharding
radosgw-admin period update --commit
radosgw-admin sync status | grep features

۵-۱۴ کارایی و طراحی#

  • ‏RGW‏ اختصاصی همگام‌سازی: با گزینه‌ی ‏rgw_run_sync_thread‏ می‌توانید همگام‌سازی را روی نمونه‌های خاصی اجرا کنید و نمونه‌های رو به کاربر را از بار آن آزاد کنید. ‏endpoint‏های ‏zone‏ را روی نمونه‌های همگام‌ساز (یا ‏load balancer‏ آن‌ها) تنظیم کنید.
  • ‏pool‏ لاگ سریع: ‏datalog‏ و ‏bilog‏ در ‏pool‏ لاگ نوشته می‌شوند؛ این ‏pool‏ را روی ‏SSD‏ بگذارید.
  • تعداد ‏shard‏های ‏datalog‏: پیش‌فرض ۱۲۸ است و موازی‌سازی همگام‌سازی داده را تعیین می‌کند.
  • پایش عقب‌ماندگی: صادرکننده‌ی ‏Prometheus‏ در ‏MGR‏ و آمار ‏perf‏ کلاینت ‏RGW‏ معیارهای همگام‌سازی دارند. یک هشدار بر اساس «قدیمی‌ترین تغییر اعمال‌نشده» تعریف کنید.
  • ظرفیت لاگ: اگر یک ‏zone‏ مدتی طولانی قطع باشد، لاگ‌های تغییرات در ‏zone‏ دیگر انباشته می‌شوند. ‏trim‏ خودکار لاگ‌ها فقط وقتی انجام می‌شود که همه‌ی ‏zone‏ها آن تغییرات را دریافت کرده باشند.
◆ عمیق‌تر

‏RPO‏ در ‏multisite‏ ثابت نیست؛ به نرخ تغییر، پهنای باند، تأخیر و تعداد اشیای کوچک بستگی دارد. اشیای کوچک بسیار زیاد (مثلاً میلیون‌ها فایل چندکیلوبایتی) گلوگاه ‏CPU‏ و درخواست‌اند، نه پهنای باند. برای برآورد واقعی، با بار نمونه‌ی خودتان آزمون کنید و «‏oldest incremental change not applied‏» را در اوج بار بسنجید.

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

  • خودکارسازی با ‏ceph rgw‏ و ‏wizard‏ داشبورد (‏Tentacle‏) راه‌اندازی را به چند دقیقه رسانده است.
  • ‏zone features‏: علاوه بر ‏resharding‏، قابلیت‌هایی مثل ‏compress-encrypted‏ (‏Reef‏) و ‏notification_v2‏ (‏Squid‏) با سازوکار ‏zone feature‏ به‌صورت هماهنگ در همه‌ی ‏zone‏ها فعال می‌شوند.
  • ‏OIDC‏ در ‏multisite‏: در ‏Umbrella‏ پشتیبانی از ‏OIDC‏ در محیط چندسایتی اضافه شده است.
  • اصلاح‌های درستی همگام‌سازی: یادداشت‌های ‏Umbrella‏ اصلاح‌های متعددی را فهرست می‌کنند، از جمله حلقه‌ی بی‌پایان ‏full sync‏، خطای ‏segfault‏ هنگام راه‌اندازی، درخواست‌های ‏forward‏‌شده با ‏UNSIGNED-PAYLOAD‏، قفل‌گذاری ‏full sync‏ در سطح ‏bucket‏ و تغییر نام ‏zonegroup‏. اگر روی نسخه‌های قدیمی‌تر مشکلات همگام‌سازی دارید، ارتقا را جدی بگیرید.
  • ‏cloud sync module‏: یک ‏zone‏ با ‏--tier-type=cloud‏ می‌تواند داده را به یک سرویس ‏S3‏ بیرونی تکثیر کند.

۵-۱۶ جمع‌بندی و مرور#

۵-۱۶-۱ خلاصه‌ی فصل#

نکته‌های کلیدی
  • ‏realm‏ فضای نام سراسری است؛ ‏zonegroup‏ واحد تکثیر داده؛ ‏zone‏ یک کلاستر؛ ‏period‏ نسخه‌ی پیکربندی.
  • فراداده از ‏master zone‏ پخش می‌شود؛ داده بین ‏zone‏های یک ‏zonegroup‏ به‌صورت ناهمگام و ‏active-active‏ تکثیر می‌شود.
  • ‏multisite‏ نسخه‌ی ۲ همگام‌سازی را درون ‏radosgw‏ برده و ‏agent‏ قدیمی ‏Hammer‏ را حذف کرده است.
  • راه‌اندازی دستی با ‏radosgw-admin‏ یا خودکار با ‏ceph rgw realm bootstrap‏ و ‏ceph rgw zone create‏.
  • ‏sync status‏، ‏bucket sync status‏ و ‏sync error list‏ ابزارهای اصلی پایش‌اند.
  • ‏failover‏ یعنی ‏master‏ کردن ‏zone‏ دیگر و ‏commit period‏؛ پس از بازگشت، ‏period‏ را ‏pull‏ کنید.
  • ‏sync policy‏، ‏archive zone‏ و ‏zone feature‏ ‏resharding‏ ‏multisite‏ را منعطف‌تر کرده‌اند.

۵-۱۶-۲ اشتباهات رایج#

  • ‏endpoint‏هایی که فقط از یک سایت قابل‌دسترسی‌اند.
  • ساختن ‏zone‏ جدید به‌جای تغییر نام ‏zone‏ پیش‌فرض دارای داده.
  • نگه داشتن کلید کاربر ‏system‏ در اسکریپت‌ها و مخازن.
  • ارتقای عجولانه‌ی ‏zone‏ ثانوی به ‏master‏ در قطع شبکه‌ی موقت.
  • فعال کردن ‏zone feature‏ پیش از ارتقای همه‌ی ‏zone‏ها.
  • نادیده گرفتن عقب‌ماندگی تا وقتی کاربران گزارش دهند.

۵-۱۶-۳ تمرین عملی#

  1. ۱. دو کلاستر تک‌نودی بسازید و ‏multisite‏ را یک‌بار دستی و یک‌بار با ‏ceph rgw realm bootstrap‏ راه بیندازید. زمان و تعداد گام‌ها را مقایسه کنید.
  2. ۲. هزار شیء در ‏zone‏ اصلی بنویسید و با ‏sync status‏ و ‏bucket sync status‏ روند همگام‌سازی را ثبت کنید.
  3. ۳. ‏RGW‏های ‏zone‏ اصلی را متوقف کنید، در ‏zone‏ ثانوی شیء بنویسید و ساخت ‏bucket‏ را امتحان کنید. پیام خطا را توضیح دهید. سپس ‏failover‏ کامل انجام دهید.
  4. ۴. یک ‏sync policy‏ بسازید که فقط ‏bucket‏ی با نام ‏critical-*‏ همگام شود و نشان دهید ‏bucket‏های دیگر همگام نمی‌شوند.
  5. ۵. یک ‏archive zone‏ اضافه کنید، شیئی را در ‏zone‏ اصلی حذف کنید و نسخه‌ی آن را در ‏archive zone‏ پیدا کنید.

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

  1. ۱. تفاوت نقش ‏zonegroup‏ و ‏realm‏ در تکثیر داده و فراداده چیست؟
  2. ۲. ‏period‏ چیست و چرا پس از تغییرات ساختاری باید ‏commit‏ شود؟
  3. ۳. سه تفاوت اصلی ‏multisite‏ نسخه‌ی ۲ با پیکربندی ‏federated‏ در ‏Hammer‏ کدام‌اند؟
  4. ۴. چرا ساخت ‏bucket‏ در ‏zone‏ غیر ‏master‏ هنگام از دست رفتن ‏master‏ شکست می‌خورد اما نوشتن شیء نه؟
  5. ۵. کاربر ‏system‏ چه نقشی دارد و چرا باید به‌شدت حفاظت شود؟
  6. ۶. در خروجی ‏sync status‏ عبارت «‏oldest incremental change not applied‏» چه اطلاعاتی می‌دهد؟
  7. ۷. گام‌های ‏failover‏ و بازگرداندن ‏master‏ قدیمی را بنویسید.
  8. ۸. تفاوت وضعیت‌های ‏enabled‏، ‏allowed‏ و ‏forbidden‏ در ‏sync policy‏ چیست؟
  9. ۹. ‏archive zone‏ چه مشکلی را حل می‌کند؟
  10. ۱۰. چرا ‏resharding‏ پیش از ‏Reef‏ در ‏multisite‏ مشکل‌ساز بود و امروز چطور حل شده است؟

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

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

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

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