انتقل إلى المحتوى الرئيسي انتقل إلى الملاحة انتقل إلى التذييل

قواعد Rediacc

القواعد والاصطلاحات الأساسية لبناء التطبيقات على منصة Rediacc. يغطي Rediaccfile و compose والشبكات والتخزين و CRIU والنشر.

قواعد 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_ID6336معرّف عزل الشبكة
REDIACC_REPOSITORYabc123-...GUID المستودع
{SVCNAME}_IPHEARTBEAT_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, يفرض renet network_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> لتنظيف بقايا عمليات الحذف القديمة.