لماذا لا تترك MinIO حيث هو
أُرشف مستودع minio/minio من قِبل مالكه في 25 أبريل 2026: أصبح للقراءة فقط، ويذكر ملف README أن المشروع لم يعد مُصانًا وأن الملفات التنفيذية المنشورة لن تتلقى تحديثات. عمليًا، لن يصلح أحد في المصدر الثغرة التالية التي تُكتشف في خادم S3 لديك. أما العميل mc، وهو أيضًا تحت رخصة AGPLv3، فقد أُرشف في 14 يوليو 2026.
يواصل MinIO لديك العمل، وهذا ما يجعل الأمر خادعًا: لا ينكسر شيء يوم الأرشفة. لكن مخزن الكائنات معرَّض للشبكة، ويحتوي وسائطك أو نسخك الاحتياطية أو صادراتك، وهو يعمل الآن دون تصحيحات أمنية. هدفان قابلان للاستضافة الذاتية يتكرران في النقاشات: Garage المنشور برخصة AGPLv3، وRustFS المنشور برخصة Apache 2.0.
لا يعيد هذا المقال تثبيت أيٍّ منهما، فلكل منهما دليله: Garage على خادم VPS وRustFS على VPS. هنا ننطلق من MinIO قيد الإنتاج ونجيب عن سؤال واحد: كيف تنقل البيانات، وتثبت أنه لم يُفقد شيء، وتحوّل التطبيق دون روابط مكسورة.
ما لا ينقله نسخ الحاوية وحده
- الكائنات الحالية — هذا هو الجزء السهل: ينسخها
rcloneأوmc mirror، مع فحص يُجرى بعد ذلك. - الإصدارات القديمة — توضح وثائق
mc mirrorأنه لا يزامن إلا الكائن الحالي، دون أي معلومة عن الإصدارات. - مستخدمو IAM وسياساته — تعيش في MinIO لا في الحاويات. الأسرار لا تُصدَّر: تُنشئ مفاتيح جديدة في الهدف.
- الروابط الموقّعة مسبقًا التي وُزّعت — يعتمد التوقيع على المضيف والمنطقة والمفاتيح. تغيير نقطة الوصول يبطلها.
- فروق التوافق مع S3 — قوائم ACL وسياسات الحاويات والوصول المجهول وتعدد الإصدارات: لكل هدف ثغراته، تُقرأ في وثائقه بدل افتراضها.
- إعدادات العملاء — نقطة الوصول والمنطقة ونمط العنونة path-style: كل ما يثبّته تطبيقك ونسخك
resticوشبكة CDN لديك.
المتطلبات وخطة السعة
تحتاج إلى صلاحية مسؤول على MinIO، وجهازًا أو خادم VPS عليه mc وrclone، وهدفًا مثبّتًا مسبقًا ويمكن بلوغه عبر HTTPS (راجع الدليلين أعلاه للوكيل العكسي والشهادة). قاعدة السعة بسيطة: ما لم تتحقق من الترحيل تُبقي المصدر سليمًا، فتدفع ثمن القرص مرتين.
توصيتنا التحريرية: خصّص في الهدف الحجم الكلي لبيانات المصدر، مضافًا إليه هامش يقارب 30% للبيانات الوصفية وفروق التوزيع. على الخادم نفسه يعني ذلك توفر ضعف الحجم الحالي على الأقل مع الهامش؛ وعلى خادمين يتحمل كل منهما نصيبه. عقدة Garage واحدة مع replication_factor = 1 تحتفظ بنسخة واحدة فقط من كل كائن: هذه ليست نسخة احتياطية، والمصدر القائم هو شبكة الأمان الوحيدة أثناء الترحيل.
الخطوة 1: جرد المصدر قبل النسخ
عرّف MinIO باسم مستعار
أنشئ اسمًا مستعارًا
srcببيانات اعتماد تقرأ كل الحاويات:mc alias set src https://s3.your-domain.com ACCESS_KEY SECRET_KEYعُدّ الكائنات والحجم
لكل حاوية دوّن عدد الكائنات والحجم:
mc du --recursive src/mediasmc ls --recursive src/medias | wc -l
ضع هذه الأرقام في جدول «قبل / بعد»: هو دليلك على اكتمال الترحيل.عُدّ الإصدارات
إن كان تعدد الإصدارات مفعّلًا فقائمة الإصدارات أطول من قائمة الكائنات:
mc ls --recursive --versions src/medias | wc -l
توضح وثائقmc lsأن--versionsيتطلب خدمة تدعم تعدد الإصدارات. الفرق الكبير بين العددين يدل على حجم السجل المعرَّض للخطر.صدّر قائمة السياسات والمستخدمين
احتفظ بسجل لما ستعيد إنشاءه:
mc admin policy list srcmc admin user list src
الأسرار ليست في هذا الخرج ولا يمكن أن تكون: ستعيد توليدها.دوّن إعدادات العملاء
في كل تطبيق يتحدث مع MinIO دوّن نقطة الوصول والمنطقة ونمط العنونة واسم متغير البيئة الذي يحملها. التحويل النهائي لن يغيّر إلا هذه القيم.
Garage وRustFS: الفروق التي يجب معرفتها قبل الاختيار
مرّر الجدول أفقيًا
| الموضوع | Garage | RustFS |
|---|---|---|
| الرخصة | AGPLv3 (ملف README للمستودع) | Apache 2.0 (ملف README للمستودع) |
| تعدد الإصدارات | غير مدعوم: `PutBucketVersioning` و`ListObjectVersions` مدرجتان ضمن المفقود | مذكور على أنه متاح في README |
| سياسات الحاويات وقوائم ACL | لا هذه ولا تلك: نظام خاص حسب المفتاح والحاوية | IAM وسياسات الحاويات؛ قوائم ACL غير مدعومة عن قصد |
| الوصول المجهول | مرفوض باستجابة 403 | يُقيَّم وفق سياسة الحاوية |
| منطقة العميل | `s3_region` من `garage.toml`، تُنسخ إلى العميل | `RUSTFS_REGION`، والقيمة الافتراضية `us-east-1`، تُنسخ إلى العميل |
| العنونة | path-style وvhost-style مدعومان | path-style افتراضيًا ودون إعداد DNS |
| الروابط الموقّعة مسبقًا | مدعومة | مدعومة؛ يجب أن يطابق التوقيعَ المضيفُ والمخطط والمنفذ والمنطقة والمسار |
| واجهة إدارة MinIO | لا ينطبق: تُدار بالأمر `garage` | سطح منفصل: يعمل S3 دون أن يكون `mc admin` مضمونًا |
تعدد الإصدارات: ما يضيع في النسخ وما العمل
هذه النقطة هي التي تحسم اختيار الهدف. لا يدعم Garage تعدد الإصدارات، وتقول صفحة توافقه مع S3 ذلك بوضوح. إن كانت حاوياتك تحتفظ بسجل يقرؤه التطبيق أو يجب حفظه لأسباب مهنية فليس Garage وجهتك. يذكر RustFS تعدد الإصدارات على أنه متاح، لكن النسخ بـ rclone sync أو mc mirror لن يعيد إنشاء سجلك هناك.
ثلاث طرق، تُختار حاوية بحاوية. قبول الفقد: لا تنتقل إلا النسخة الحالية، وهذا يناسب الحالات التي كان فيها تعدد الإصدارات مجرد شبكة أمان ضد الخطأ البشري. النسخ إصدارًا إصدارًا: تسرد المعرّفات بالأمر mc ls --versions ثم تعيد تشغيل الإصدارات من الأقدم إلى الأحدث نحو هدف يدعم تعدد الإصدارات بعد تفعيله على الحاوية؛ وستكون تواريخ الإصدارات تواريخ النسخ لا الأصلية. أو أرشفة السجل على حدة في حاوية أرشيف وترحيل الحالي فقط. في كل الحالات جرّب كائنًا ذا عدة إصدارات قبل إطلاق النسخ العام.
الخطوة 2: تجهيز الهدف، Garage أو RustFS
Garage: عيّن التخطيط ثم طبّقه
لا تصلح أي حاوية قبل تطبيق التخطيط (layout). اقرأ معرّف العقدة ثم أسند إليها منطقة وسعة:
garage statusgarage layout assign -z dc1 -c 1G <node_id>garage layout apply --version 1
اضبط-cعلى مساحة القرص المخصصة فعلًا.Garage: أنشئ المفتاح والحاوية والصلاحيات
تُمنح الصلاحيات حسب المفتاح والحاوية:
garage key create app-keygarage bucket create mediasgarage bucket allow --read --write --owner medias --key app-key
يظهر السر عند إنشاء المفتاح: انسخه فورًا إلى مدير الأسرار لديك.Garage: انسخ المنطقة إلى العميل
يحدد القسم
[s3_api]فيgarage.tomlالقيمةs3_region. ضع هذه القيمة نفسها في إعدادات عملائك وأبقِ على العنونة path-style. اختلاف المنطقة يعني توقيعات مرفوضة.RustFS: الاسم المستعار والحاوية
مع ضبط
mcعلى الهدف:mc alias set dst http://127.0.0.1:9000 ACCESS_KEY SECRET_KEYmc mb dst/medias
توصي وثائق RustFS بمستخدمي IAM أو حسابات الخدمة بدل المفاتيح الجذرية للتطبيقات.RustFS: أعد إنشاء المستخدمين والسياسات
خذ القائمة المصدَّرة في الخطوة 1 وأعد إنشاء كل مستخدم وكل سياسة في RustFS، بمفتاح جديد لكل تطبيق. ثم وحّد المنطقة: القيمة الافتراضية لـ
RUSTFS_REGIONهيus-east-1ويجب أن يستخدم العميل القيمة نفسها.
الخطوة 3: النسخ بـ rclone وإثبات النتيجة
عرّف الوجهتين
استخدم المزوّد
Minioللمصدر وOtherللهدف:rclone config create src s3 provider=Minio access_key_id=ACCESS_KEY secret_access_key=SECRET_KEY endpoint=https://s3.your-domain.comrclone config create dst s3 provider=Other access_key_id=ACCESS_KEY secret_access_key=SECRET_KEY endpoint=http://127.0.0.1:3900 region=garageregion=garageيفترض أنs3_region = "garage"، وهي قيمة مثال Garage: ضع قيمتك إن اختلفت. مع RustFS استبدل نقطة الوصول بنقطة الهدف والمنطقة بقيمةRUSTFS_REGION.أجرِ تجربة جافة
يعدّل
rclone syncالوجهة لتعكس المصدر، بما في ذلك الحذف. لا تعكسه أبدًا، وابدأ بالتجربة الجافة:rclone sync src:medias dst:medias --checksum --metadata --transfers 16 --dry-runشغّل النسخ الفعلي والخدمة قائمة
يبقى MinIO في الخدمة خلال هذه الجولة الأولى:
rclone sync src:medias dst:medias --checksum --metadata --transfers 16 --progress
يطلب--metadataمن rclone الحفاظ على البيانات الوصفية: تحقق على كائن تجريبي من وصول نوع المحتوى سليمًا.تحقق من النسخ
تأكد أن كل ما في المصدر موجود في الهدف:
rclone check src:medias dst:medias --one-way --combined /tmp/check-medias.txt
يسرد الملف كل مسار برمز:=مطابق،+غائب عن الوجهة،*مختلف،!خطأ. الهدف صفر سطر من+أو*أو!.أعد العدّ بعد النسخ
أعد تشغيل
mc du --recursiveوعدّ الكائنات على الهدف بالاسم المستعارdst، واملأ عمود «بعد» في جدولك. يجب أن يتطابق الرقمان إلا في الفروق التي تفسرها الإصدارات.
ETag متعدد الأجزاء وmc mirror: قراءة الفروق دون خطأ
ETag لكائن صغير أُرسل في طلب واحد هو MD5 الخاص به، ويستخدمه rclone مجموعًا اختباريًا. أما في كائن متعدد الأجزاء فتنبّه وثائق rclone إلى أن ETag لم يعد MD5 البيانات: إنه MD5 لتسلسل قيم MD5 لكل جزء، يليه عدد الأجزاء. فهو يتوقف إذن على حجم الأجزاء الذي اختاره العميل عند الرفع.
استقبل MinIO لديك الكائن بتقطيع معيّن، ويرسله rclone إلى الهدف بتقطيع آخر: المحتوى متطابق بايتًا ببايت، أما ETag فلا. وأمر rclone check الذي يبلغ عن هذه الكائنات على أنها مختلفة ليس مصيبًا بالضرورة. أعد تشغيله عليها بالخيار --download الذي ينزّل الطرفين ويقارن البيانات نفسها: أبطأ لكنه بلا لبس.
إن فضّلت البقاء ضمن منظومة mc فالنسخ يُكتب mc mirror --preserve src/medias dst/medias. يحتفظ --preserve بخصائص المصدر وقواعد سياسة الحاوية؛ والأخيرة لا معنى لها إلا إن كان الهدف يقرؤها، وهذا يستبعد Garage. بدون --overwrite لا يُنسخ من جديد كائن موجود في الهدف: يُسجَّل الخطأ ويستمر النسخ. ومع --remove تُحذف كائنات الهدف الغائبة عن المصدر. وmc mirror لا يزامن إلا الكائن الحالي بلا إصدارات. وتقارن مقارنته الاسم والحجم والتاريخ والمجموع الاختباري دون قراءة المحتوى، وقد تختلف المجاميع المركبة لمجرد تغيّر توزيع الأجزاء، فتظهر فروق وهمية خصوصًا في الكائنات الكبيرة التي رُفعت بأحجام أجزاء مختلفة. وتذكّر أن مستودع mc مؤرشف أيضًا: الأداة تعمل لكنها لن تتطور، فلا تبنِ عليها أتمتة طويلة الأمد.
الروابط الموقّعة مسبقًا: لماذا تتعطل عند التحويل
يحتوي الرابط الموقّع مسبقًا على توقيع محسوب من المفتاح السري والمضيف والمنطقة والمسار. تقول وثائق RustFS ذلك: يجب أن يطابق المضيف والمخطط والمنفذ والمنطقة ومسار الطلب ما استُخدم في التوقيع. فتغيير نقطة الوصول أو المفتاح يبطل كل رابط صدر، بلا استثناء، ولا شيء يعيد توجيهه.
النتيجة مسألة ترتيب. الروابط التي يولّدها تطبيقك لحظيًا تتجدد من تلقاء نفسها فور ربطه بالهدف الجديد. أما التي أرسلتها بالبريد أو خزّنتها في قاعدة البيانات فستموت عند التحويل. لذا قصّر مدة صلاحية الروابط قبل ذلك بأيام قليلة كي يقل عدد الروابط الطويلة المتداولة، ونبّه الفرق التي توزعها يدويًا.
الخطوة 4: التحويل على ثلاث مراحل دون قطع حاد
مزامنة أولى والخدمة قائمة
هي نسخ الخطوة 3 يجري ومازال MinIO يخدم الحركة. تستغرق ما تستغرقه ولا تزعج أحدًا. دوّن وقت انتهائها.
جمّد الكتابة
حوّل التطبيق إلى وضع القراءة فقط أو إلى صيانة قصيرة، أو فعّل الكتابة المزدوجة إن سمح بها شيفرتك. يمكن أن تستمر القراءة على MinIO أثناء المزامنة الأخيرة.
المزامنة الأخيرة بالخيار --update
انقل فقط ما تغيّر منذ ذلك الحين:
rclone sync src:medias dst:medias --update --metadata --transfers 16 --progressrclone check src:medias dst:medias --one-way
أعد عدّ الكائنات والحجم وقارنها بجدول الخطوة 1.غيّر نقطة الوصول عبر متغير بيئة
يجب أن يقرأ تطبيقك نقطة الوصول والمنطقة والمفاتيح من بيئته لا من الشيفرة. عدّل هذه المتغيرات وأعد تشغيل الخدمة ثم اختبر رفعًا وقراءة وحذفًا على كائن تجريبي. ولّد من جديد الروابط الموقّعة التي تهمك.
راقب ثم فكّك لاحقًا فقط
أبقِ MinIO في مكانه متوقفًا أو للقراءة فقط مدة كافية لمرور دورة كاملة من نسخك الاحتياطية ومهامك المجدولة. وجّه نسخك
resticإلى الهدف من جديد، راجع دليل النسخ الاحتياطية بـ restic.
الرجوع إلى الوراء يقتصر على متغير واحد
ما دام MinIO لم يُحذف وكانت المزامنة الأخيرة قد جرت في وضع القراءة فقط، فالرجوع يعني إعادة نقطة الوصول القديمة إلى البيئة وإعادة التشغيل. وإن جرت كتابات على الهدف منذ التحويل فأعدها أولًا إلى المصدر بأمر rclone copy في الاتجاه المعاكس (لا sync أبدًا لأنه سيحذف) ثم ارجع. لا تمحُ MinIO إلا بعد فترة مراقبة تحددها مسبقًا.
استكشاف الأخطاء: الأعراض التي تتكرر
يردّ Garage على نداء غير موقّع برمز 403 مع الرسالة Garage does not support anonymous access yet. هذا هو السلوك المتوقع من Garage، وطلب الوصول المجهول لا يزال في متتبع المشروع على أنه ميزة لم تُعطَ أولوية قط. إن كان تطبيقك يقدّم ملفات عامة بلا توقيع فسيلزمك استخدام روابط موقّعة مسبقًا أو وكيل أمام الحاوية.
إن رُفضت الكتابات بأخطاء توقيع بعد التحويل مباشرة فقارن منطقة العميل بمنطقة الهدف (s3_region في Garage وRUSTFS_REGION في RustFS) وتأكد أن العميل يستخدم العنونة path-style. وإن أبلغ rclone check عن كائنات متعددة الأجزاء على أنها مختلفة مع تساوي أحجامها فأعد تشغيله بالخيار --download. وإن لم تنسخ إعادةُ تشغيل mc mirror كائنًا معدَّلًا فالسبب غياب --overwrite. أخيرًا، إن كانت حاوية فارغة في الهدف رغم نجاح المزامنة فتحقق أن المفتاح مُنح --read --write على تلك الحاوية في Garage، أو السياسة المناسبة في RustFS.
الوثائق الرسمية
أعد قراءة هذه المصادر قبل التنفيذ فالمشاريع تتغير: مستودع github.com/minio/minio وملف README فيه لحالة MinIO ورخصته؛ ومستودع github.com/rustfs/rustfs وdocs.rustfs.com (صفحة بروتوكول S3) لـ RustFS؛ وgaragehq.deuxfleurs.fr لصفحة توافق Garage مع S3 ودليل البدء السريع؛ وrclone.org لأمري sync وcheck وصفحة واجهة S3؛ ووثائق MinIO عن mc mirror وmc ls للخيارات المذكورة. لتثبيت الهدف راجع دليلينا عن Garage وRustFS، ولنسخ هذا التخزين احتياطيًا راجع الاستراتيجية الموصوفة في مقال 3-2-1 مع restic وS3.