قواعد Rediacc
يعمل كل مستودع Rediacc داخل بيئة معزولة تحتوي على Docker daemon خاص به، ووحدة تخزين LUKS مشفرة، ونطاق IP مخصص. تضمن هذه القواعد أن تطبيقك يعمل بشكل صحيح ضمن هذه البنية.
Rediaccfile
- كل مستودع يحتاج إلى Rediaccfile, سكريبت bash يحتوي على دوال دورة الحياة.
- دوال دورة الحياة:
up()،down(). اختياري:info(). up()يبدأ خدماتك.down()يوقفها.info()يوفر معلومات الحالة (حالة الحاويات، السجلات الأخيرة، الصحة).- يتم تحميل Rediaccfile بواسطة renet, لديه وصول إلى متغيرات الشل، وليس فقط متغيرات البيئة.
متغيرات البيئة المتاحة في Rediaccfile
| المتغير | مثال | الوصف |
|---|---|---|
REDIACC_WORKING_DIR | /mnt/rediacc/mounts/abc123/ | المسار الجذري للمستودع المُركّب |
REDIACC_NETWORK_ID | 6336 | معرّف عزل الشبكة |
REDIACC_REPOSITORY | abc123-... | GUID المستودع |
{SVCNAME}_IP | HEARTBEAT_IP=127.0.24.195 | عنوان IP loopback لكل خدمة (اسم الخدمة بأحرف كبيرة) |
Rediaccfile الأدنى
#!/bin/bash
_compose() {
renet compose -- "$@"
}
up() {
_compose up -d
}
down() {
_compose down
}
Compose
- استخدم
renet compose، ولا تستخدمdocker composeأبداً, يقوم renet بحقن عزل الشبكة، وشبكة المضيف، وعناوين IP loopback، وتسميات الخدمة. - لا تقم بتعيين
network_modeفي ملف compose, يفرض renetnetwork_mode: hostعلى جميع الخدمات. أي قيمة تعيّنها سيتم الكتابة فوقها. - لا تقم بتعيين تسميات
rediacc.*, يقوم renet بالحقن التلقائي لـrediacc.network_idوrediacc.service_ipوrediacc.service_name. - يتم تجاهل تعيينات
ports:في وضع شبكة المضيف. أضف تسميةrediacc.service_portلتوجيه HTTP (الخدمات بدون هذه التسمية لا تحصل على مسارات HTTP). استخدم تسمياتrediacc.tcp_ports/rediacc.udp_portsلإعادة توجيه TCP/UDP. - سياسات إعادة التشغيل (
restart: always،on-failure، إلخ) آمنة للاستخدام, يقوم renet بإزالتها تلقائياً لتوافق CRIU. يقوم watchdog الموجّه باستعادة الحاويات الموقوفة تلقائياً بناءً على السياسة الأصلية المحفوظة في.rediacc.json. - الإعدادات الخطرة محظورة بشكل افتراضي, يتم رفض
privileged: trueوpid: hostوipc: hostوالـ bind mounts إلى مسارات النظام. استخدمrenet compose --unsafeللتجاوز على مسؤوليتك الخاصة.
متغيرات البيئة داخل الحاويات
يقوم Renet بحقن هذه تلقائياً في كل حاوية:
| المتغير | الوصف |
|---|---|
SERVICE_IP | عنوان IP loopback المخصص لهذه الحاوية |
REDIACC_NETWORK_ID | معرّف عزل الشبكة |
تسمية الخدمات والتوجيه
- يصبح اسم الخدمة في compose بادئة URL للمسار التلقائي.
- Grand repos:
https://{service}.{repo}.{machine}.{baseDomain}(مثال:https://myapp.marketing.server-1.example.com). - Fork repos:
https://{service}-fork-{tag}.{repo}.{machine}.{baseDomain}(مثال:https://myapp-fork-staging.marketing.server-1.example.com). يمنع الفاصل-fork-تعارضات URL مع أسماء خدمات grand repo. يستخدم URL الـ fork دائماً شهادة wildcard الموجودة للمستودع الأصلي، لذا لا حاجة لشهادة جديدة. - للنطاقات المخصصة، استخدم تسميات Traefik (ملاحظة: النطاقات المخصصة ليست متوافقة مع fork, النطاق ينتمي إلى grand repo).
الشبكات
- كل مستودع يحصل على Docker daemon خاص به في
/var/run/rediacc/docker-<networkId>.sock. - كل خدمة تحصل على عنوان IP loopback فريد ضمن شبكة فرعية /26 (مثال:
127.0.24.192/26). - الربط تلقائي: يمكن للخدمات الربط بـ
0.0.0.0أوlocalhost، حيث تقوم النواة بإعادة كتابة العنوان بشكل شفاف إلى عنوان IP loopback المخصص للخدمة. الربط الصريح بـ${SERVICE_IP}لا يزال يعمل ولكنه لم يعد مطلوباً. - يمكن لفحوصات الصحة استخدام
localhostأو${SERVICE_IP}. مثال:healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] - الاتصالات بين المستودعات محظورة على مستوى النواة: تحظر النواة تلقائياً الاتصالات بعناوين IP loopback خارج الشبكة الفرعية
/26للمستودع. لا يمكن لخدمة في مستودع واحد الوصول إلى خدمات في مستودع آخر. - الاتصال بين الخدمات: استخدم أسماء الخدمات (مثل
db،redis)، يقوم renet بحقن كل اسم خدمة تلقائياً كاسم مضيف يتم تحويله إلى عنوان IP الصحيح. لا تعمل أسماء DNS الخاصة بـ Docker في وضع المضيف، ولكن تعمل أسماء الخدمات عبر/etc/hosts. تجنب تضمين${DB_IP}أو ما شابه في ملفات التكوين الدائمة (مثل سلاسل الاتصال المخزّنة في قاعدة بيانات)، عند الـ fork، يتم نقل عنوان IP الخام ليشير إلى مستودع خاطئ. تتحقق أسماء الخدمات دائماً بشكل صحيح لكل مستودع. - تعارض المنافذ مستحيل بين المستودعات, كل منها لديه Docker daemon ونطاق IP خاص به.
- إعادة توجيه منافذ TCP/UDP: أضف تسميات لكشف المنافذ غير HTTP:
labels: - "rediacc.tcp_ports=5432,3306" - "rediacc.udp_ports=53"
التخزين
- يتم تخزين جميع بيانات Docker داخل المستودع المشفر, يقع
data-rootالخاص بـ Docker في{mount}/.rediacc/docker/dataداخل وحدة تخزين LUKS. وحدات التخزين المسماة والصور وطبقات الحاويات جميعها مشفرة ومنسوخة احتياطياً ومُفرّعة تلقائياً. - يُوصى بالـ bind mounts إلى
${REDIACC_WORKING_DIR}/...للوضوح، لكن وحدات التخزين المسماة تعمل أيضاً بأمان.volumes: - ${REDIACC_WORKING_DIR}/data:/data # bind mount (موصى به) - pgdata:/var/lib/postgresql/data # named volume (آمن أيضاً) - يتم تركيب وحدة تخزين LUKS في
/mnt/rediacc/mounts/<guid>/. - تلتقط لقطات BTRFS ملف دعم LUKS بالكامل، بما في ذلك جميع البيانات المُركّبة بـ bind.
- مخزن البيانات هو ملف مجموعة BTRFS ذو حجم ثابت على قرص النظام. استخدم
rdc machine query --name <name> --systemلرؤية المساحة الحرة الفعلية. قم بالتوسعة معrdc datastore resize.
CRIU (الترحيل الحي)
- الاشتراك عبر التسمية: أضف
rediacc.checkpoint=trueللحاويات التي تريد إنشاء نقاط تحقق لها. الحاويات بدون هذه التسمية (قواعد البيانات، الذاكرة المؤقتة) تبدأ من جديد وتتعافى عبر آلياتها الخاصة (WAL، LDF، AOF). repo down --checkpointيحفظ حالة العمليات قبل التوقف، ويستعيد تلقائياً عندrepo upالتالي. هذا هو التدفق الرئيسي على نفس الجهاز، وتم التحقق من أنه يعمل.backup push --checkpointيلتقط حالة ذاكرة العمليات الجارية وحالة القرص للحاويات المُعلَّمة، ثم ينقل وحدة التخزين إلى جهاز آخر. الاستعادة على الجهاز الهدف عبرrepo up.repo fork --checkpointيلتقط حالة العمليات قبل التفريع ويقوم بنسخ checkpoint عبر CoW مع الـ fork. ⚠️ على نفس الجهاز، يفشلrepo upاللاحق على الـ fork حالياً بـcriu failed: type RESTORE errno 0بينما لا يزال الأصل قيد التشغيل. أخطاء CRIU الأولية checkpoint-restore/criu#478 / #514. استخدمrepo down --checkpointللحفظ والاستعادة في نفس المكان، أوbackup push --checkpointللترحيل بين الأجهزة.repo upيكتشف بيانات checkpoint تلقائياً ويستعيدها إن وُجدت. استخدم--skip-checkpointلإجبار البدء من جديد.- استعادة مدركة للتبعيات: يستخدم
depends_onفي compose لبدء قواعد البيانات أولاً (انتظار healthy)، ثم استعادة CRIU لحاويات التطبيق. - تصبح اتصالات TCP قديمة بعد الاستعادة، ويجب على التطبيقات التعامل مع
ECONNRESETوإعادة الاتصال. لا يحافظ CRIU على حالة اتصال TCP النشط أثناء الاستعادة في أي تدفق مدعوم. - وضع Docker التجريبي يتم تفعيله تلقائياً على أدوات daemon لكل مستودع.
- يتم تثبيت CRIU أثناء
rdc config machine setup. /etc/criu/runc.confيتم تكوينه معtcp-establishedافتراضياً.- يتم حقن إعدادات أمان الحاويات تلقائياً للحاويات المُعلَّمة، يضيف
renet composeما يلي للحاويات التي تحملrediacc.checkpoint=true:cap_add:CHECKPOINT_RESTORE،SYS_PTRACE،NET_ADMIN(الحد الأدنى لـ CRIU على النواة 5.9+)security_opt:apparmor=unconfined(دعم AppArmor في CRIU ليس مستقراً بعد في المنبع)userns_mode: host(يتطلب CRIU الوصول إلى مساحة اسم init لـ/proc/pid/map_files)
- الحاويات بدون التسمية تعمل بوضع أمان أنظف (بدون capabilities إضافية).
- يتم الحفاظ على ملف تعريف seccomp الافتراضي لـ Docker، ويستخدم CRIU
PTRACE_O_SUSPEND_SECCOMP(النواة 4.3+) لتعليق المرشحات مؤقتاً أثناء checkpoint/restore. - لا تقم بتعيين capabilities CRIU يدوياً في ملف compose، يتولى renet ذلك بناءً على التسمية.
- راجع قالب heartbeat للحصول على تطبيق مرجعي متوافق مع CRIU.
أنماط التطبيقات المتوافقة مع CRIU
- تعامل مع
ECONNRESETعلى جميع الاتصالات المستمرة (مجمّعات قواعد البيانات، websockets، طوابير الرسائل). - استخدم مكتبات مجمّعات الاتصال التي تدعم إعادة الاتصال التلقائي.
- أضف
process.on("uncaughtException")كشبكة أمان لأخطاء المقابس القديمة من كائنات المكتبات الداخلية. - يتم إدارة سياسات إعادة التشغيل تلقائياً بواسطة renet (تُزال لـ CRIU، watchdog يتولى الاستعادة).
- تجنب الاعتماد على DNS الخاص بـ Docker, استخدم عناوين IP loopback للاتصال بين الخدمات.
سياسات أمان المضيف حسب نظام التشغيل
عبر أنظمة التشغيل الخمسة المدعومة رسمياً للخوادم (راجع المتطلبات)، يستخدم Docker daemon الخاص بكل مستودع والحاويات التي يشغّلها تسميات الحاويات الافتراضية. لا يقوم rdc config machine setup بتثبيت سياسة SELinux مخصصة أو ملف تعريف AppArmor مخصص.
- Ubuntu 24.04 / openSUSE Leap 16.0: AppArmor مُفعَّل افتراضياً. تعمل الحاويات تحت ملف تعريف docker-container الافتراضي. الاستثناء الوحيد هو CRIU (يُضاف
apparmor=unconfinedللحاويات التي تحملrediacc.checkpoint=true، كما هو موضح أعلاه). - Fedora 43 / Oracle Linux 10: يعمل SELinux بوضع التطبيق (enforcing) افتراضياً. تحصل الحاويات على سياق
container_tالقياسي. لا حاجة لتثبيت سياسة إضافية. إذا فشلت خطوة إعداد بسبب رفض AVC، راجع استكشاف الأخطاء: رفض SELinux. - Debian 13: AppArmor متاح لكن غير مُطبَّق افتراضياً على جميع النطاقات. لا تزال الحاويات تستخدم ملف تعريف docker-container.
لا يلزم تحديد إعداد وضع أمان خاص بنظام التشغيل؛ فكلٌّ من rdc و renet يكتشفان ما يعمل ويوفران نفس عزل المستودع على جميع التوزيعات الخمس.
الأمان
- تشفير LUKS إلزامي للمستودعات القياسية. كل مستودع لديه مفتاح تشفير خاص به.
- يتم تخزين بيانات الاعتماد في تكوين CLI (
~/.config/rediacc/rediacc.json). فقدان التكوين يعني فقدان الوصول إلى وحدات التخزين المشفرة. - لا تقم أبداً بإرسال بيانات الاعتماد إلى نظام التحكم بالإصدارات. استخدم
env_fileوقم بتوليد الأسرار فيup(). - عزل المستودع: Docker daemon والشبكة والتخزين لكل مستودع معزولة تماماً عن المستودعات الأخرى على نفس الجهاز.
- عزل الوكلاء: تعمل وكلاء الذكاء الاصطناعي في وضع fork-only بشكل افتراضي. كل مستودع لديه مفتاح SSH خاص به مع تطبيق sandbox من جانب الخادم (ForceCommand
sandbox-gateway). جميع الاتصالات محاطة بـ sandbox مع Landlock LSM، وOverlayFS home overlay، وTMPDIR لكل مستودع. يتم حظر الوصول إلى نظام الملفات بين المستودعات من قبل النواة. sudoمعطّل داخل sandbox المستودع عن قصد في التصميم. يتطلب عزل نظام الملفات Landlock تفعيلNoNewPrivs، الذي يمنع أي رفع للصلاحيات، لذا سيفشلsudoبرسالةno new privileges flag is set. يمتلك المستخدم المالك للمستودع بالفعل الصلاحيات اللازمة لكل ما يحدث داخل مسار تحميل المستودع ومقبس Docker الخاص به. للعمليات التي تحتاج فعلاً إلى صلاحيات مرفوعة (تثبيت حزم المضيف، ضبط النواة)، شغّلها خارج الـ sandbox أو من دالةup()في Rediaccfile التي ينفّذها مسار البنية التحتية.- شبكات Docker bridge معطّلة في كل daemon خاص بالمستودع. يحمل ملف
daemon.jsonلكل مستودع القيم"bridge": "none"و"iptables": false، لذا فإن أمرdocker run <image>البسيط ينشئ حاوية مزودة فقط بواجهة loopback وبدون أي اتصال خارجي. هذا ليس خللاً، بل هو الطريقة التي يُفرض بها العزل بين المستودعات: تُطبَّق خطاطيف eBPF على مستوى النواة التي تمنع مستودعاً من الوصول إلى عناوين IP الـ loopback لمستودع آخر فقط على الحاويات التي تعيش في مساحة اسم الشبكة الخاصة بالمضيف. لخدمات الإنتاج استخدمrenet compose، الذي يحقنnetwork_mode: hostتلقائياً. للحاويات العابرة لمرة واحدة داخل الشل، مرّر--network hostبشكل صريح.
النشر
rdc repo upيقوم تلقائياً بتحميل وحدة تخزين LUKS إذا لم تكن محمّلة، ثم ينفذup()في جميع ملفات Rediaccfile.rdc repo downينفذdown()ويوقف Docker daemon.rdc repo down --unmountيغلق أيضاً وحدة تخزين LUKS (يقفل التخزين المشفر).- الفروع (
rdc repo fork) تنشئ نسخة CoW (copy-on-write) بـ GUID و networkId جديدين، في زمن ثابت بصرف النظر عن حجم المستودع. يعمل BTRFS reflink على تكرار بيانات الصورة الوصفية فقط وليس البيانات نفسها، لذا فإن مستودعاً بحجم 100 جيجابايت يتم تفريعه في نفس الثواني القليلة التي يتم فيها تفريع مستودع بحجم 1 جيجابايت. يشارك الفرع مفتاح تشفير الأصل. - التسلّم (
rdc repo takeover --name <fork> -m <machine>) يستبدل بيانات المستودع grand ببيانات فرع. يحتفظ grand بهويته (GUID، networkId، النطاقات، الإقلاع التلقائي، سلسلة النسخ الاحتياطية). يتم حفظ بيانات الإنتاج القديمة كفرع احتياطي. الاستخدام: اختبار الترقية على فرع، التحقق، ثم تسلّم الإنتاج. الرجوع بـrdc repo takeover --name <backup-fork> -m <machine>. - مسارات الوكيل تستغرق حوالي 3 ثوانٍ لتصبح نشطة بعد النشر. تحذير “Proxy is not running” أثناء
repo upهو إعلامي في بيئات ops/dev. - يطبع
rdc repo upوrdc repo fork --upنمط URL للخدمات المُعلَّمة بـrediacc.service_portفي نهاية النشر. استبدل{service}باسم الخدمة المكشوفة الخاصة بك للحصول على URL الدقيق. الخدمات بدونrediacc.service_port(قواعد البيانات، العمّال) لا تحصل على مسارات ولا يتم عرضها.
الأخطاء الشائعة
- استخدام
docker composeبدلاً منrenet compose, لن تحصل الحاويات على عزل الشبكة. - سياسات إعادة التشغيل آمنة, يقوم renet بإزالتها تلقائياً ويتولى watchdog الاستعادة.
- استخدام
privileged: true, غير ضروري، إذ يقوم renet بحقن صلاحيات CRIU المحددة عوضاً عن ذلك. - ترميز عناوين IP الخام في ملفات التكوين الدائمة - استخدم أسماء الخدمات للاتصالات للحفاظ على عزل الـ fork سليماً.
- استخدام
rdc term connect -cكحل بديل للأوامر الفاشلة, أبلغ عن الأخطاء بدلاً من ذلك. repo deleteينفذ تنظيفاً كاملاً يشمل عناوين IP loopback ووحدات systemd. شغّلrdc machine prune --name <name>لتنظيف بقايا عمليات الحذف القديمة.