> ## Content Index
> Fetch the complete content index at: https://arabroot.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# Ceph للمبتدئين: تخزين موزع يتحمل تعطل السيرفرات
- URL: https://arabroot.io/articles/ceph-للمبتدئين-تخزين-موزع-يتحمل-تعطل-السيرفرات/
- Published: 2026-10-13T04:00:00.000Z
- Updated: 2026-10-12T21:17:56.000Z
- Description: كيف يوزع Ceph البيانات على عدة سيرفرات ويعيد نسخها وحده عندما يتعطل قرص، ومتى يكون أكبر من حاجتك.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, هندسة البرمجيات وتصميم الأنظمة, التشغيل والنسخ الاحتياطي, الاستضافة الذاتية

في مقال [أنواع التخزين](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-block-%D9%88-file-%D9%88-object-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/#ceph) خصصنا لـ Ceph قسماً واحداً، عرفنا فيه أنه يجمع أقراص عدة سيرفرات في مخزن واحد ويقدم منه تخزين الكتل والملفات والكائنات معاً، وانتهينا إلى أنه يحتاج ثلاثة سيرفرات على الأقل وفريقاً يفهم معنى أن تصبح البيانات في حالة Degraded. وبقيت الأسئلة التي يسألها كل من يقترب من Ceph فعلاً: كيف يعرف Ceph مكان كل كائن دون جدول مركزي، وما الفرق بين ثلاث نسخ والترميز التصحيحي Erasure Coding، وماذا يحدث بالضبط عندما يتوقف قرص في منتصف الليل، ولماذا يقول الجميع إن ثلاثة سيرفرات هي الحد الأدنى بينما التجربة تقول إنها لا تكفي ليصلح Ceph نفسه. وهذا ما سوف نجيب عنه هنا بالشرح ثم بتجربة حقيقية على Cluster صغير نوقف فيه الأقراص واحداً بعد الآخر ونراقب ما يحدث بالثانية.

وسوف نناقش في هذا المقال ما يلي:

- طبقة RADOS والخدمات الأربع MON وMGR وOSD وMDS، وما الذي يحتاج كل منها.
- كيف يحسب الكلاينت مكان الكائن عبر مجموعات التوزيع Placement Groups وخوارزمية CRUSH.
- ال Pools بالنسخ الكاملة Replication أم بالترميز التصحيحي Erasure Coding، ومعنى size وmin\_size.
- الواجهات الثلاث RBD وCephFS وRGW، وأين تجدها في Proxmox وKubernetes.
- حالات ال PG التي تراها في `ceph -s` وما تعنيه كل منها.
- متى يكون Ceph أكبر من حاجتك، بالأرقام من توثيقه: عدد السيرفرات والذاكرة والشبكة.
- تجربة عملية على Ceph 20.2.4 بأربعة OSDs، نوقف فيها قرصاً ثم قرصين ثم ثلاثة.

## RADOS والخدمات التي تعمل فوقه

كل ما تحفظه في Ceph ينتهي في الأسفل ككائنات Objects داخل طبقة اسمها RADOS، وهي اختصار لعبارة Reliable Autonomic Distributed Object Store، والكلمة المهمة هنا Autonomic، أي أن الأقراص نفسها هي التي تنسخ البيانات وتكتشف الأعطال وتعيد التوزيع، وليس سيرفراً مركزياً يأمرها، كما يشرح [توثيق البنية](https://docs.ceph.com/en/tentacle/architecture/?ref=arabroot.io). وقد عرفنا الخدمات الأربع في [مقال أنواع التخزين](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-block-%D9%88-file-%D9%88-object-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/#ceph)، لذلك نلخصها هنا بما يهمك عند التخطيط فقط:

- OSD (Object Storage Daemon): خدمة واحدة لكل قرص، تحفظ الكائنات بصيغة BlueStore مباشرة على القرص دون نظام ملفات بينهما، وتنسخها وتستعيدها. وعدد ال OSDs هو عدد الأقراص تقريباً، وهي التي تستهلك أغلب الذاكرة والمعالج.
- MON (Monitor): يحفظ خرائط ال Cluster، أي من هي ال OSDs وأيها يعمل وكيف تتوزع البيانات، والتوثيق في [مرجع إعداد ال Monitors](https://docs.ceph.com/en/tentacle/rados/configuration/mon-config-ref/?ref=arabroot.io) يشترط أغلبية تعمل لتكوين النصاب Quorum، ولذلك تجدها بعدد فردي، ثلاثة أو خمسة.
- MGR (Manager): يجمع المقاييس ويشغل لوحة التحكم Dashboard ووحدات مثل الضبط التلقائي لعدد ال PGs، ويعمل منه واحد نشط وآخر احتياطي.
- MDS (Metadata Server): لا تحتاجه إلا مع نظام الملفات CephFS، لأنه يحفظ شجرة المجلدات والصلاحيات.

والفكرة التي تجعل Ceph مختلفاً عن NAS كبير هي أن الكلاينت يتصل بال OSDs مباشرة، فلا يوجد سيرفر في المنتصف تمر عبره كل البيانات ويصبح عنق زجاجة أو نقطة فشل واحدة SPOF، وقد شرحنا معنى نقطة الفشل الواحدة في [مفاهيم التوفر العالي](https://arabroot.io/articles/%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D8%A7%D9%84%D8%AA%D9%88%D9%81%D8%B1-%D8%A7%D9%84%D8%B9%D8%A7%D9%84%D9%8A-high-availability-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/#spof). وقد يتساءل البعض: إذا لم يكن هناك سيرفر في المنتصف، فكيف يعرف الكلاينت أي قرص يسأل عن الملف؟ والإجابة في القسم التالي.

## كيف يجد Ceph مكان كل كائن: مجموعات التوزيع Placement Groups وCRUSH

الحل البسيط لمعرفة مكان كل كائن هو جدول كبير: اسم الكائن وأرقام الأقراص التي تحمله، وهذا ما تفعله أنظمة كثيرة، لكن الجدول يصبح بحجم مليارات السطور، ويجب أن يسأله كل كلاينت قبل كل قراءة، ويجب أن يتحدث عند كل عطل، فيصبح هو نفسه المشكلة. وCeph لا يحفظ هذا الجدول أصلاً، وإنما يحسب المكان في كل مرة بخطوتين كما في [توثيق البنية](https://docs.ceph.com/en/tentacle/architecture/?ref=arabroot.io): يحسب الهاش Hash لاسم الكائن، فيعرف إلى أي مجموعة توزيع Placement Group ينتمي من بين مجموعات ال Pool، ثم يمرر رقم المجموعة وخريطة ال Cluster إلى خوارزمية CRUSH فتعطيه قائمة ال OSDs التي تحمل هذه المجموعة، وأولها هو ال Primary. والصورة التالية تبين ذلك بالأرقام الحقيقية من التجربة في آخر المقال:

![مخطط يبدأ بكلاينت يأخذ خريطة ال Cluster من MON، ثم ثلاث خطوات مرقمة: الكائن obj-7 في Pool اسمه rep، ثم الهاش 1c1ed88d الذي يعطي PG 2.d، ثم CRUSH يعطي acting [1, 3, 0] و primary osd.1، وتحتها أربعة سيرفرات node0 إلى node3، والكلاينت يكتب إلى osd.1 في node1 الذي ينسخ إلى osd.3 وosd.0، بينما osd.2 ليس في هذه المجموعة](https://arabroot.io/content/images/2026/10/ceph-basics-01-crush.webp)

الكلاينت يحسب المكان بنفسه، ويكتب إلى ال Primary الذي ينسخ إلى البقية قبل أن يرد

لاحظ أن ال Primary بحسب التوثيق لا يخبر الكلاينت بنجاح الكتابة إلا بعد أن تؤكد ال OSDs الأخرى أنها حفظت نسختها، وهذا يعني أن الكتابة في Ceph متسقة Consistent، فما تقرأه بعد الكتابة هو ما كتبته، والثمن أن زمن الكتابة هو زمن أبطأ نسخة في المجموعة، ولهذا يهم Ceph كثيراً أن تكون الشبكة سريعة. وأما لماذا توجد مجموعات التوزيع بين الكائن وال OSD، فالسبب أن تتبع ملايين الكائنات واحداً واحداً عند العطل مكلف، بينما تتبع بضع مئات من المجموعات سهل، فعندما يسقط قرص لا يسأل Ceph أي كائن تأثر، وإنما أي مجموعة تأثرت.

وعدد المجموعات في كل Pool يحدده اليوم الضبط التلقائي PG Autoscaler، والهدف بحسب [توثيق مجموعات التوزيع](https://docs.ceph.com/en/tentacle/rados/operations/placement-groups/?ref=arabroot.io) هو الإعداد `mon_target_pg_per_osd` وقيمته الافتراضية 100 مجموعة لكل OSD، والتوثيق يوصي بـ 200 لكل Cluster إلا الصغيرة جداً. وفي ال CRUSH Map تحدد أيضاً مجال الفشل Failure Domain، أي ما الذي لا يجوز أن يحمل نسختين من المجموعة نفسها: القرص، أو السيرفر Host وهو الافتراضي، أو الخزانة Rack، أو الغرفة. وإذا كان مجال الفشل هو السيرفر فإن ثلاث نسخ تحتاج ثلاثة سيرفرات مختلفة على الأقل، وهذه النقطة سوف تعود إلينا في التجربة.

## ال Pools: ثلاث نسخ أم الترميز التصحيحي Erasure Coding؟

ال Pool هو قسم منطقي من ال Cluster له قاعدته الخاصة في الحماية، وتنشئ لكل استخدام Pool مستقلاً: واحد لأقراص الآلات الافتراضية، وآخر لملفات CephFS، وآخر لبيانات S3\. وهناك نوعان من الحماية:

- النسخ الكاملة Replication: كل كائن يحفظ كاملاً على عدد من ال OSDs هو `size`، والافتراضي ثلاث نسخ. ومعه الإعداد `min_size`، وهو أقل عدد من النسخ يجب أن يكون متاحاً حتى يقبل Ceph القراءة والكتابة، والافتراضي اثنتان مع size 3\. وهذا يعني أنك تتحمل سقوط نسخة واحدة دون أن يتوقف شيء، وسقوط نسختين دون أن تضيع البيانات، لكن الكتابة تتوقف.
- الترميز التصحيحي Erasure Coding: يقسم الكائن إلى k أجزاء بيانات، ويحسب منها m أجزاء ترميز Coding Chunks، ويضع كل جزء على OSD في مجال فشل مختلف، فتستطيع أن تفقد أي m أجزاء وتعيد بناء الكائن من الباقي. والمساحة الإضافية بحسب [توثيق ال Erasure Code](https://docs.ceph.com/en/tentacle/rados/operations/erasure-code/?ref=arabroot.io) هي (k+m)/k، أي 1.5 مرة مع 4+2، بدل 3 مرات مع النسخ الكاملة.

والصورة التالية تبين الكائن نفسه بالطريقتين، مع ما قاسه `ceph df` في التجربة:

![مقارنة بين لوحتين: في الأولى Pool بالنسخ الكاملة size 3 يحفظ الكائن obj-7 بحجم 4 MiB في ثلاث نسخ كاملة على ثلاثة سيرفرات، والمساحة ثلاثة أضعاف، وفي التجربة 200 MiB أخذت 600 MiB. وفي الثانية Pool بالترميز التصحيحي k=2 وm=1 يقسم الكائن إلى جزأي بيانات بحجم 2 MiB وجزء ترميز بحجم 2 MiB، والمساحة 1.5 ضعف، وفي التجربة 200 MiB أخذت 300 MiB](https://arabroot.io/content/images/2026/10/ceph-basics-02-replica-ec.webp)

النسخ الكاملة تأخذ ثلاثة أضعاف المساحة، والترميز التصحيحي يأخذ (k+m)/k

وقد يتساءل البعض: إذا كان الترميز التصحيحي يوفر نصف المساحة، فلماذا لا نستخدمه دائماً؟ والإجابة أن كل كتابة تحتاج حساب أجزاء الترميز، وكل تعديل صغير في وسط الكائن يحتاج قراءة الأجزاء وإعادة حسابها، والاستعادة بعد العطل تقرأ من عدة أقراص لتبني جزءاً واحداً، لذلك يناسب البيانات الكبيرة التي تكتب مرة وتقرأ كثيراً، مثل ملفات S3 والنسخ الاحتياطية، ولا يناسب أقراص قواعد البيانات. وعندك ثلاث قواعد من التوثيق نفسه:

- التوثيق يقول إن الملفات الشخصية Profiles التي فيها m=1 «لا ينصح بها بشدة» للبيانات الحقيقية، لأنك سوف تفقد الوصول أثناء الصيانة وتفقد البيانات إذا تزامن عطلان، ويقترح 4+2 أو 6+3 إذا لم تكن متأكداً.
- الترميز التصحيحي يحتاج k+m مجالات فشل على الأقل، أي ستة سيرفرات مع 4+2، والتوثيق يفضل k+m+1 حتى يبقى مكان للاستعادة.
- التوثيق يوصي بأن تكون min\_size بقيمة k+1 أو أكثر حتى لا تقبل الكتابة وأنت على حافة الضياع.

وفي التجربة أنشأنا Pool بـ k=2 وm=1 لنرى المساحة فقط، ولاحظ في المخرج أدناه أن Ceph 20.2.4 أعطاه min\_size 2، أي k وليس k+1، وهذا مثال جيد على أن القيمة الافتراضية ليست دائماً القيمة الآمنة، فراجع `ceph osd pool ls detail` بعد إنشاء أي Pool.

## ثلاث واجهات فوق RADOS: RBD وCephFS وRGW

لا يتعامل تطبيقك مع RADOS مباشرة في أغلب الحالات، وإنما مع إحدى ثلاث واجهات، ولكل منها نوع التخزين الذي شرحناه في [أنواع التخزين](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-block-%D9%88-file-%D9%88-object-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/#three-types):

- RBD (RADOS Block Device): قرص كتل Block Device مقسم إلى كائنات بحجم 4 MiB موزعة على ال Cluster، تستخدمه الآلات الافتراضية في Proxmox وOpenStack، ويدعم ال Snapshots والنسخ Clones، كما في [توثيق RBD](https://docs.ceph.com/en/tentacle/rbd/?ref=arabroot.io). وهذا هو الاستخدام الأشهر في الشركات الصغيرة والمتوسطة، فـ Proxmox VE يثبت Ceph ويديره من واجهته في ما يسميه [Hyper-Converged Ceph Cluster](https://pve.proxmox.com/wiki/Deploy%5FHyper-Converged%5FCeph%5FCluster?ref=arabroot.io)، أي أن السيرفرات نفسها تشغل الآلات الافتراضية وتحمل الأقراص، فتنتقل الآلة من سيرفر إلى آخر دون نسخ قرصها.
- CephFS: نظام ملفات مشترك تربطه عدة أجهزة في الوقت نفسه بصلاحيات POSIX، ويحتاج MDS، كما في [توثيق CephFS](https://docs.ceph.com/en/tentacle/cephfs/?ref=arabroot.io)، وهو البديل الموزع لمشاركة NFS من سيرفر واحد التي شرحناها في [الفرق بين NAS وSAN](https://arabroot.io/articles/%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-nas-%D9%88-san-%D9%88%D9%85%D8%AA%D9%89-%D9%8A%D9%83%D9%81%D9%8A%D9%83-das/#nfs).
- RGW (RADOS Gateway): بوابة متوافقة مع S3، وقد قارناها بأدوات S3 الأخرى في [أنواع التخزين](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-block-%D9%88-file-%D9%88-object-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/#ceph-rgw)، وشرحنا الأدوات الأخف منها في [تخزين S3 على سيرفرك](https://arabroot.io/articles/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-s3-%D8%B9%D9%84%D9%89-%D8%B3%D9%8A%D8%B1%D9%81%D8%B1%D9%83-%D8%A7%D9%84%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D9%88%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D8%A9/).

وفي Kubernetes يدير Ceph عادة مشروع [Rook](https://rook.io/?ref=arabroot.io)، وهو مشروع وصل إلى مستوى Graduated في CNCF في 7 أكتوبر 2020 بحسب [صفحته في CNCF](https://www.cncf.io/projects/rook/?ref=arabroot.io)، فيعطي ال Pods أقراصاً من RBD أو مجلدات من CephFS، وقد ذكرنا لماذا يحتاج Kubernetes تخزيناً موزعاً أصلاً في [هل تحتاج Kubernetes فعلاً؟](https://arabroot.io/articles/%D9%87%D9%84-%D8%AA%D8%AD%D8%AA%D8%A7%D8%AC-kubernetes-%D9%81%D8%B9%D9%84%D8%A7/).

## حالات ال PG وما تعنيه HEALTH\_WARN

أول ما ستراه عند أي مشكلة هو سطر طويل مثل `active+undersized+degraded`، وهو ليس رسالة خطأ واحدة، وإنما مجموعة صفات للمجموعة في اللحظة نفسها. والجدول التالي يلخص الحالات التي ظهرت في تجربتنا، بتعريفها من [توثيق حالات ال PG](https://docs.ceph.com/en/tentacle/rados/operations/pg-states/?ref=arabroot.io):

| الحالة                  | المعنى                                         | هل القراءة والكتابة تعمل؟             |
| ----------------------- | ---------------------------------------------- | ------------------------------------- |
| active                  | Ceph يقبل الطلبات على هذه المجموعة             | نعم                                   |
| clean                   | كل الكائنات منسوخة بالعدد الصحيح               | نعم، وهذه هي الحالة الطبيعية          |
| undersized              | عدد النسخ المتاحة أقل من size                  | نعم ما دامت النسخ لا تقل عن min\_size |
| degraded                | بعض الكائنات ليس لها العدد الصحيح من النسخ بعد | نعم ما دامت المجموعة active           |
| recovering وbackfilling | Ceph ينسخ البيانات ليعيد العدد الصحيح          | نعم، وقد يبطؤ الأداء                  |
| remapped                | المجموعة على OSDs غير التي تحددها CRUSH مؤقتاً | نعم                                   |
| peered                  | النسخ المتاحة أقل من min\_size                 | لا، الطلبات تنتظر                     |
| down                    | نسخة تحمل بيانات ضرورية متوقفة                 | لا، المجموعة خارج الخدمة              |

والقاعدة العملية من هذا الجدول أن ما يجب أن يوقظك ليلاً هو كلمة active عندما تختفي، أي peered أو down أو inactive، أما degraded وحدها فتعني أن Ceph يعمل وأنه يحتاج وقتاً أو مكاناً ليصلح نفسه، وهذا هو الفرق الذي سوف نراه بالأرقام في التجربة.

## ماذا يحدث عندما يتوقف قرص؟ down ثم out ثم الاستعادة

عندما يتوقف OSD لا يبدأ Ceph بالنسخ فوراً، وإنما يمر بمرحلتين لكل منهما توقيت تجده في [توثيق تفاعل ال Monitors وال OSDs](https://docs.ceph.com/en/tentacle/rados/configuration/mon-osd-interaction/?ref=arabroot.io):

1. down: ال OSDs تتبادل نبضات Heartbeats، فإذا لم يرد OSD خلال 20 ثانية (`osd_heartbeat_grace`) يبلغ عنه جيرانه، ويحتاج ال Monitor بلاغين من مكانين مختلفين (`mon_osd_min_down_reporters` بقيمة 2) ليعلنه down. ومن هنا تصبح مجموعاته undersized وdegraded، لكنها تبقى active.
2. out: إذا بقي down مدة `mon_osd_down_out_interval`، وقيمتها الافتراضية 600 ثانية، يخرجه ال Monitor من التوزيع، فتحسب CRUSH أماكن جديدة لمجموعاته، وتبدأ الاستعادة Recovery بنسخ البيانات من النسخ الباقية إلى الأماكن الجديدة.

والانتظار عشر دقائق قبل النسخ مقصود، والسبب أن أغلب حالات التوقف هي إعادة تشغيل سيرفر أو تحديث نواة، ولو بدأ Ceph بنسخ مئات الجيجابايتات عند كل إعادة تشغيل لأتعب الشبكة والأقراص دون فائدة. وفي التجربة خفضنا القيمة إلى 60 ثانية حتى نرى المراحل كلها بسرعة، والصورة التالية تبين التوقيت الحقيقي:

![خط زمني بأربع نقاط: عند 17:11:11 أوقفنا osd.2، وعند 17:11:15 تقريباً أبلغ عنه الجيران فأصبحت المجموعات active+undersized+degraded والقراءة والكتابة مستمرة، وعند 17:12:10 تقريباً أخرجه ال Monitor بعد mon_osd_down_out_interval الذي كان 60 ثانية في التجربة و600 ثانية افتراضياً، فاختارت CRUSH سيرفراً آخر للنسخة الثالثة، وعند 17:12:29 عادت المجموعات 65 كلها active+clean بثلاث نسخ](https://arabroot.io/content/images/2026/10/ceph-basics-03-recovery.webp)

من إيقاف القرص إلى عودة كل المجموعات active+clean في 78 ثانية، منها 60 ثانية انتظار مقصود

## متى يكون Ceph أكبر من حاجتك؟

هذا القسم أهم من كل الأوامر، لأن أكثر مشاكل Ceph التي تقرأ عنها في المنتديات سببها أنه وضع في مكان لا يناسبه. والأرقام التالية من [توصيات العتاد](https://docs.ceph.com/en/tentacle/start/hardware-recommendations/?ref=arabroot.io) في توثيق Tentacle:

| الخدمة | المعالج                                                           | الذاكرة                                                        | الشبكة                                    |
| ------ | ----------------------------------------------------------------- | -------------------------------------------------------------- | ----------------------------------------- |
| كل OSD | Thread واحد كحد أدنى و3 منصوح بها لكل قرص HDD، و4 و6 لكل قرص NVMe | 4 GB أو أكثر، و2 إلى 4 GB تعمل ببطء، وأقل من 2 GB غير منصوح به | 1 Gb/s كحد أدنى، والمنصوح 25 Gb/s أو أكثر |
| كل MON | نواتان على الأقل                                                  | 5 GB أو أكثر                                                   | 1 Gb/s، والمنصوح 10 Gb/s أو أكثر          |
| كل MDS | نواتان على الأقل، والسرعة أهم من العدد                            | 8 GiB أو أكثر                                                  | 1 Gb/s، والمنصوح 10 Gb/s أو أكثر          |

وفي الصفحة نفسها ثلاثة أرقام أخرى تستحق أن تتوقف عندها: القيمة الافتراضية لـ `osd_memory_target` هي 4 GiB لكل OSD، أي أن سيرفراً فيه 12 قرصاً يحتاج قرابة 48 GiB لل OSDs وحدها، والتوثيق يوصي بقرص لا يقل عن 1 TiB لكل OSD لأن الأقراص الأصغر يذهب جزء كبير منها للبيانات الوصفية، ثم مثال الشبكة: نسخ 1 TiB عبر شبكة 1 Gb/s يأخذ ثلاث ساعات، و10 TiB يأخذ ثلاثين ساعة، بينما يأخذ 1 TiB عشرين دقيقة على 10 Gb/s. وهذا الرقم الأخير هو الذي يحدد كم ساعة تبقى بياناتك بنسختين فقط بعد سقوط سيرفر. وأما الشبكة الثانية للنسخ Cluster Network فالتوثيق في [إعداد الشبكة](https://docs.ceph.com/en/tentacle/rados/configuration/network-config-ref/?ref=arabroot.io) يقول إن Ceph يعمل جيداً بشبكة عامة واحدة في كثير من الحالات خاصة مع 25GE أو أسرع، وإن الشبكة الثانية تفيد مع حركة كلاينت عالية.

ثم عدد السيرفرات: ثلاث نسخ مع مجال فشل بمستوى السيرفر تحتاج ثلاثة سيرفرات لتعمل، لكن ثلاثة سيرفرات لا تكفي ليصلح Ceph نفسه، لأنه إذا سقط سيرفر فلا يوجد سيرفر رابع يضع عليه النسخة الثالثة، فيبقى ال Cluster في حالة undersized حتى يعود السيرفر أو تضيف غيره. والصورة التالية تبين الحالات الثلاث كما رأيناها في التجربة:

![ثلاث لوحات لأربعة سيرفرات مع Pool بـ size 3 وmin_size 2: في الأولى سقط سيرفر واحد فأعيدت النسخة على السيرفر الرابع وعاد ال Cluster إلى active+clean في 78 ثانية، وفي الثانية بقي سيرفران فبقيت المجموعات undersized والكتابة تعمل لأنه لا يوجد سيرفر ثالث للنسخة، وفي الثالثة بقي سيرفر واحد فأصبحت النسخة أقل من min_size وتوقفت القراءة والكتابة](https://arabroot.io/content/images/2026/10/ceph-basics-04-failure-domains.webp)

مع size 3 يحتاج Ceph سيرفراً رابعاً ومساحة فارغة ليعيد النسخة الثالثة بعد سقوط سيرفر

لذلك إذا كان لديك سيرفر أو اثنان، أو شبكة 1 Gb/s فقط، أو لا يوجد في فريقك من يتابع التخزين، فالأنسب أن تبقى مع ZFS على سيرفر واحد مع نسخ متماثل بـ send وreceive إلى سيرفر آخر كما شرحنا في [ZFS للمبتدئين](https://arabroot.io/articles/zfs-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%83%D9%8A%D9%81-%D9%8A%D8%AD%D9%85%D9%8A-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA%D9%83-%D9%85%D9%86-%D8%A7%D9%84%D8%AA%D9%84%D9%81-%D8%A7%D9%84%D8%B5%D8%A7%D9%85%D8%AA/#send-receive)، وإذا كان ما تريده هو S3 فقط فأدوات مثل Garage وSeaweedFS أخف بكثير كما في [مقارنة خوادم S3](https://arabroot.io/articles/%D8%A7%D9%8A-%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-s3-%D9%85%D8%B3%D8%AA%D8%B6%D8%A7%D9%81-%D8%B0%D8%A7%D8%AA%D9%8A%D8%A7-%D9%8A%D9%86%D8%A7%D8%B3%D8%A8%D9%83/). ويصبح Ceph منطقياً عندما يجتمع الآتي: أربعة سيرفرات أو أكثر، وشبكة 10 Gb/s على الأقل، وحاجة لتخزين كتل مشترك للآلات الافتراضية أو لأكثر من نوع تخزين من نظام واحد، ومن يتابعه يومياً. وأما المقارنة مع GlusterFS والبدائل الأخرى فلها مقال مستقل هو [Ceph أم GlusterFS؟](https://arabroot.io/articles/ceph-%D8%A7%D9%85-glusterfs-%D8%A7%D9%8A-%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%85%D9%88%D8%B2%D8%B9-%D8%AA%D8%AE%D8%AA%D8%A7%D8%B1-%D8%A7%D9%84%D9%8A%D9%88%D9%85/).

## تجربة عملية: Cluster صغير بأربعة OSDs على Docker

الآن سوف نقوم بتشغيل Cluster حقيقي من Ceph 20.2.4 (Tentacle) على جهاز واحد، فيه MON واحد وMGR واحد وأربعة OSDs، كل OSD في Container باسم سيرفر مختلف حتى تعامله CRUSH كسيرفر مستقل. وقبل أن نبدأ عليك أن تعرف ما الذي يختلف عن الإنتاج، والسبب في كل اختلاف:

- لم نستخدم cephadm، وهي الأداة الرسمية للتثبيت، لأنها بحسب [توثيقها](https://docs.ceph.com/en/tentacle/cephadm/install/?ref=arabroot.io) تحتاج Python 3 وsystemd وPodman أو Docker على سيرفر حقيقي، وLVM2 لتجهيز الأقراص، وهذا يعني أقراصاً حقيقية لا نريد أن نلمسها على جهاز التجربة. لذلك اتبعنا خطوات [التثبيت اليدوي](https://docs.ceph.com/en/latest/install/manual-deployment/?ref=arabroot.io) نفسها داخل Containers من Docker Image الرسمية `quay.io/ceph/ceph:v20.2.4`.
- كل OSD يكتب في ملف متفرق Sparse File بحجم 4 GiB داخل Volume خاص به، وليس على قرص، فلا شيء في التجربة يلمس أقراص جهازك أو أقسامها.
- كل ال Containers على جهاز واحد، لذلك الأرقام هنا للتوضيح فقط، وستجد تحذير `BLUESTORE_SLOW_OP_ALERT` طوال التجربة، وسببه أن BlueStore يكتب على ملفات داخل جهاز افتراضي بدل أقراص حقيقية.

### ملفات التجربة

قم بإنشاء مجلد للتجربة، ثم أنشئ فيه الملف compose.yaml التالي:

```yaml
# Lab only: a tiny Ceph cluster (1 MON, 1 MGR, 4 OSDs on sparse files) on one machine.
# Nothing here touches host disks: every OSD writes a file inside its own Docker volume.
name: lab-stor2-ceph
x-ceph: &ceph
  image: quay.io/ceph/ceph:v20.2.4
  entrypoint: ["/bin/bash"]
  volumes:
    - etc:/etc/ceph
    - ./:/lab:ro
networks:
  net:
    name: lab-stor2-net
    ipam:
      config:
        - subnet: 10.250.42.0/24
volumes:
  etc: { name: lab-stor2-etc }
  osd0: { name: lab-stor2-osd0 }
  osd1: { name: lab-stor2-osd1 }
  osd2: { name: lab-stor2-osd2 }
  osd3: { name: lab-stor2-osd3 }
services:
  mon:
    <<: *ceph
    container_name: lab-stor2-mon
    hostname: mon
    command: ["/lab/mon.sh"]
    networks: { net: { ipv4_address: 10.250.42.10 } }
  mgr:
    <<: *ceph
    container_name: lab-stor2-mgr
    hostname: mgr
    command: ["/lab/mgr.sh"]
    networks: [net]
    depends_on: [mon]
  osd0: &osd
    <<: *ceph
    container_name: lab-stor2-osd0
    hostname: node0
    command: ["/lab/osd.sh"]
    networks: [net]
    depends_on: [mon]
    volumes: [etc:/etc/ceph, ./:/lab:ro, osd0:/var/lib/ceph/osd]
  osd1:
    <<: *osd
    container_name: lab-stor2-osd1
    hostname: node1
    volumes: [etc:/etc/ceph, ./:/lab:ro, osd1:/var/lib/ceph/osd]
  osd2:
    <<: *osd
    container_name: lab-stor2-osd2
    hostname: node2
    volumes: [etc:/etc/ceph, ./:/lab:ro, osd2:/var/lib/ceph/osd]
  osd3:
    <<: *osd
    container_name: lab-stor2-osd3
    hostname: node3
    volumes: [etc:/etc/ceph, ./:/lab:ro, osd3:/var/lib/ceph/osd]
```

ثم الملف mon.sh الذي ينشئ ال Cluster عند أول تشغيل، أي الملف ceph.conf والمفاتيح وخريطة ال Monitors، ثم يشغل ال Monitor:

```bash
#!/bin/bash
# Bootstrap one monitor (mon.a) and keyrings into the shared /etc/ceph volume, then run the monitor.
set -e
MON_IP=10.250.42.10
if [ ! -f /etc/ceph/ceph.conf ]; then
  FSID=$(uuidgen)
  cat > /etc/ceph/ceph.conf <<CONF
[global]
fsid = $FSID
mon_initial_members = a
mon_host = $MON_IP
public_network = 10.250.42.0/24
auth_cluster_required = cephx
auth_service_required = cephx
auth_client_required = cephx
osd_pool_default_size = 3
osd_pool_default_min_size = 2
osd_crush_chooseleaf_type = 1
mon_allow_pool_delete = true
mon_osd_down_out_interval = 60
mon_warn_on_pool_no_redundancy = true
[osd]
osd_objectstore = bluestore
bluestore_block_create = true
bluestore_block_size = 4294967296
osd_memory_target = 1073741824
CONF
  ceph-authtool --create-keyring /tmp/mon.keyring --gen-key -n mon. --cap mon 'allow *'
  ceph-authtool --create-keyring /etc/ceph/ceph.client.admin.keyring --gen-key -n client.admin \
    --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow *' --cap mgr 'allow *'
  ceph-authtool --create-keyring /etc/ceph/bootstrap-osd.keyring --gen-key -n client.bootstrap-osd \
    --cap mon 'profile bootstrap-osd' --cap mgr 'allow r'
  ceph-authtool /tmp/mon.keyring --import-keyring /etc/ceph/ceph.client.admin.keyring
  ceph-authtool /tmp/mon.keyring --import-keyring /etc/ceph/bootstrap-osd.keyring
  monmaptool --create --addv a "[v2:$MON_IP:3300,v1:$MON_IP:6789]" --fsid "$FSID" /tmp/monmap
  mkdir -p /var/lib/ceph/mon/ceph-a
  ceph-mon --mkfs -i a --monmap /tmp/monmap --keyring /tmp/mon.keyring
fi
exec ceph-mon -f -i a --public-addr $MON_IP
```

في الإعداد أعلاه لاحظ التالي:

- `osd_pool_default_size = 3` و`osd_pool_default_min_size = 2` هما القيم الافتراضية نفسها، كتبناها حتى تراها.
- `osd_crush_chooseleaf_type = 1` يجعل مجال الفشل هو السيرفر Host، أي لا نسختين على سيرفر واحد.
- `mon_osd_down_out_interval = 60` للتجربة فقط، والقيمة الافتراضية 600 ثانية، فلا تنقلها إلى الإنتاج.
- `bluestore_block_create` و`bluestore_block_size` تجعل BlueStore ينشئ ملفاً بحجم 4 GiB بدل أن يطلب قرصاً.
- `osd_memory_target` بقيمة 1 GiB حتى تتسع التجربة في جهاز واحد، والتوثيق يقول إن أقل من 2 GB غير منصوح به في الإنتاج.

ثم الملف osd.sh الذي يسجل OSD جديداً عند ال Monitor ويجهز ملفه ثم يشغله، وهو الملف نفسه لكل ال OSDs الأربعة:

```bash
#!/bin/bash
# Create one OSD backed by a 4 GiB sparse file inside this container's volume, then run it.
set -e
D=/var/lib/ceph/osd/data
mkdir -p $D
if [ ! -f $D/whoami ]; then
  until ceph -s -n client.bootstrap-osd -k /etc/ceph/bootstrap-osd.keyring >/dev/null 2>&1; do sleep 2; done
  UUID=$(uuidgen)
  SECRET=$(ceph-authtool --gen-print-key)
  ID=$(echo "{\"cephx_secret\": \"$SECRET\"}" | \
    ceph osd new "$UUID" -i - -n client.bootstrap-osd -k /etc/ceph/bootstrap-osd.keyring)
  ceph-authtool --create-keyring $D/keyring --name osd.$ID --add-key "$SECRET"
  ceph-osd -i "$ID" --mkfs --osd-uuid "$UUID" --osd-data $D
  echo "$ID" > $D/whoami
fi
ID=$(cat $D/whoami)
exec ceph-osd -f -i "$ID" --osd-data $D --keyring $D/keyring
```

والملف mgr.sh يسجل مفتاح ال Manager ثم يشغله:

```bash
#!/bin/bash
# Create the mgr.x key once, then run the manager in the foreground.
set -e
mkdir -p /var/lib/ceph/mgr/ceph-x
if [ ! -f /var/lib/ceph/mgr/ceph-x/keyring ]; then
  until ceph -s >/dev/null 2>&1; do sleep 2; done
  ceph auth get-or-create mgr.x mon 'allow profile mgr' osd 'allow *' mds 'allow *' \
    -o /var/lib/ceph/mgr/ceph-x/keyring
fi
exec ceph-mgr -f -i x
```

### تشغيل ال Cluster

```bash
docker compose up -d
docker exec lab-stor2-mon ceph config set mon auth_allow_insecure_global_id_reclaim false
docker exec lab-stor2-mon ceph -s
docker exec lab-stor2-mon ceph osd tree
```

الأمر الثاني يزيل تحذير `mon is allowing insecure global_id reclaim` الذي يظهر في كل Cluster جديد، وبعد دقيقة تقريباً سوف يكون المخرج كما يلي:

```text
  cluster:
    id:     6a0526dc-5238-4afc-9238-20b975515e28
    health: HEALTH_OK

  services:
    mon: 1 daemons, quorum a (age 94s) [leader: a]
    mgr: x(active, since 77s)
    osd: 4 osds: 1 up (since 0.829685s), 4 in (since 54s)

ID  CLASS  WEIGHT   TYPE NAME       STATUS  REWEIGHT  PRI-AFF
-1         0.01556  root default
-3         0.00389      host node0
 0    hdd  0.00389          osd.0       up   1.00000  1.00000
-7         0.00389      host node1
 1    hdd  0.00389          osd.1       up   1.00000  1.00000
-5         0.00389      host node2
 2    hdd  0.00389          osd.2       up   1.00000  1.00000
-9         0.00389      host node3
 3    hdd  0.00389          osd.3       up   1.00000  1.00000
```

لاحظ أن كل OSD ظهر تحت سيرفر باسمه، node0 إلى node3، وهذا ما يجعل CRUSH تضع النسخ الثلاث على ثلاثة منها. ولاحظ أيضاً أن `ceph -s` قال 1 up بينما الشجرة تقول إن الأربعة up، والسبب أنها لقطة من ثانية كانت فيها ال OSDs تبدأ واحداً بعد الآخر.

### Pool بالنسخ الكاملة وآخر بالترميز التصحيحي

```bash
docker exec lab-stor2-mon bash -c '
ceph osd pool create rep 32 32 replicated
ceph osd erasure-code-profile set ec21 k=2 m=1 crush-failure-domain=host
ceph osd erasure-code-profile get ec21
ceph osd pool create ec 32 32 erasure ec21
ceph osd pool application enable rep rados
ceph osd pool application enable ec rados
ceph osd pool ls detail'
```

والمخرج سوف يكون كما يلي، بعد حذف سطور الإنشاء واختصار السطور الطويلة بثلاث نقاط:

```text
crush-device-class=
crush-failure-domain=host
crush-num-failure-domains=0
crush-osds-per-failure-domain=0
crush-root=default
k=2
m=1
plugin=isa
technique=reed_sol_van
pool 1 '.mgr' replicated size 3 min_size 2 crush_rule 0 object_hash rjenkins pg_num 1 pgp_num 1 autoscale_mode on ...
pool 2 'rep' replicated size 3 min_size 2 crush_rule 0 object_hash rjenkins pg_num 32 pgp_num 32 autoscale_mode on ...
pool 3 'ec' erasure profile ec21 size 3 min_size 2 crush_rule 1 object_hash rjenkins pg_num 32 pgp_num 32 autoscale_mode on ... stripe_width 8192 ...
```

في المخرج أعلاه لاحظ التالي:

- ال Pool المسمى `.mgr` أنشأه ال Manager لنفسه، وله ثلاث نسخ أيضاً.
- ال Pool المسمى `ec` حجمه 3 أي k+m، و`min_size 2` تساوي k، وهي أقل من k+1 التي يوصي بها التوثيق، وهذا ما ذكرناه في قسم ال Pools.
- الإضافة Plugin المستخدمة `isa` بطريقة Reed-Solomon، وهي الافتراضية في هذا الإصدار كما يظهر في المخرج.

### نكتب 200 MiB في كل Pool ونرى أين ذهبت

```bash
docker exec lab-stor2-mon bash -c '
dd if=/dev/urandom of=/tmp/4m.bin bs=1M count=4 status=none
for i in $(seq 1 50); do rados -p rep put obj-$i /tmp/4m.bin; done
for i in $(seq 1 50); do rados -p ec put obj-$i /tmp/4m.bin; done
ceph df
ceph osd map rep obj-7
ceph osd map ec obj-7'
```

والمخرج سوف يكون كما يلي:

```text
--- RAW STORAGE ---
CLASS    SIZE   AVAIL      USED  RAW USED  %RAW USED
hdd    16 GiB  15 GiB  1007 MiB  1007 MiB       6.15
TOTAL  16 GiB  15 GiB  1007 MiB  1007 MiB       6.15

--- POOLS ---
POOL  ID  PGS   STORED  OBJECTS     USED  %USED  MAX AVAIL
.mgr   1    1  449 KiB        2  1.3 MiB      0    4.7 GiB
rep    2   32  200 MiB       50  600 MiB   3.99    4.7 GiB
ec     3   32  200 MiB       50  300 MiB   2.04    9.4 GiB
osdmap e25 pool 'rep' (2) object 'obj-7' -> pg 2.1c1ed88d (2.d) -> up ([1,3,0], p1) acting ([1,3,0], p1)
osdmap e25 pool 'ec' (3) object 'obj-7' -> pg 3.1c1ed88d (3.d) -> up ([2,0,1], p2) acting ([2,0,1], p2)
```

في المخرج أعلاه لاحظ التالي:

- ال 200 MiB نفسها أخذت 600 MiB في `rep` و300 MiB في `ec`، أي 3 أضعاف و1.5 ضعف كما في المعادلة.
- `MAX AVAIL` في `ec` ضعف الرقم في `rep` تقريباً، لأن Ceph يحسب المساحة المتاحة بعد الحماية وليس المساحة الخام.
- الكائن `obj-7` له الهاش نفسه 1c1ed88d في ال Pools الاثنين، فوقع في المجموعة رقم d في كل منهما، لكن CRUSH وضعته على OSDs مختلفة لأن لكل Pool قاعدة ورقماً مختلفاً، وهذه هي الأرقام التي في الصورة الأولى.
- الأمر `ceph osd map` لا يبحث عن الكائن، وإنما يحسب مكانه، ولذلك يعطيك النتيجة نفسها حتى لاسم كائن لم يكتب بعد.

### نوقف OSD ونراقب الاستعادة

الآن سوف نوقف `osd.2` بإشارة SIGKILL كأن السيرفر انقطعت عنه الكهرباء، ثم نطبع حالة المجموعات كل عشر ثوان بالملف `watch-pgs.sh` التالي:

```bash
#!/bin/bash
# Print health, OSD counts and PG states every 10 seconds (run on the host; uses python3 inside the mon container).
for i in $(seq 1 30); do
  echo "=== t+$((i*10))s $(date -u +%T)"
  docker exec lab-stor2-mon bash -c 'ceph -s --format json | python3 -c "
import json, sys
j = json.load(sys.stdin); o = j[\"osdmap\"]; p = j[\"pgmap\"]
print(\"health\", j[\"health\"][\"status\"], \"up\", o[\"num_up_osds\"], \"in\", o[\"num_in_osds\"],
      [(s[\"state_name\"], s[\"count\"]) for s in p[\"pgs_by_state\"]],
      \"degraded\", p.get(\"degraded_objects\", 0), \"misplaced\", p.get(\"misplaced_objects\", 0))"'
  sleep 10
done
```

```bash
date -u +%T; docker kill lab-stor2-osd2; ./watch-pgs.sh
```

والمخرج سوف يكون كما يلي:

```text
17:11:11
lab-stor2-osd2
=== t+10s 17:11:15
health HEALTH_WARN up 3 in 4 [('peering', 36), ('active+clean', 18), ('active+undersized+degraded', 10), ('active+undersized', 1)] degraded 17 misplaced 0
=== t+20s 17:11:29
health HEALTH_WARN up 3 in 4 [('active+undersized+degraded', 36), ('active+clean', 18), ('active+undersized', 11)] degraded 71 misplaced 0
=== t+30s 17:11:43
health HEALTH_WARN up 3 in 4 [('active+undersized+degraded', 36), ('active+clean', 18), ('active+undersized', 11)] degraded 71 misplaced 0
=== t+40s 17:11:57
health HEALTH_WARN up 3 in 4 [('active+undersized+degraded', 36), ('active+clean', 18), ('active+undersized', 11)] degraded 71 misplaced 0
=== t+50s 17:12:14
health HEALTH_WARN up 3 in 3 [('active+clean', 26), ('active+recovery_wait+degraded', 21), ('active+undersized+degraded', 11), ('active+undersized', 6), ('active+recovering+degraded', 1)] degraded 67 misplaced 0
=== t+60s 17:12:29
health HEALTH_WARN up 3 in 3 [('active+clean', 65)] degraded 0 misplaced 0
```

في المخرج أعلاه لاحظ التالي:

- خلال أربع ثوان أصبح ال OSD خارج `up`، ودخلت 36 مجموعة في `peering`، أي أن ال OSDs الباقية تتفق على من يحمل آخر نسخة، ثم أصبحت `active+undersized+degraded`، وكلمة active بقيت طوال الوقت، أي أن التطبيقات لم تلاحظ شيئاً.
- 18 مجموعة بقيت `active+clean` لأن osd.2 لم يكن من أعضائها أصلاً، وهذا هو أثر التوزيع بالمجموعات: العطل يمس جزءاً من البيانات وليس كلها.
- بين 17:11:29 و17:11:57 لم يتغير شيء، وهذه هي مرحلة الانتظار قبل `out`، ثم عند الدقيقة أصبح `in 3`، فبدأت الاستعادة `recovering`، وبعد 19 ثانية عادت المجموعات ال 65 كلها `active+clean`.
- بقيت الحالة `HEALTH_WARN` بسبب تحذير البطء الخاص بالملفات الذي ذكرناه، وأما من ناحية البيانات فال Cluster سليم، وقرأنا الكائن بعدها وطابق ال MD5 الأصل.

### نكسر ال Cluster: سيرفران ثم سيرفر واحد

بعد أن أصبح osd.2 خارج التوزيع، بقيت ثلاثة سيرفرات لثلاث نسخ، فماذا يحدث إذا سقط سيرفر آخر؟ نوقف `osd.3` وننتظر 100 ثانية حتى يمر وقت ال out، ثم نجرب الكتابة:

```bash
docker kill lab-stor2-osd3; sleep 100
docker exec lab-stor2-mon bash -c 'ceph -s; ceph health detail | grep -vi bluestore | head -5; time rados -p rep put new-1 /tmp/4m.bin && echo WRITE_OK'
```

والمخرج سوف يكون كما يلي (مختصراً):

```text
    health: HEALTH_WARN
            Degraded data redundancy: 93/306 objects degraded (30.392%), 44 pgs degraded, 57 pgs undersized
    osd: 4 osds: 2 up (since 115s), 2 in (since 38s); 8 remapped pgs
    pgs:     93/306 objects degraded (30.392%)
             9/306 objects misplaced (2.941%)
             44 active+undersized+degraded
             13 active+undersized
             8  active+clean+remapped
[WRN] PG_DEGRADED: Degraded data redundancy: 93/306 objects degraded (30.392%), 44 pgs degraded, 57 pgs undersized
    pg 1.0 is stuck undersized for 102s, current state active+undersized+degraded, last acting [1,0]
    pg 2.0 is stuck undersized for 102s, current state active+undersized+degraded, last acting [1,0]

real	0m0.191s
WRITE_OK
```

لاحظ كلمة `stuck`، فال OSD خرج من التوزيع وانتهى الانتظار، لكن لا يوجد سيرفر ثالث سليم تضع عليه CRUSH النسخة الثالثة، فلن تنتهي هذه الحالة مهما انتظرت، ومع ذلك الكتابة نجحت في أقل من ثانية لأن نسختين تساوي min\_size. وهذه هي حالة Cluster من ثلاثة سيرفرات فقط بعد سقوط واحد منها، ولهذا قلنا إن ثلاثة سيرفرات تعمل ولكنها لا تصلح نفسها.

ثم نوقف `osd.1` فيبقى سيرفر واحد، ونجرب الكتابة والقراءة مع مهلة 20 ثانية:

```bash
docker kill lab-stor2-osd1; sleep 30
docker exec lab-stor2-mon bash -c 'ceph -s | grep -E "osd:|health"
timeout 20 rados -p rep put new-2 /tmp/4m.bin; echo "exit=$?"
timeout 20 rados -p rep get obj-7 /tmp/y; echo "exit=$?"'
```

```text
    health: HEALTH_WARN
    osd: 4 osds: 2 up (since 2m), 2 in (since 83s); 8 remapped pgs
exit=124
exit=124
```

الكتابة والقراءة انتظرتا حتى انتهت المهلة (الرمز 124 هو رمز الأمر timeout)، وهذا متوقع لأن النسخة الواحدة أقل من min\_size. لكن لاحظ الشيء الغريب: بعد 30 ثانية ما زال `ceph -s` يقول `2 up`، أي أنه لا يعرف أن osd.1 توقف. والسبب في القاعدة التي شرحناها: ال Monitor يحتاج بلاغين من OSDs أخرى ليعلن OSD متوقفاً، ولم يبق إلا OSD واحد يبلغ. فماذا يحدث بعدها؟ في سجل ال Monitor وجدنا السطر التالي:

```text
mon.a@0(leader).osd e34 no beacon from osd.1 since 2026-10-12T17:12:25.050532+0000, 903.475160 seconds ago.  marking down
```

أي أن ال Monitor لم يعلنه down إلا بعد 900 ثانية من آخر رسالة Beacon منه، وهي قيمة `mon_osd_report_timeout`، وكل هذا الوقت كانت لوحة المراقبة تقول إن السيرفر يعمل بينما التطبيقات متوقفة. وبعد ساعات كانت الحالة كما يلي:

```text
    health: HEALTH_WARN
            1 osds down
            1 host (1 osds) down
            Reduced data availability: 59 pgs inactive, 26 pgs down
    osd: 4 osds: 1 up (since 2h), 2 in (since 2h); 6 remapped pgs
    pgs:     90.769% pgs not active
             26 undersized+degraded+peered
             26 down
             7  undersized+peered
             5  active+undersized+degraded+remapped
```

ولاحظ هنا أيضاً أن osd.1 بقي `in` رغم أنه down منذ ساعتين، والسبب الإعداد `mon_osd_min_in_ratio` بقيمة 0.75، فال Monitor لا يخرج OSDs أكثر إذا كانت نسبة ال in أقل من 75%، حتى لا يبدأ نقل بيانات ضخم وال Cluster في أسوأ حالاته. والدرس من هذا الجزء أن مراقبة Ceph لا تكفي فيها حالة ال OSDs، وإنما يجب أن تنبهك أي مجموعة ليست active، وأن تختبر الكتابة نفسها من الخارج.

### نعيد السيرفرات ونتحقق من البيانات

```bash
date -u +%T; docker start lab-stor2-osd1 lab-stor2-osd2 lab-stor2-osd3
```

ثم راقبنا الحالة كل عشر ثوان كما في الخطوة السابقة، وطبعناها في سطر واحد، فكان أول سطر سليم كما يلي، ثم قرأنا الكائن obj-7:

```text
20:03:17
20:03:43 HEALTH_WARN up 4 in 4 active+clean:65 degraded 0
```

```bash
docker exec lab-stor2-mon bash -c 'rados -p rep get obj-7 /tmp/z; md5sum /tmp/z'
```

```text
c5b2cc67f0bd377e011daf22513a9f16  /tmp/z
```

خلال 26 ثانية من التشغيل عادت المجموعات ال 65 كلها `active+clean`، والسبب أن ال OSDs عادت بأقراصها كما هي، فلم يحتج Ceph إلا لنسخ ما كتب أثناء غيابها، والكائن obj-7 طابق ال MD5 الأصلي `c5b2cc67…` الذي حسبناه عند الكتابة. فلم تضع البيانات في أي مرحلة، وإنما توقفت الخدمة عندما نزلنا تحت min\_size، وهذا هو السلوك الذي تريده من نظام تخزين: أن يتوقف بدل أن يقبل كتابة لا يستطيع حمايتها.

### قرص RBD كالذي تستخدمه الآلة الافتراضية

```bash
docker exec lab-stor2-mon bash -c '
ceph osd pool create vms 32; rbd pool init vms
rbd create vms/disk1 --size 2G
rbd info vms/disk1
rbd bench --io-type write --io-size 4M --io-total 200M vms/disk1 | tail -1
rbd du vms/disk1
rbd snap create vms/disk1@before; rbd snap ls vms/disk1'
```

```text
rbd image 'disk1':
	size 2 GiB in 512 objects
	order 22 (4 MiB objects)
	snapshot_count: 0
	id: 1215299df6c5
	block_name_prefix: rbd_data.1215299df6c5
	format: 2
	features: layering, exclusive-lock, object-map, fast-diff, deep-flatten
elapsed: 6   ops: 50   ops/sec: 7.28812   bytes/sec: 29 MiB/s
NAME   PROVISIONED  USED
disk1        2 GiB  200 MiB
SNAPID  NAME    SIZE   PROTECTED  TIMESTAMP
     3  before  2 GiB             Mon Oct 12 20:04:18 2026
```

في المخرج أعلاه لاحظ التالي:

- القرص بحجم 2 GiB مقسم إلى 512 كائناً بحجم 4 MiB، وكل كائن منها يمر بالخطوات التي في الصورة الأولى، فقرص الآلة الافتراضية موزع على كل ال OSDs.
- القرص يأخذ مساحة بقدر ما كتب فيه فقط، 200 MiB من 2 GiB، وهذا ما يسمى Thin Provisioning.
- سرعة 29 MiB/s رقم للتوضيح فقط من ملفات على جهاز واحد، ولا تقارنه بأقراص حقيقية، وفي الإنتاج لا تقيس إلا بأقراصك وشبكتك.

ولم نربط القرص بـ `rbd map`، لأن ذلك يحتاج وحدة `rbd` في نواة الجهاز المضيف، ونحن لا نحمل وحدات نواة على جهاز التجربة، وأما الآلات الافتراضية فتستطيع QEMU أن تصل إلى القرص عبر librbd دون وحدة النواة كما في [توثيق QEMU مع RBD](https://docs.ceph.com/en/tentacle/rbd/qemu-rbd/?ref=arabroot.io).

### التنظيف

```bash
docker compose down -v
docker rmi quay.io/ceph/ceph:v20.2.4
```

الأمر الأول يحذف ال Containers الستة والشبكة `lab-stor2-net` وال Volumes الخمسة بما فيها ملفات ال OSDs، فلا يبقى شيء من التجربة.

## أخطاء شائعة

- بناء Cluster بثلاثة سيرفرات وsize 3 ثم توقع أن يصلح نفسه بعد سقوط سيرفر: سوف يبقى undersized حتى يعود السيرفر، فإذا أردت الإصلاح التلقائي فخطط لسيرفر رابع ومساحة فارغة تكفي بياناته.
- تغيير min\_size إلى 1 حتى تعود الكتابة أثناء عطل: أنت بذلك تقبل كتابة على نسخة واحدة، فإذا تعطل قرصها قبل الاستعادة ضاعت تلك الكتابات.
- Pool بالترميز التصحيحي m=1 لبيانات حقيقية، أو بعدد سيرفرات يساوي k+m بالضبط فلا يبقى مكان للاستعادة.
- تشغيل Ceph على شبكة 1 Gb/s مشتركة مع حركة المستخدمين، ثم الاستغراب من بطء الكتابة وطول الاستعادة.
- أقراص استهلاكية رخيصة في الإنتاج، والتوثيق يشدد على أقراص من فئة Enterprise، ويطلب SSD على الأقل لسيرفرات ال Monitors.
- مراقبة عدد ال OSDs التي up فقط، وقد رأيت في التجربة أن ال Monitor قد يتأخر 900 ثانية في إعلان OSD متوقف، فراقب المجموعات غير active، واختبر الكتابة نفسها.
- ملء ال Cluster إلى حده دون حساب مساحة سيرفر يسقط، فعندما تنقل الاستعادة بياناته إلى الباقي قد تتجاوز الأقراص نسبة الامتلاء Full Ratio، وعندها يوقف Ceph الكتابة حتى لا تضيع البيانات كما يحذر توثيق العتاد.

## الخلاصة

- Ceph لا يحفظ جدولاً لأماكن الكائنات، وإنما يحسبها كل كلاينت بالهاش ثم CRUSH من خريطة يأخذها من ال Monitors، ولذلك لا يوجد سيرفر في منتصف الطريق.
- مجموعات التوزيع Placement Groups هي وحدة النسخ والاستعادة، والضبط التلقائي يحدد عددها.
- النسخ الكاملة size 3 تأخذ ثلاثة أضعاف المساحة، والترميز التصحيحي يأخذ (k+m)/k، وفي التجربة 200 MiB أخذت 600 و300 MiB، وm=1 لا ينصح به للإنتاج.
- عند سقوط OSD تبقى البيانات متاحة ما دامت النسخ لا تقل عن min\_size، والاستعادة تبدأ بعد mon\_osd\_down\_out\_interval، وفي التجربة عاد ال Cluster سليماً بعد 78 ثانية منها 60 انتظار.
- ثلاثة سيرفرات تكفي لتعمل، وأربعة هي الأقل ليصلح Ceph نفسه، ومع سيرفر واحد توقفت القراءة والكتابة ولم يعلن ال Monitor العطل إلا بعد 900 ثانية.
- إذا كان لديك سيرفر أو اثنان أو شبكة بطيئة فابدأ بـ ZFS والنسخ المتماثل، أو بأداة S3 خفيفة، واترك Ceph لحين تحتاجه فعلاً.

[![](https://arabroot.io/content/images/2026/10/storage-types-cover-cover.webp)دليلأنواع التخزين وواجهة S3: أين تضع بيانات تطبيقك؟أين تضع قاعدة البيانات وملفات المستخدمين والنسخ الاحتياطية، ولماذا يناسب كلاً منها نوع مختلف من التخزين.](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-block-%D9%88-file-%D9%88-object-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)[![](https://arabroot.io/content/images/2026/10/ceph-vs-glusterfs-cover-cover.webp)دليلCeph أم GlusterFS؟ أي تخزين موزع تختار اليوممقارنة عملية بين Ceph وGlusterFS في البنية والتعافي من الأعطال، ولماذا تحتاج سبباً قوياً لتبدأ اليوم على GlusterFS.](https://arabroot.io/articles/ceph-%D8%A7%D9%85-glusterfs-%D8%A7%D9%8A-%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D9%85%D9%88%D8%B2%D8%B9-%D8%AA%D8%AE%D8%AA%D8%A7%D8%B1-%D8%A7%D9%84%D9%8A%D9%88%D9%85/)[![](https://arabroot.io/content/images/2026/10/zfs-basics-cover-cover.webp)دليلZFS للمبتدئين: كيف يحمي بياناتك من التلف الصامتكيف يكتشف ZFS التلف الصامت ويصلحه، وكيف ترتب الأقراص في Pool يتحمل العطل وتستعيد ملفاتك من Snapshots.](https://arabroot.io/articles/zfs-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%83%D9%8A%D9%81-%D9%8A%D8%AD%D9%85%D9%8A-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA%D9%83-%D9%85%D9%86-%D8%A7%D9%84%D8%AA%D9%84%D9%81-%D8%A7%D9%84%D8%B5%D8%A7%D9%85%D8%AA/)[![](https://arabroot.io/content/images/2026/10/s3-servers-compare-cover-cover.webp)دليلأي تخزين S3 مستضاف ذاتياً يناسبك؟ وما الذي تغير في MinIOمقارنة عملية بين أشهر أدوات تخزين S3 المستضافة ذاتياً بقياسات على جهاز واحد، لتعرف أيها يناسب سيرفرك وماذا حدث فعلاً ل MinIO.](https://arabroot.io/articles/%D8%A7%D9%8A-%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-s3-%D9%85%D8%B3%D8%AA%D8%B6%D8%A7%D9%81-%D8%B0%D8%A7%D8%AA%D9%8A%D8%A7-%D9%8A%D9%86%D8%A7%D8%B3%D8%A8%D9%83/)[![](https://arabroot.io/content/images/2026/10/ha-concepts-cover-cover.webp)دليلمفاهيم التوفر العالي High Availability للمبتدئينماذا يعني أن تبقى خدمتك تعمل رغم تعطل جزء منها، وكيف تحسب التوفر وتجد نقاط الفشل الواحدة وتقرر متى يستحق ذلك تكلفته.](https://arabroot.io/articles/%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D8%A7%D9%84%D8%AA%D9%88%D9%81%D8%B1-%D8%A7%D9%84%D8%B9%D8%A7%D9%84%D9%8A-high-availability-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)

## سجل التحديثات

- أكتوبر 2026: كتابة المقال، والتجربة على Ceph 20.2.4 Tentacle من Docker Image الرسمية `quay.io/ceph/ceph:v20.2.4` بتثبيت يدوي وأربعة OSDs على ملفات متفرقة، فوق Docker Engine 29.8.0 (Docker Desktop مع WSL2)، والمعلومات من توثيق Ceph لإصدار Tentacle وصفحة الإصدارات كما قرأناها في 12 أكتوبر 2026.