في مقال أنظمة إدارة التعلم LMS ذكرنا أن Open edX صمم أصلاً للمقررات المفتوحة MOOC التي يدخلها آلاف المتعلمين في وقت واحد، وأنه يحتاج من اليوم الأول ذاكرة أكبر من غيره، وفي دليل Moodle ثبتنا منصة تقوم على سيرفر ويب وقاعدة بيانات وContainer للمهام المجدولة، والسؤال الآن: كيف يبدو Open edX عندما تشغله بنفسك، وكم يحتاج فعلاً، وما الذي يفعله مصمم المقرر والمتعلم في أول يوم؟ وفي هذا المقال سوف نتناول:
- مكونات المنصة كما يشغلها Tutor، ولماذا هي أكثر من Moodle بكثير.
- التثبيت بأداة Tutor على Docker، مع ما حدث في أول تشغيل وكم استغرقت التهيئة Initialisation.
- بناء مقرر كملفات OLX واستيراده، وتسجيل متعلمين، ومتعلم يحل سؤالاً وتصل درجته إلى صفحة التقدم.
- العربية في الواجهة، واستهلاك الذاكرة وهو خامل، والنسخ الاحتياطي، والترقية، والتوسع Scaling.
ما الذي تثبته عندما تثبت Open edX؟
Open edX منصة مفتوحة المصدر مكتوبة بلغة Python وإطار Django، وكودها في مستودع openedx-platform بترخيص AGPL-3.0، وهي ليست تطبيقاً واحداً وإنما مجموعة خدمات تعمل معاً، وأهم ما يجب أن تفهمه قبل التثبيت أنها مقسومة إلى منصتين: منصة التعلم LMS التي يدخلها المتعلم، والاستوديو Studio (ويسمى في الكود CMS) الذي يبني فيه الفريق المقررات، ولكل منهما نطاق مستقل وعملية مستقلة ومهام خلفية Worker مستقلة. والصورة التالية تبين ما يشغله Tutor على سيرفر واحد:

في المخطط أعلاه لاحظ التالي:
- الواجهات الأمامية MFE: اختصار Micro-Frontends، وهي صفحات React مستقلة مثل صفحة الدخول وصفحة المقرر وصفحة التقدم، يخدمها Container خاص بها على نطاق ثالث، وأغلب ما يراه المتعلم اليوم يأتي منها وليس من قوالب Django القديمة.
- قاعدتا بيانات أساسيتان: MySQL تحمل الحسابات والتسجيل في المقررات والدرجات، وMongoDB تحمل محتوى المقررات نفسه، لذلك فالنسخة الاحتياطية الكاملة هي الاثنتان معاً كما سيأتي.
- Meilisearch: محرك البحث في المحتوى، وتوثيق Tutor عن التوسع يذكر أنه ليس مصدراً للحقيقة، أي أنك تستطيع حذف فهرسه وإعادة بنائه دون خسارة بيانات.
وقد يتساءل البعض: لماذا لا نثبت كل هذا بأنفسنا كما فعلنا مع Moodle؟ والإجابة أن ذلك ممكن نظرياً، ولكن عدد الخدمات وإعدادات كل منها والترحيل Migrations الذي يتغير مع كل إصدار يجعل التثبيت اليدوي مشروعاً بحد ذاته، لذلك صار Tutor هو الطريقة التي يعتمدها مجتمع Open edX، فهو أداة سطر أوامر بلغة Python تحفظ إعداداتك في ملف config.yml واحد، وتولد منه ملفات Docker Compose، وتشغل التهيئة والترقية بالترتيب الصحيح.
الإصدار Release والمتطلبات Requirements
يصدر Open edX إصدارين رئيسيين في السنة بأسماء أشجار مرتبة أبجدياً، وملاحظات إصدار Verawood تذكر أنه صدر في 30 يوليو 2026، وصفحة الإصدارات تقول إن الإصدار الأحدث هو الوحيد المدعوم، وإن الإصدار التالي Willow مخطط له في ديسمبر 2026. ولكل إصدار رئيسي إصدار رئيسي من Tutor، فـ صفحة التثبيت تربط Verawood بـ Tutor 22، والإصدار الذي جربناه هو Tutor 22.0.2.
ومتطلبات Tutor هي كما يلي:
| البند | الحد الأدنى | الموصى به |
|---|---|---|
| الذاكرة RAM | 4GB | 8GB |
| المعالج CPU | 2 | 4 |
| القرص | 8GB | 25GB |
| البرامج | Docker 24.0.5 أو أحدث، وDocker Compose 2، ونظام Linux بمعمارية AMD64 أو ARM64، والمنفذان 80 و443 مفتوحان | |
ولاحظ أن ال Docker Images وحدها تأخذ مساحة كبيرة، ففي تجربتنا كان Docker Image الأساسي overhangio/openedx:22.0.2-indigo بحجم 5.04GB، ومعه MySQL بحجم 1.12GB وMongoDB بحجم 1.19GB، والمجموع مع الباقي نحو 8.1GB قبل أن تضيف مقرراً واحداً، لذلك اعتبر 25GB هي البداية الواقعية وليست رفاهية.
التثبيت بأداة Tutor
تثبيت Tutor في بيئة Python مستقلة
توثيق Tutor ينصح بتثبيته في بيئة افتراضية Virtual Environment حتى لا يختلط بحزم Python الخاصة بالنظام، وسوف نثبت الإصدار بالرقم، ومعه إضافتان Plugins: tutor-mfe التي تشغل الواجهات الأمامية، وtutor-indigo وهي القالب Theme الافتراضي:
python3 -m venv ~/tutor-venv
. ~/tutor-venv/bin/activate
pip install "tutor==22.0.2" "tutor-mfe>=22,<23" "tutor-indigo>=22,<23"
tutor --version
tutor plugins listtutor, version 22.0.2
NAME STATUS VERSION
indigo ✅ enabled 22.0.0
mfe ✅ enabled 22.1.0والطريقة التي في التوثيق هي pip install "tutor[full]"، وهي تثبت معه 13 إضافة منها المنتديات Forum والملاحظات Notes وMinIO، وفي تجربتنا بقي pip يحاول حل اعتماديات هذه الحزمة أكثر من عشر دقائق دون نتيجة، فثبتنا الإضافتين اللتين نحتاجهما فقط. ولاحظ أن غياب إضافة tutor-forum يعني أن النقاشات داخل المقرر لا تعمل، فإذا كانت مؤسستك تعتمد عليها فأضفها بالقيد نفسه >=22,<23.
الإعداد Configuration قبل التشغيل
نضبط اسم المنصة واللغة الافتراضية والمنفذ، وكل قيمة تكتبها هنا تحفظ في config.yml:
tutor config save \
--set PLATFORM_NAME="Lab Academy" \
--set LANGUAGE_CODE=ar \
--set [email protected] \
--set CADDY_HTTP_PORT=127.0.0.1:27080
tutor config printvalue LMS_HOST
tutor config printvalue CMS_HOST
tutor config printvalue MFE_HOSTlocal.openedx.io
studio.local.openedx.io
apps.local.openedx.ioفي الإعداد أعلاه لاحظ التالي:
- النطاقات الافتراضية تنتهي بـ
local.openedx.io، وهو نطاق يشير إلى127.0.0.1، فيصلح للتجربة على جهازك، وفي الإنتاج تغيرها إلى نطاقك كما في قسم النطاق والشهادة. - الإعداد
CADDY_HTTP_PORT=127.0.0.1:27080من دليل تشغيل Open edX خلف Reverse Proxy، ويجعل Caddy يستمع على الجهاز نفسه فقط، وسوف نرى بعد قليل لماذا لا يكفي هذا وحده لتصفح المنصة على منفذ غير 80. - كلمات مرور MySQL وMongoDB والمفاتيح السرية يولدها Tutor عشوائياً في
config.yml، لذلك احم هذا الملف كما تحمي كلمات المرور، ولا تضعه في مستودع Git عام.
التشغيل والتهيئة Initialisation
الأمر الذي في التوثيق يقوم بكل شيء: يولد الملفات، ويسحب ال Images، ويشغل ال Containers، ثم يشغل التهيئة التي تنشئ قواعد البيانات وتطبق الترحيل:
tutor local launch --non-interactiveوفي أول تشغيل لنا توقف الأمر بالخطأ التالي:
ERROR 2003 (HY000): Can't connect to MySQL server on 'mysql:3306' (111)
[7/10] Waiting for MySQL service (this may take a while)...
...
MySQL initialisation error
Error: Command failed with status 1: docker compose ... run --rm -T mysql-job sh -e -c echo "Initialising MySQL..."وعند قراءة سجل MySQL تبين السبب، فالتشغيل الأول لقاعدة بيانات جديدة يمر بخادم مؤقت ثم إعادة تشغيل، وتهيئة InnoDB وحدها أخذت 40 ثانية على سيرفر مشغول، بينما مهمة التهيئة في Tutor تحاول الاتصال عشر مرات فقط ثم تستسلم:
[Note] [Entrypoint]: MySQL init process done. Ready for start up.
2026-10-21T09:32:02.259505Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
2026-10-21T09:32:41.829854Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
[System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.4.11' port: 3306والحل أن تترك MySQL حتى يكتمل، ثم تشغل التهيئة وحدها، فهي آمنة للتكرار لأن كل خطوة فيها تتحقق مما سبق:
tutor local do initوفي تجربتنا استغرقت التهيئة 6085 ثانية، أي نحو 101 دقيقة، وأنشأت 609 جدولاً في قاعدة openedx، و1378 سجلاً في جدول الترحيل django_migrations. وهذا الرقم ليس ما سوف تراه على سيرفر مخصص، فالسيرفر الذي جربنا عليه كان مشغولاً بأعمال أخرى، وصفحة البداية السريعة تتحدث عن أقل من عشر دقائق على سيرفر باتصال جيد، ولكنه يبين نقطة مهمة: الترحيل في Open edX عمل ثقيل على المعالج، لأن Django يحسب حالة مئات النماذج Models قبل كل خطوة، لذلك لا تجرب الترقية على سيرفر الإنتاج في ساعات العمل.
Killed فالسبب نقص الذاكرة كما يذكر دليل النشر المحلي، وأما رسالة MySQL initialisation error في أول تشغيل فغالباً سببها بطء MySQL في أول إقلاع، فانتظره ثم أعد tutor local do init بدلاً من حذف كل شيء والبدء من جديد.حساب المدير Admin
tutor local do createuser --staff --superuser --password='Lab-Admin-2026!' admin [email protected]والخيار --staff يعطيه صلاحية الدخول إلى الاستوديو، و--superuser يعطيه لوحة إدارة Django، وفي الإنتاج اترك الخيار --password واكتب كلمة المرور عندما يطلبها الأمر، حتى لا تبقى في سجل أوامر ال Shell.
نجرب العطل: المنصة على منفذ Port غير 80
بعد التهيئة طلبنا النطاقات الثلاثة على المنفذ 27080 من الجهاز نفسه:
for h in local.openedx.io studio.local.openedx.io apps.local.openedx.io; do
curl -s -o /dev/null -w "$h %{http_code} %{redirect_url}\n" \
--resolve $h:27080:127.0.0.1 http://$h:27080/
donelocal.openedx.io 301 http://apps.local.openedx.io/catalog/
studio.local.openedx.io 302 http://studio.local.openedx.io:27080/home/
apps.local.openedx.io 302 http://local.openedx.io/لاحظ أن منصة التعلم حولت الطلب إلى صفحة الفهرس في الواجهات الأمامية بعنوان دون المنفذ، فيذهب المتصفح إلى المنفذ 80 ولا يجد شيئاً، بينما حافظ الاستوديو على المنفذ، والسبب أن عناوين الواجهات الأمامية مبنية من MFE_HOST وLMS_HOST كما هي، والقاعدة هنا: Open edX مصمم ليعمل على 80 و443 مباشرة أو خلف Reverse Proxy يستقبل عليهما ويمرر إلى CADDY_HTTP_PORT كما يشرح دليل Tutor، ولا تحاول نشره للمستخدمين على منفذ آخر. وفي التجربة اكتفينا بتوجيه المتصفح إلى المنفذ الصحيح لأخذ الصور.
بناء المقرر Course: من الاستوديو Studio أو من ملفات OLX
يبنى المقرر في Open edX من أربعة مستويات، وتوثيق الوحدات يعرف الوحدة Unit بأنها الصفحة الواحدة التي يراها المتعلم، وفيها مكون أو أكثر Component مثل نص أو سؤال أو فيديو. والصورة التالية تبين المستويات على المقرر الذي سوف نبنيه:

الطريقة البسيطة هي أن يبني المعلم كل هذا بالنقر في الاستوديو، وهي مناسبة لمقرر واحد، ولكن إذا كان لديك عشرات المقررات أو تريد مراجعة التغييرات قبل نشرها فالأنسب أن تعرف أن المقرر في Open edX له صيغة ملفات اسمها OLX، وهي مجلد فيه ملفات XML، كل مستوى في مجلد باسمه، وهي نفسها الصيغة التي يخرجها الاستوديو عند تصدير المقرر. وفيما يلي المقرر الذي استخدمناه كاملاً:
net101/
course.xml
course/2026.xml
chapter/week1.xml
sequential/dns.xml
vertical/dns-intro.xml
vertical/dns-quiz.xml
html/dns-text.xml
html/dns-text.html
problem/dns-q1.xml
policies/2026/policy.json
policies/2026/grading_policy.jsonالملف الرئيسي course.xml يحدد المؤسسة ورمز المقرر والدورة Run، ومنها يتكون معرف المقرر course-v1:LabX+NET101+2026:
<course url_name="2026" org="LabX" course="NET101"/><!-- course/2026.xml -->
<course display_name="مقدمة في الشبكات" language="ar" start="2026-01-01T00:00:00Z">
<chapter url_name="week1"/>
</course>
<!-- chapter/week1.xml -->
<chapter display_name="الأسبوع الأول: أساسيات الشبكات">
<sequential url_name="dns"/>
</chapter>
<!-- sequential/dns.xml -->
<sequential display_name="نظام أسماء النطاقات DNS" graded="true" format="Homework">
<vertical url_name="dns-intro"/>
<vertical url_name="dns-quiz"/>
</sequential>
<!-- vertical/dns-intro.xml -->
<vertical display_name="تعريف DNS">
<html url_name="dns-text"/>
</vertical>
<!-- vertical/dns-quiz.xml -->
<vertical display_name="تمرين قصير">
<problem url_name="dns-q1"/>
</vertical>
<!-- html/dns-text.xml -->
<html filename="dns-text" display_name="شرح DNS"/>ونص الشرح في html/dns-text.html:
<p>نظام أسماء النطاقات DNS يحول اسم النطاق مثل example.com إلى عنوان IP يستطيع جهازك الاتصال به.</p>ثم السؤال في problem/dns-q1.xml، بمحاولتين ودرجة واحدة:
<problem display_name="سؤال عن DNS" max_attempts="2" weight="1">
<multiplechoiceresponse>
<label>ما وظيفة DNS؟</label>
<choicegroup type="MultipleChoice">
<choice correct="true">تحويل اسم النطاق إلى عنوان IP</choice>
<choice correct="false">توزيع عناوين IP على الأجهزة</choice>
<choice correct="false">تشفير الاتصال بين المتصفح والسيرفر</choice>
</choicegroup>
</multiplechoiceresponse>
</problem>وأخيراً سياسة التقييم في policies/2026/grading_policy.json، وهي هنا نوع واحد من التقييم هو الواجبات Homework بوزن 100%، ودرجة النجاح 50%، ثم policy.json بالاسم واللغة وتاريخ البداية:
{"GRADER": [{"type": "Homework", "min_count": 1, "drop_count": 0, "short_label": "HW", "weight": 1.0}], "GRADE_CUTOFFS": {"Pass": 0.5}}{"course/2026": {"display_name": "مقدمة في الشبكات", "language": "ar", "start": "2026-01-01T00:00:00Z"}}الآن نقوم بنسخ المجلد إلى Container الاستوديو واستيراده بأمر import، والوسيط الأول هو المجلد الأب والثاني اسم مجلد المقرر:
docker cp net101 edu1-openedx-cms-1:/tmp/net101
docker exec edu1-openedx-cms-1 bash -c 'cd /openedx/edx-platform && ./manage.py cms import /tmp net101'والمخرج طويل، وهذه أهم أسطره:
Importing...
Importing static content? True
Importing python lib? True
Seeding forum roles for course course-v1:LabX+NET101+2026
... Course import course-v1:LabX+NET101+2026: No tags.csv file present.
... Extracting dates from 8 items in course-v1:LabX+NET101+2026واستغرق الاستيراد 57 ثانية، ولاحظ أن اسم ال Container يبدأ باسم مشروع Docker Compose، وهو عندنا edu1-openedx لأننا غيرنا الإعداد LOCAL_PROJECT_NAME، والقيمة الافتراضية tutor_local، فاعرف الاسم عندك بالأمر tutor local status. والصورة التالية للمقرر بعد الاستيراد كما يراه المدير في الاستوديو:

وقد يتساءل البعض: هل يغني هذا عن الاستوديو؟ لا، فالمعلم سوف يعدل في الاستوديو، وإذا عدلت أنت الملفات بعد ذلك ثم استوردتها فوق المقرر فسوف تكتب فوق تعديلاته، لذلك اتفق مع الفريق على مصدر واحد للحقيقة: إما الاستوديو، وتصدر منه نسخة OLX للحفظ والمراجعة، وإما الملفات في Git، ولا يعدل أحد في الاستوديو.
المتعلمون والتسجيل Enrollment والدرجة Grade
أنشأنا متعلمين وسجلناهما في المقرر من سطر الأوامر، وفي الإنتاج سوف يسجل المتعلمون أنفسهم من صفحة المقرر أو يأتون من الدخول الموحد SSO، ولكن التسجيل من سطر الأوامر يفيدك عندما تستلم قائمة من القبول والتسجيل:
tutor local do createuser --password='Lab-Stud-2026!' student1 [email protected]
tutor local do createuser --password='Lab-Stud-2026!' student2 [email protected]
docker exec edu1-openedx-lms-1 bash -c 'cd /openedx/edx-platform && \
./manage.py lms enroll_user_in_course -e [email protected] -c course-v1:LabX+NET101+2026 && \
./manage.py lms enroll_user_in_course -e [email protected] -c course-v1:LabX+NET101+2026'ثم تحققنا من التسجيل من داخل Django:
[('student1', 'audit', True), ('student2', 'audit', True)]ولاحظ أن نوع التسجيل audit، وهو التسجيل المجاني الافتراضي، وفي Open edX أنواع تسجيل Enrollment Modes أخرى مثل verified المرتبط بالشهادات والدفع، وهي من الأشياء التي ورثتها المنصة من طبيعة المقررات المفتوحة، وفي مدرسة أو جامعة تكتفي غالباً بنوع واحد.
بعد ذلك دخلنا بحساب student1، وهذه صفحة المقرر كما رآها:

ثم أجاب عن السؤال إجابة صحيحة، فظهرت علامة الصح ودرجة 1 من 1، وبقيت له محاولة واحدة:

وصفحة التقدم Progress حسبت الدرجة مباشرة بحسب سياسة التقييم التي كتبناها: 100% في الواجبات Homework، ودرجة النجاح 50%، ورسالة «أنت ناجح حالياً في هذا المساق»، بينما إكمال المحتوى 50% لأن المتعلم فتح وحدة واحدة من اثنتين:

العربية واتجاه الكتابة RTL بصراحة
بعد ضبط LANGUAGE_CODE=ar انقلبت الواجهة كلها إلى اليمين، والصورة التالية لصفحة الفهرس قبل الدخول:

وأثناء التجربة وجدنا التفاصيل التالية، وهي التي سوف يسألك عنها الفريق:
- عبارات إنجليزية: زر
searchوعبارة «It works! Powered by the Open edX Platform» في الصفحة الرئيسية، وروابط التذييل Footer كلها، وسطر «You have used 1 of 2 attempts» تحت السؤال، وفقرة Hidden Scores في صفحة التقدم، وصفحة الإعدادات في Tutor تعترف بذلك وتقول إن بعض النصوص لن تكون مترجمة إذا لم يساهم ما يكفي من المترجمين في لغتك، وتشرح كيف تضيف ترجماتك. - المصطلح: الترجمة تستخدم «مساق» للمقرر Course في كل مكان، فإذا كانت مؤسستك تقول «مقرر» فهذا من أول ما تعدله في الترجمات المخصصة.
- التوقيت: الاستوديو يعرض التواريخ بتوقيت UTC، فاضبط تواريخ البدء والتسليم وأنت تعرف الفرق مع توقيت مؤسستك.
- الأرقام: درجة السؤال ظهرت «1,0/1,0» بفاصلة عشرية، والنسب في صفحة التقدم «%100» بالعلامة قبل الرقم.
والخلاصة أن العربية صالحة للمتعلم من اليوم الأول، ولكنها أقل اكتمالاً من Moodle الذي رأينا حزمته العربية مترجمة بنسبة 99%، فاجمع هذه العبارات في قائمة واحدة قبل الإطلاق.
كم يستهلك من الذاكرة RAM وهو خامل؟
بعد التهيئة والتجربة، وبلا أي متعلم متصل، قسنا الذاكرة بالأمر docker stats --no-stream:
| ال Container | الذاكرة |
|---|---|
| lms-worker | 1.445GiB |
| cms-worker | 1.449GiB |
| lms | 840MiB |
| cms | 738MiB |
| mysql | 687MiB |
| meilisearch | 132MiB |
| mongodb | 123MiB |
| mfe | 54MiB |
| caddy | 44MiB |
| redis وsmtp | أقل من 5MiB لكل منهما |
المجموع نحو 5.4GB والمنصة خاملة، وهذا ما يجعل 4GB حداً أدنى نظرياً فقط. ولاحظ أن أكبر رقمين هما العاملان Workers وليس منصة التعلم، والسبب أن كل عامل Celery يشغل افتراضياً عملية لكل معالج كما في توثيق Celery، والسيرفر الذي جربنا عليه فيه 8 معالجات، فوجدنا داخل كل عامل 9 عمليات: عملية رئيسية وثماني عمليات فرعية:
docker exec edu1-openedx-lms-worker-1 sh -c 'ps -eo args | grep -c "[c]elery"'9وكنا قد وضعنا حداً للذاكرة 1500MB لكل عامل عبر ملف docker-compose.prod.override.yml الذي يقرؤه Tutor من مجلد env/local إذا وجده، فوصل العاملان إلى الحد تقريباً. لذلك على سيرفر فيه معالجات كثيرة وذاكرة قليلة راقب العاملين أولاً، وفيما يلي ملف الحدود الذي استخدمناه، وهو مثال تعدل أرقامه بحسب سيرفرك:
# $(tutor config printroot)/env/local/docker-compose.override.yml
services:
lms:
mem_limit: 3g
cms:
mem_limit: 3g
mysql:
mem_limit: 1500m
mongodb:
mem_limit: 1g
meilisearch:
mem_limit: 512mالنطاق Domain والشهادة HTTPS
في الإنتاج تحتاج سجلات DNS للنطاقات كلها، وصفحة المتطلبات تقترح سجل A للنطاق الرئيسي وسجل CNAME لكل النطاقات الفرعية تحته:
learn 1800 IN A 203.0.113.10
*.learn 1800 IN CNAME learn.example.com.ثم تعيد ضبط النطاقات وتفعل HTTPS:
tutor config save \
--set LMS_HOST=learn.example.com \
--set CMS_HOST=studio.learn.example.com \
--set ENABLE_HTTPS=true \
--set CADDY_HTTP_PORT=80
tutor local launchوعند ENABLE_HTTPS=true يصدر Caddy داخل Tutor شهادات Let's Encrypt تلقائياً كما تذكر صفحة الإعدادات، بشرط أن يكون المنفذان 80 و443 مفتوحين وأن تشير السجلات إلى السيرفر، وهذه خطوة لا يمكن تجربتها على جهاز محلي. وإذا كان لديك Reverse Proxy على السيرفر مثل Caddy أو Nginx Proxy Manager فأبق ENABLE_HTTPS=true، واضبط ENABLE_WEB_PROXY=false وCADDY_HTTP_PORT=127.0.0.1:81، ووجه إليه النطاقات كلها مع الترويسات X-Forwarded-Proto وX-Forwarded-Port كما في دليل Tutor، وتذكر أن النطاقات أكثر من ثلاثة، فهناك أيضاً meilisearch.learn.example.com الذي تستخدمه الواجهات الأمامية للبحث.
التحقق Verification من النجاح
tutor local statusيظهر كل ال Containers بحالةUp، ولا يوجد Container يعيد التشغيل Restarting.- النطاق الرئيسي يحول إلى
/catalog/في الواجهات الأمامية، والمقرر يظهر في الفهرس. - الدخول إلى الاستوديو بحساب المدير يفتح صفحة المقررات، والمقرر المستورد فيه مستوياته كلها.
- متعلم مسجل يحل سؤالاً فتظهر درجته في صفحة التقدم دون انتظار.
النسخ الاحتياطي Backup والاستعادة Restore
كل بيانات Tutor في مجلد واحد هو $(tutor config printroot)، وفيه config.yml ومجلد env المولد ومجلد data لقواعد البيانات والملفات، ودليل النسخ الاحتياطي ونقل البيانات يعطي أمرين لأخذ نسخة من قاعدتي البيانات وهما تعملان:
tutor local exec \
-e USERNAME="$(tutor config printvalue MYSQL_ROOT_USERNAME)" \
-e PASSWORD="$(tutor config printvalue MYSQL_ROOT_PASSWORD)" \
mysql sh -c 'mysqldump --all-databases --user=$USERNAME --password=$PASSWORD > /var/lib/mysql/dump.sql'
tutor local exec mongodb mongodump --out=/data/db/dump.mongodb... done dumping `openedx.modulestore.definitions` (12 documents)
... done dumping `openedx.modulestore.structures` (6 documents)
... done dumping `openedx.modulestore.active_versions` (1 document)وفي منصتنا الصغيرة كان ملف dump.sql بحجم 6.4MB، ونسخة MongoDB بحجم 68KB لأن فيها مقرراً واحداً، والمجلد كله 634MB. والنسختان تقعان داخل data/mysql وdata/mongodb، فانقلهما بعد ذلك خارج السيرفر، ومعهما config.yml ومجلد data/openedx-media الذي يحمل الملفات المرفوعة، كما في النسخ الاحتياطي بقاعدة 3-2-1 واختبار الاستعادة. وأما النسخة الأبسط والأكثر اكتمالاً فهي التي يذكرها الدليل قبل الترقية: إيقاف المنصة ثم نسخ المجلد كله بأمر rsync، وعلى سيرفر آخر بالإصدار نفسه من Tutor تضعه في مكانه وتشغل tutor local start -d.
التحديث Upgrade إلى إصدار أحدث
التحديثات داخل الإصدار الرئيسي نفسه، مثل الانتقال من 22.0.1 إلى 22.0.2، هي تحديث حزمة Tutor ثم إعادة launch، وأما الانتقال من إصدار رئيسي إلى آخر، مثل Verawood إلى Willow، فهو كما يقول دليل الترقية لا رجوع عنه إلا من نسخة احتياطية، لأن الرجوع إلى إصدار أقدم غير مدعوم. والخطوات بالترتيب:
- اقرأ ملف
CHANGELOG.mdفي Tutor، وابحث عن التغييرات التي عليها العلامة 💥، فهي التي تغير السلوك الذي يعتمد عليه الآخرون Breaking Change، مثل ترقية Meilisearch في Tutor 22.0.0 التي تتطلب حذف الفهرس وإعادة بنائه. - أوقف المنصة وخذ نسخة كاملة من المجلد بأمر
rsync. - تأكد أن كل إضافة Plugin لها إصدار متوافق مع الإصدار الجديد.
- جرب الترقية على نسخة من المنصة أولاً، وتذكر أن التهيئة قد تأخذ وقتاً طويلاً كما رأينا.
- حدث الحزم، ثم شغل
tutor local launch، وإذا كنت تبني Docker Images خاصة بك فاستخدمtutor local upgrade --from=<الإصدار السابق>قبله كما في الدليل.
tutor local stop
sudo rsync -avr "$(tutor config printroot)"/ /backup/tutor-before-upgrade/
pip install --upgrade "tutor<24" "tutor-mfe<24" "tutor-indigo<24"
tutor local launchالتوسع Scaling: من سيرفر واحد إلى Kubernetes
دليل التوسع في Tutor يفرق بين ثلاثة أشياء: التوسع العمودي Vertical Scaling بزيادة موارد السيرفر نفسه، والتوسع الأفقي Horizontal Scaling بإضافة سيرفرات، والتوافرية العالية High Availability، ويقول بوضوح إن السيرفر الواحد نقطة فشل وحيدة Single Point of Failure، فلا توافرية عالية معه. وفيما يلي المراحل كما يصفها الدليل:
| المرحلة | ماذا تفعل | الأمر أو الإعداد |
|---|---|---|
| سيرفر واحد أكبر | زد عمليات uWSGI لمنصة التعلم والاستوديو، والافتراضي 2 لكل منهما، وكل عملية تحتاج أكثر من 500MB | OPENEDX_LMS_UWSGI_WORKERS=8 وOPENEDX_CMS_UWSGI_WORKERS=4 |
| فصل البيانات | انقل قواعد البيانات خارج السيرفر بالترتيب: MySQL ثم MongoDB ثم Redis ثم تخزين الملفات | RUN_MYSQL=false وMYSQL_HOST=… |
| عدة سيرفرات | Kubernetes بدلاً من Docker Compose، وزيادة النسخ Replicas لكل خدمة | tutor k8s scale lms 8 |
والدليل يذكر مثالاً منقولاً عن منصة كبيرة تخدم 500 ألف مستخدم مختلف في الأسبوع على سيرفر واحد فيه 8 معالجات و16GB من الذاكرة، وهو رقم مفيد لتعرف أن السيرفر الواحد يذهب بعيداً، ولكنه ليس وعداً، فالحمل الحقيقي يحدده عدد المتصلين في اللحظة نفسها، وليس عدد المستخدمين في الأسبوع، وقسه كما شرحنا في اختبار الأحمال والضغط. ونقطة أخرى من الدليل: إذا كنت تتوقع أن تنتقل إلى عدة سيرفرات يوماً ما، فخزن الملفات المرفوعة في تخزين متوافق مع S3 من البداية عبر إضافة tutor-minio، لأن تخزينها في مجلد على السيرفر يصعب التوسع بعد ذلك.
متى تختار Open edX؟
- نقاط القوة: صمم لمقررات مفتوحة بأعداد كبيرة، وتجربة المتعلم حديثة ومنظمة في وحدات قصيرة، والمقرر ملفات OLX يمكن مراجعتها في Git، وTutor يوحد التثبيت والترقية، وطريق واضح إلى Kubernetes.
- نقاط الضعف: يحتاج 5GB من الذاكرة وهو خامل، والتهيئة والترقية ثقيلتان على المعالج، والعربية أقل اكتمالاً من Moodle، وأدوات الفصل الدراسي التقليدي مثل الشعب والحضور ليست من طبيعته، والإضافات الأساسية مثل المنتديات تثبت منفصلة.
فإذا كانت مؤسستك تطلق مقررات مفتوحة للجمهور أو برامج تدريب ذاتي لآلاف المتدربين فهو مناسب جداً، وأما مدرسة بفصول وشعب ومعلمين يرفعون واجبات أسبوعية فالأقرب لها Moodle.
مشكلات شائعة وحلولها Troubleshooting
MySQL initialisation errorفي أول تشغيل: MySQL لم يكمل إقلاعه الأول، فانتظر حتى ترىready for connectionsفيtutor local logs mysql، ثم شغلtutor local do init.- المتصفح يذهب إلى عنوان بلا منفذ: المنصة تبني عناوين الواجهات الأمامية من النطاق وحده، فشغلها على 80 و443 أو خلف Reverse Proxy عليهما.
- الصفحة تبقى في حالة تحميل: صفحات الواجهات الأمامية تتصل بمنصة التعلم بعد التحميل، وفي تجربتنا احتاجت صفحة المقرر أكثر من 20 ثانية في أول فتح بعد التشغيل، لأن العمليات تبدأ بارد Cold Start، فانتظر قبل أن تبحث عن خطأ.
- العاملون يصلون إلى حد الذاكرة: كل عامل Celery يشغل عملية لكل معالج، فارفع الحد أو خفف عدد العمليات قبل أن يقتل النظام العامل بسبب نقص الذاكرة.
- النقاشات لا تظهر: المنتديات إضافة منفصلة
tutor-forum، وليست جزءاً من Tutor الأساسي.
تنظيف المختبر
tutor local dc down --remove-orphans Container edu1-openedx-mongodb-1 Removed
Container edu1-openedx-permissions-1 Removed
Container edu1-openedx-mysql-1 Removed
Network edu1-openedx_default Removing
Network edu1-openedx_default Removedهذا الأمر يحذف ال Containers والشبكة فقط، والبيانات تبقى في مجلد Tutor، فإذا أردت حذف كل شيء فاحذف المجلد بعد أن تتأكد أنه لا يحمل شيئاً تحتاجه، ثم احذف ال Docker Images بأسمائها الكاملة بأمر docker image rm، فهي وحدها أكثر من 8GB.
تدريب عملي
- صدر المقرر من الاستوديو، وقارن المجلد الناتج بملفات OLX التي كتبناها، ثم أضف وحدة جديدة فيها فيديو واستوردها مرة أخرى على نسخة من المنصة.
- غير
max_attemptsفي السؤال إلى 1، وأجب إجابة خاطئة بحسابstudent2، ثم راقب صفحة التقدم ورسالة النجاح.
الخلاصة
وصلنا لنهاية الموضوع، وأهم ما فيه:
- Open edX منصتان: منصة التعلم LMS والاستوديو Studio، ومعهما واجهات أمامية MFE وعاملان وأربع قواعد بيانات، وTutor هو الطريقة المعتمدة لتشغيلها.
- الإصدار الحالي Verawood مع Tutor 22، والإصدار الأحدث هو الوحيد المدعوم، والإصدار التالي Willow في ديسمبر 2026.
- ال Docker Images وحدها نحو 8.1GB، والمنصة الخاملة تستهلك نحو 5.4GB من الذاكرة، والعاملان أكبر المستهلكين.
- التهيئة الأولى طبقت 1378 خطوة ترحيل، وهي ثقيلة على المعالج، والمنصة مصممة لتعمل على 80 و443.
- المقرر يمكن أن يكون ملفات OLX في Git، والدرجة تصل إلى صفحة التقدم بحسب سياسة التقييم.
- النسخة الكاملة هي MySQL وMongoDB والملفات و
config.ymlمعاً، والترقية بين الإصدارات الرئيسية لا رجوع عنها إلا من هذه النسخة.
سجل التحديثات
- أكتوبر 2026: كتابة الدليل وتجربته على Open edX Verawood مع Tutor 22.0.2 وMySQL 8.4.11 وMongoDB 7.0.39.