نشر تطبيق Django باستخدام Nginx وGunicorn
يُعد نشر تطبيقات Django على خادم حقيقي من أهم المراحل التي يمر بها مطورو Python عند الانتقال من بناء التطبيقات محلياً إلى تقديم خدمات ويب يستطيع المستخدمون الوصول إليها عبر الإنترنت. فبعد الانتهاء من تصميم النماذج (Models)، وإنشاء واجهات المستخدم، وبناء واجهات API، وربط قاعدة البيانات، تأتي مرحلة مختلفة تماماً تتطلب الاهتمام بالأمان والأداء وإدارة العمليات ومراقبة الأخطاء. وهنا تظهر أهمية الجمع بين Django وGunicorn وNginx، وهي مجموعة تقنيات تُستخدم على نطاق واسع لتشغيل تطبيقات Python على خوادم Linux.
قد يبدو تشغيل تطبيق Django على جهازك أمراً بسيطاً للغاية؛ يكفي تنفيذ الأمر python manage.py runserver ثم فتح المتصفح على localhost:8000. لكن هذا الخادم مخصص للتطوير، وليس لتقديم تطبيق حقيقي يعتمد عليه المستخدمون. عندما يبدأ التطبيق باستقبال طلبات متعددة، ويحتاج إلى معالجة ملفات ثابتة، والتعامل مع اتصالات HTTPS، وإدارة العمليات التي قد تتوقف أو تتعطل، تصبح الحاجة إلى بنية نشر مناسبة أمراً أساسياً. لذلك سنشرح في هذا الدليل كيفية إعداد خادم Ubuntu، وتجهيز بيئة Python، وتثبيت Django وGunicorn، وضبط Nginx، وربط التطبيق بقاعدة البيانات، وإعداد HTTPS، ثم تحسين الأداء ومراقبة الخدمة وصيانتها.
سنستخدم مشروعاً افتراضياً اسمه myproject، ومستخدماً مخصصاً لتشغيل التطبيق، وخادماً يعمل بنظام Ubuntu. وسنفترض أن المشروع موجود في مجلد /srv/myproject، وأن اسم النطاق هو kitaboka.com. يمكنك استبدال هذه القيم بمعلومات مشروعك الفعلية. الهدف ليس مجرد نسخ الأوامر وتشغيلها، بل فهم وظيفة كل مكون، ولماذا نحتاج إليه، وكيفية تشخيص المشكلات التي قد تظهر أثناء النشر.
البنية التي سننشئها

المتصفح يتصل بـ Nginx، الذي يمرر الطلبات الديناميكية إلى Gunicorn، ثم يتولى Django تنفيذ منطق التطبيق.
1. فهم مكونات بيئة النشر
قبل البدء في تنفيذ الأوامر، من المفيد أن نفهم الدور الذي يؤديه كل مكون في البنية التي سنستخدمها. فكثير من مشكلات النشر لا تنتج عن خطأ في الكود نفسه، بل عن سوء فهم العلاقة بين الخوادم أو عن إعداد غير صحيح للاتصال بينها. عندما نعرف أين ينتهي دور Nginx وأين يبدأ دور Gunicorn، يصبح تشخيص أخطاء مثل 502 Bad Gateway أو DisallowedHost أكثر سهولة.
1.1 ما هو Django؟
Django هو إطار عمل لتطوير تطبيقات الويب باستخدام Python. يوفر مجموعة متكاملة من الأدوات التي تساعد على بناء التطبيقات بسرعة، مثل نظام توجيه الروابط، وطبقة التعامل مع قواعد البيانات ORM، ونظام المصادقة، ولوحة الإدارة، والوسائط البرمجية Middleware، وآليات الحماية من بعض الهجمات الشائعة.
عند وصول طلب HTTP إلى تطبيق Django، يمر الطلب بمجموعة من المراحل. يبدأ الأمر عادةً بمسار URL يحدد الوظيفة المطلوبة، ثم يتم تنفيذ View المناسبة، وقد تتواصل هذه الوظيفة مع قاعدة البيانات، وتستدعي خدمات أخرى، ثم تعيد استجابة HTML أو JSON أو ملفاً أو نوعاً آخر من المحتوى. أثناء هذه العملية، يمكن أن تتدخل طبقات Middleware لتنفيذ وظائف مشتركة مثل إدارة الجلسات، والتحقق من CSRF، وتسجيل الدخول، ومعالجة الاستجابات.
لكن Django ليس مسؤولاً وحده عن جميع تفاصيل تشغيل الخادم. فهو يوفر إطار التطبيق، بينما تتولى خوادم أخرى استقبال الاتصالات وإدارة العمليات وتقديم الملفات الثابتة والتعامل مع TLS. لذلك لا ينبغي اعتبار نجاح التطبيق باستخدام خادم التطوير دليلاً على أن بيئة الإنتاج جاهزة.
1.2 ما هو Gunicorn؟
Gunicorn، أو Green Unicorn، هو خادم WSGI لتشغيل تطبيقات Python. يعمل كحلقة وصل بين طبقة HTTP وتطبيق Django الذي يستخدم واجهة WSGI التقليدية. يستطيع Gunicorn تشغيل عدة Workers، بحيث يمكنه معالجة طلبات متعددة بالتوازي وفقاً لنوع العمال وإعداداتهم وموارد الخادم.
بدلاً من تشغيل Django بخادم التطوير، نستخدم Gunicorn لتشغيل التطبيق في بيئة الإنتاج. ويمكن أن يعمل Gunicorn خلف Nginx، بحيث لا يحتاج إلى استقبال اتصالات الإنترنت العامة مباشرةً. في هذه الحالة يستقبل Gunicorn الطلبات من Nginx عبر منفذ داخلي أو Unix Socket.
من المهم معرفة أن Gunicorn ليس بديلاً عن Nginx في جميع الوظائف. فهو مسؤول أساساً عن تشغيل تطبيق Python وتوزيع الطلبات على العمال، بينما يتفوق Nginx في تقديم الملفات الثابتة، وإدارة اتصالات العملاء، وإنهاء اتصال HTTPS، وتنفيذ وظائف الوكيل العكسي.
1.3 ما هو Nginx؟
Nginx هو خادم ويب وReverse Proxy يمكنه استقبال اتصالات HTTP وHTTPS من المستخدمين. في بنيتنا، سيكون Nginx هو نقطة الدخول العامة للتطبيق. يستطيع استقبال الطلبات على المنفذين 80 و443، والتحقق من اسم النطاق، وتقديم ملفات CSS وJavaScript والصور مباشرةً، ثم تمرير الطلبات الديناميكية إلى Gunicorn.
تظهر أهمية Nginx خصوصاً عندما يحتوي التطبيق على ملفات ثابتة كثيرة أو يحتاج إلى اتصال HTTPS أو يواجه عدداً كبيراً من الاتصالات. كما يتيح ضبط رؤوس HTTP، وحدود حجم الطلبات، وسياسات التخزين المؤقت، والضغط، وإعادة التوجيه، وغيرها من الإعدادات.
1.4 الفرق بين خادم التطوير وبيئة الإنتاج
خادم التطوير الذي يأتي مع Django مصمم لتسهيل عملية البرمجة، وإعادة تحميل التطبيق تلقائياً، وإظهار الأخطاء أثناء التطوير. لكنه ليس مصمماً ليكون نقطة الدخول العامة لتطبيق يعتمد عليه مستخدمون حقيقيون.
في الإنتاج نحتاج إلى فصل المهام، وإدارة العمليات، وتحديد عدد العمال، وتوفير سجلات واضحة، وإعداد الوصول الآمن، وتحديد كيفية التعامل مع الملفات والبيانات. ومن هنا تأتي البنية التي تجمع بين Nginx وGunicorn وDjango.
المكون | الوظيفة الأساسية | ما لا ينبغي الاعتماد عليه وحده |
|---|---|---|
Django | منطق التطبيق ومعالجة الطلبات | استقبال اتصالات الإنترنت وإدارة الخادم بالكامل |
Gunicorn | تشغيل تطبيق Python وإدارة Workers | تقديم جميع الملفات الثابتة وإدارة TLS كحل كامل |
Nginx | استقبال الاتصالات والوكالة العكسية والملفات | تنفيذ منطق التطبيق المكتوب في Django |
systemd | تشغيل الخدمة وإعادة تشغيلها وإدارتها | معالجة طلبات HTTP |
PostgreSQL | تخزين البيانات واسترجاعها | تشغيل منطق التطبيق أو تقديم صفحات الويب |
2. التحضير قبل نشر تطبيق Django
تبدأ عملية النشر الجيدة قبل الاتصال بالخادم. من الأفضل مراجعة المشروع محلياً، والتأكد من أن الاعتماديات واضحة، وأن إعدادات الإنتاج منفصلة عن إعدادات التطوير، وأن الملفات الحساسة غير موجودة في مستودع Git. فكل مشكلة في هذه المرحلة قد تتحول إلى توقف للخدمة أو تسريب بيانات أو صعوبة في تحديث التطبيق بعد إطلاقه.
2.1 المتطلبات الأساسية
سنحتاج إلى خادم افتراضي VPS أو خادم مخصص يعمل بنظام Linux، ويفضل أن يكون Ubuntu بإصدار مدعوم. كما نحتاج إلى صلاحية SSH، واسم نطاق يشير إلى عنوان IP الخاص بالخادم، وإصدار Python متوافق مع إصدار Django والمكتبات المستخدمة.
المتطلبات النموذجية لهذا الدليل:
Ubuntu Server بإصدار مدعوم.
Python 3 و
venvوpip.Git لنقل المشروع أو تحديثه.
Django وإعتماديات المشروع.
Gunicorn.
Nginx.
قاعدة بيانات مثل PostgreSQL أو MySQL، حسب احتياجات التطبيق.
شهادة TLS عند نشر التطبيق على الإنترنت.
مستخدم Linux مخصص لتشغيل المشروع.
لا يعني ذلك أن كل مشروع يحتاج إلى تثبيت قاعدة بيانات على الخادم نفسه. يمكن استخدام قاعدة بيانات مُدارة على خادم منفصل، وقد يكون ذلك مناسباً عندما تريد فصل موارد التطبيق عن موارد التخزين أو الاستفادة من النسخ الاحتياطية وإدارة قواعد البيانات التي يقدمها مزود الخدمة.
2.2 التأكد من جاهزية المشروع محلياً
قبل النشر، شغّل الاختبارات الموجودة في المشروع، وتأكد من أن إعدادات قاعدة البيانات تعمل، وأن ملفات البيئة المطلوبة موثقة، وأن التطبيق لا يعتمد على مسارات محلية موجودة على جهاز التطوير فقط.
يمكن تنفيذ بعض الفحوصات الأساسية:
python --version
python -m pip --version
python manage.py check
python manage.py testإذا كنت تستخدم ملف اعتماديات مثل requirements.txt، فتأكد من أنه يحتوي على المكتبات اللازمة لتشغيل التطبيق، وليس فقط المكتبات المثبتة على جهازك بشكل عشوائي.
python -m pip freeze > requirements.txtهذا الأمر قد يكون مناسباً لتوثيق بيئة موجودة، لكنه ليس دائماً أفضل طريقة لإدارة الاعتماديات في المشاريع الكبيرة. الأفضل أن تحافظ على ملف اعتماديات واضح ومراجع، وأن تتجنب إدراج حزم غير مستخدمة أو مكتبات تطوير داخل بيئة الإنتاج دون حاجة.
2.3 فصل إعدادات التطوير عن الإنتاج
من أكثر الأخطاء شيوعاً استخدام إعدادات التطوير نفسها في الخادم الحقيقي. فمثلاً، قد يكون DEBUG=True أثناء التطوير مفيداً، لكنه قد يكشف تفاصيل داخلية عند حدوث استثناء في الإنتاج. كما أن السماح بجميع المضيفين أو استخدام مفاتيح سرية ثابتة داخل الكود يسبب مخاطر أمنية.
من الأفضل الاعتماد على متغيرات البيئة لتحديد الإعدادات الحساسة والمتغيرة بين البيئات. يمكن أن يكون لديك ملف إعدادات أساسي، مع فصل إعدادات الإنتاج في ملف مستقل أو تحميل القيم من البيئة.
مثال مبسط:
# settings.pyimport osfrom pathlib import PathBASE_DIR = Path(__file__).resolve().parent.parentSECRET_KEY = os.environ["DJANGO_SECRET_KEY"]DEBUG = os.environ.get("DJANGO_DEBUG", "False").lower() == "true"ALLOWED_HOSTS = [ host.strip() for host in os.environ.get( "DJANGO_ALLOWED_HOSTS", "kitaboka.com,www.kitaboka.com" ).split(",") if host.strip()]في هذا المثال، لا نضع المفتاح السري الحقيقي داخل ملف Python. كما أن DEBUG تكون قيمته الافتراضية False، وهي نقطة مهمة عند تشغيل التطبيق في بيئة إنتاجية. أما ALLOWED_HOSTS فتحتوي على أسماء النطاقات التي يُسمح للتطبيق باستقبال الطلبات الموجهة إليها.
ينبغي أيضاً مراجعة إعدادات CSRF وملفات الوسائط والجلسات والبريد الإلكتروني وقاعدة البيانات. إعدادات الأمان ليست مجرد قيمة واحدة، بل مجموعة من الخيارات التي يجب أن تتوافق مع طريقة نشر التطبيق.
3. إعداد خادم Ubuntu
بعد التأكد من جاهزية المشروع، نبدأ بتجهيز الخادم. سنستخدم SSH للاتصال به، ثم نحدّث الحزم، وننشئ مستخدماً مخصصاً، ونجهز المجلدات التي سيعمل منها التطبيق. من الأفضل تجنب تشغيل التطبيق نفسه بصلاحيات root، لأن ذلك يزيد من أثر أي ثغرة قد تؤدي إلى تنفيذ أوامر داخل العملية.
3.1 الاتصال بالخادم عبر SSH
من جهازك المحلي، افتح الطرفية ونفذ الأمر التالي مع استبدال عنوان IP بعنوان خادمك:
ssh root@YOUR_SERVER_IPإذا كان مزود الخادم قد أنشأ مستخدماً إدارياً غير root، فاستخدمه بدلاً من ذلك. بعد الاتصال، تحقق من إصدار النظام:
lsb_release -a
uname -aثم قم بتحديث قائمة الحزم وتثبيت التحديثات المتاحة:
sudo apt update
sudo apt upgrade -yقد تحتاج إلى إعادة تشغيل الخادم إذا تضمنت التحديثات مكونات أساسية مثل النواة. تحقق من رسائل النظام وإرشادات مزود الخادم قبل إعادة التشغيل، خصوصاً إذا كانت هناك خدمات أخرى تعمل عليه.
3.2 إنشاء مستخدم مخصص للتطبيق
سننشئ مستخدماً اسمه djangoapp لتشغيل المشروع:
sudo adduser djangoappيمكن بعد ذلك إنشاء مجلد المشروع وتحديد الملكية:
sudo mkdir -p /srv/myproject
sudo chown -R djangoapp:djangoapp /srv/myprojectيمكنك تسجيل الدخول إلى هذا المستخدم أو تنفيذ أوامر المشروع باسمه. ومن الأفضل أن تكون ملكية ملفات الكود واضحة، وأن تكون صلاحيات الكتابة محدودة. ليس من الضروري أن يمتلك مستخدم التطبيق صلاحية تعديل جميع ملفات النظام، ولا ينبغي أن يحصل على صلاحيات إدارية إلا عند الحاجة إلى مهمة إدارية محددة.
3.3 تثبيت Python والأدوات اللازمة
على Ubuntu، يمكن تثبيت Python وبيئة venv وGit باستخدام:
sudo apt install -y \
python3 \
python3-venv \
python3-pip \
git \
build-essentialقد تحتاج بعض المكتبات إلى حزم تطوير إضافية أثناء تثبيت الاعتماديات، مثل رؤوس مكتبات PostgreSQL أو أدوات بناء مكتبات Python التي تحتوي على امتدادات أصلية. إذا ظهر خطأ متعلق بمكتبة معينة، راجع وثائقها لمعرفة الحزمة النظامية المطلوبة بدلاً من تثبيت حزم عشوائية.
تحقق من الإصدارات:
python3 --version
git --versionاحرص على استخدام إصدار Python مدعوم من إصدار Django الذي يعتمد عليه مشروعك. توافق الإصدارات مهم خصوصاً إذا كان المشروع قديماً أو يحتوي على مكتبات لم تُحدّث منذ فترة.
3.4 إعداد الجدار الناري
إذا كنت تستخدم UFW، يمكنك السماح باتصالات SSH وHTTP وHTTPS:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw statusتأكد من أن قاعدة SSH مسموح بها قبل تفعيل الجدار الناري، حتى لا تفقد القدرة على الاتصال بالخادم. كما ينبغي أن تكون منافذ Gunicorn الداخلية غير متاحة للعامة، لأن Nginx سيكون نقطة الدخول الخارجية.
إذا كنت تستخدم مزوداً يوفر جداراً نارياً من لوحة التحكم، فراجع قواعده أيضاً. إعداد UFW لا يضمن وحده أن كل حركة المرور مسموح بها أو محظورة على مستوى الشبكة الخارجية.
4. نقل مشروع Django إلى الخادم
يمكن نقل المشروع باستخدام Git، أو عبر آلية نشر آلية، أو باستخدام ملفات يتم رفعها بطريقة آمنة. سنستخدم Git في هذا المثال، لأنه يسهّل تحديث المشروع وتتبع الإصدارات.
4.1 استنساخ المستودع
سجّل الدخول إلى المستخدم المخصص:
sudo -iu djangoappثم انتقل إلى مجلد المشروع:
cd /srv/myprojectإذا كان المجلد فارغاً ومخصصاً للمشروع، يمكنك استنساخ المستودع داخله:
git clone https://github.com/USERNAME/REPOSITORY.git .استبدل USERNAME/REPOSITORY بعنوان مستودعك. إذا كان المستودع خاصاً، استخدم طريقة مصادقة آمنة، مثل مفاتيح SSH أو آلية نشر مخصصة، وتجنب وضع رموز الوصول داخل الأوامر التي قد تُحفظ في سجل الطرفية.
بعد الاستنساخ، تحقق من بنية الملفات:
ls -laقد يكون ملف manage.py في جذر المستودع، أو داخل مجلد فرعي، حسب طريقة تنظيم المشروع. في بقية الدليل سنفترض أنه موجود في /srv/myproject/manage.py.
4.2 إنشاء البيئة الافتراضية
البيئة الافتراضية تساعد على عزل مكتبات المشروع عن مكتبات النظام وعن المشاريع الأخرى الموجودة على الخادم.
cd /srv/myproject
python3 -m venv venv
source venv/bin/activateبعد تفعيل البيئة، يجب أن يشير الأمر python إلى المفسر الموجود داخل venv. تحقق من ذلك:
which python
python --versionثم حدّث أدوات تثبيت الحزم:
python -m pip install --upgrade pip setuptools wheelإنشاء البيئة الافتراضية داخل مجلد المشروع خيار عملي في هذا المثال، لكن يمكن وضعها في مجلد مستقل إذا كانت لديك سياسة تنظيم خاصة بالخادم.
4.3 تثبيت اعتماديات المشروع
إذا كان لديك requirements.txt:
pip install -r requirements.txtبعد ذلك ثبّت Gunicorn إذا لم يكن ضمن ملف الاعتماديات:
pip install gunicornمن الأفضل تثبيت الإصدارات التي تم اختبارها، وعدم الاعتماد على أحدث إصدار لكل مكتبة بصورة تلقائية في خادم الإنتاج. يمكنك تثبيت Gunicorn ضمن ملف الاعتماديات الخاص بالمشروع حتى تكون بيئة التشغيل قابلة لإعادة البناء.
تحقق من تثبيت الحزم:
pip show django
pip show gunicornثم نفّذ فحص Django:
python manage.py checkإذا ظهرت أخطاء في هذه المرحلة، فمن الأفضل حلها قبل الانتقال إلى إعداد Gunicorn وNginx. فكل طبقة إضافية تجعل تتبع سبب المشكلة أصعب إذا لم يكن التطبيق نفسه يعمل بصورة صحيحة.
5. إعداد متغيرات البيئة في الإنتاج
تحتاج تطبيقات Django عادةً إلى معلومات لا ينبغي تضمينها مباشرةً في ملفات الكود، مثل المفتاح السري، وكلمات مرور قاعدة البيانات، ومعلومات خدمات البريد الإلكتروني، ومفاتيح واجهات API. لذلك سنستخدم ملف بيئة خارج مجلد الكود، ونضبط صلاحياته بحيث لا يستطيع قراءته إلا المستخدمون المصرح لهم.
5.1 إنشاء ملف البيئة
يمكننا إنشاء ملف باسم /etc/myproject.env بصلاحيات إدارية:
sudo nano /etc/myproject.envضع فيه القيم المناسبة لمشروعك:
DJANGO_SECRET_KEY=replace-with-a-long-random-secret
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=example.com,www.example.com
DB_NAME=myproject
DB_USER=myproject_user
DB_PASSWORD=replace-with-a-strong-password
DB_HOST=127.0.0.1
DB_PORT=5432
EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=notifications@example.com
EMAIL_HOST_PASSWORD=replace-with-email-passwordهذه مجرد أسماء متغيرات نموذجية؛ يجب أن تتطابق مع الأسماء التي يقرأها مشروعك. لا يكفي إنشاء ملف البيئة وحده، بل يجب أن تكون إعدادات Django مهيأة لاستخدام هذه القيم.
يمكن توليد مفتاح سري عشوائي باستخدام Python:
python -c "import secrets; print(secrets.token_urlsafe(50))"ثم ضع القيمة الناتجة في الملف بدلاً من النص التجريبي. لا تستخدم مفتاحاً منشوراً في مستودع عام، ولا تعِد استخدام مفتاح مشروع آخر.
اضبط الصلاحيات:
sudo chown root:djangoapp /etc/myproject.env
sudo chmod 640 /etc/myproject.envبهذه الطريقة، يستطيع root والمستخدمون ضمن المجموعة المناسبة قراءة الملف، بينما لا يُسمح للمستخدمين الآخرين بقراءته. راجع سياسة مجموعات المستخدمين على الخادم للتأكد من أن الوصول محصور بالفعل.
5.2 قراءة متغيرات البيئة داخل Django
في ملف إعدادات المشروع، يمكن استخدام os.environ:
import osSECRET_KEY = os.environ["DJANGO_SECRET_KEY"]DEBUG = os.environ.get("DJANGO_DEBUG", "False").lower() == "true"DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": os.environ["DB_NAME"], "USER": os.environ["DB_USER"], "PASSWORD": os.environ["DB_PASSWORD"], "HOST": os.environ.get("DB_HOST", "127.0.0.1"), "PORT": os.environ.get("DB_PORT", "5432"), }}إذا كنت تستخدم MySQL، فستحتاج إلى محرك قاعدة البيانات المناسب وإعدادات الاتصال الخاصة به. لا تستخدم إعدادات PostgreSQL الواردة هنا دون تغييرها عندما يكون مشروعك مبنياً على MySQL.
ومن المهم ألا تطبع كلمات المرور أو المفتاح السري داخل سجلات التطبيق. عند تشخيص مشكلة في إعدادات البيئة، اطبع أسماء المتغيرات أو تحقق من وجودها، وليس قيمها الحساسة.
6. إعداد قاعدة البيانات وتشغيل migrations
تُعد قاعدة البيانات جزءاً أساسياً من معظم تطبيقات Django. قد يعمل التطبيق محلياً بقاعدة SQLite، لكن عند النشر قد تختار PostgreSQL أو MySQL بسبب احتياجات الأداء والتزامن والإدارة والنسخ الاحتياطي.
سنستخدم PostgreSQL كمثال. إذا كانت قاعدة البيانات مستضافة خارج الخادم، فتجاوز تثبيت PostgreSQL المحلي، واضبط معلومات الاتصال وفقاً لما يوفره مزود قاعدة البيانات.
6.1 تثبيت PostgreSQL
على Ubuntu:
sudo apt update
sudo apt install -y postgresql postgresql-contribتحقق من حالة الخدمة:
sudo systemctl status postgresqlإذا كانت الخدمة غير مفعلة:
sudo systemctl enable --now postgresql6.2 إنشاء قاعدة البيانات والمستخدم
ادخل إلى واجهة PostgreSQL:
sudo -u postgres psqlثم أنشئ مستخدماً وقاعدة بيانات:
CREATE USER myproject_user
WITH PASSWORD 'replace-with-a-strong-password';
CREATE DATABASE myproject
OWNER myproject_user;
\qاستبدل كلمة المرور بالقيمة الفعلية نفسها التي وضعتها في ملف البيئة. لا تستخدم كلمة المرور التجريبية كما هي.
يمكنك اختبار الاتصال باستخدام:
psql -h 127.0.0.1 \
-U myproject_user \
-d myprojectسيطلب منك إدخال كلمة المرور. إذا نجح الاتصال، فهذا يعني أن إعدادات المستخدم وقاعدة البيانات الأساسية صحيحة.
في بيئة الإنتاج، راجع أيضاً إعدادات PostgreSQL المتعلقة بالمصادقة، والاستماع على الشبكة، والصلاحيات. لا تفتح منفذ قاعدة البيانات للإنترنت العام إذا كان التطبيق وقاعدة البيانات يعملان على الخادم نفسه ولا توجد حاجة لذلك.
6.3 تشغيل migrations
بعد تثبيت الاعتماديات وضبط متغيرات البيئة، انتقل إلى مجلد المشروع وتأكد من تفعيل البيئة الافتراضية. ثم نفّذ:
cd /srv/myproject
source venv/bin/activate
python manage.py migrateيقوم الأمر بتطبيق تغييرات مخطط قاعدة البيانات المسجلة في ملفات migrations. إذا كان المشروع يحتوي على تطبيقات متعددة، فقد تتضمن العملية إنشاء جداول وعلاقات وفهارس وقيود.
لا تنفذ migrations بصورة عمياء على قاعدة بيانات إنتاجية تحتوي على بيانات مهمة. راجع التغييرات، وخذ نسخة احتياطية عند الحاجة، وتحقق من أن التغييرات الكبيرة لن تسبب توقفاً طويلاً أو تعارضاً مع إصدار التطبيق الحالي.
6.4 إنشاء مستخدم الإدارة
إذا كان مشروعك يستخدم لوحة إدارة Django:
python manage.py createsuperuserاتبع التعليمات لإدخال اسم المستخدم والبريد الإلكتروني وكلمة المرور. استخدم كلمة مرور قوية، وفكر في تقييد الوصول إلى لوحة الإدارة من خلال سياسات إضافية عند الحاجة، مثل VPN أو قواعد وصول على مستوى Nginx أو طبقة مصادقة إضافية.
6.5 جمع الملفات الثابتة
تحتاج تطبيقات Django غالباً إلى ملفات CSS وJavaScript والصور والخطوط. في الإنتاج، يُستخدم collectstatic لجمع الملفات الثابتة من التطبيقات ومجلدات الأصول إلى مجلد موحد.
في إعدادات Django:
STATIC_URL = "/static/"STATIC_ROOT = BASE_DIR / "staticfiles"MEDIA_URL = "/media/"MEDIA_ROOT = BASE_DIR / "media"بعد التأكد من أن الإعدادات صحيحة، نفّذ:
python manage.py collectstatic --noinputسيجمع Django الملفات الثابتة في STATIC_ROOT. أما الملفات التي يرفعها المستخدمون، فتُحفظ عادةً في MEDIA_ROOT أو في تخزين خارجي، ولا ينبغي الخلط بينها وبين الملفات الثابتة التي تأتي مع التطبيق.
من المهم أيضاً الانتباه إلى أن بعض التطبيقات تستخدم تخزيناً خارجياً للملفات، مثل خدمات Object Storage. في هذه الحالة قد لا يكون تقديم ملفات الوسائط مباشرةً من Nginx على الخادم نفسه هو التصميم المناسب.
7. اختبار Gunicorn قبل ربطه بـ Nginx
قبل إنشاء خدمة systemd أو تعديل إعدادات Nginx، من الأفضل تشغيل Gunicorn يدوياً والتحقق من أنه يستطيع تحميل تطبيق Django. هذه الخطوة تساعد على فصل أخطاء التطبيق عن أخطاء الوكيل العكسي.
7.1 تحديد ملف WSGI
غالباً ما يحتوي مشروع Django على مجلد إعدادات يتضمن settings.py وurls.py وwsgi.py. إذا كان اسم مجلد الإعدادات config، فسيكون مسار WSGI عادةً:
config.wsgi:applicationأما إذا كان اسم المجلد myproject:
myproject.wsgi:applicationتحقق من بنية المشروع قبل اختيار المسار. استخدام اسم غير صحيح يؤدي إلى أخطاء مثل ModuleNotFoundError أو عدم العثور على تطبيق WSGI.
7.2 تشغيل Gunicorn على localhost
نفّذ الأمر التالي من مجلد المشروع:
cd /srv/myproject
source venv/bin/activate
gunicorn config.wsgi:application \
--bind 127.0.0.1:8000 \
--workers 3استبدل config باسم حزمة إعدادات Django الحقيقية. هنا نربط Gunicorn بعنوان 127.0.0.1، أي أن الخدمة تستقبل الاتصالات المحلية فقط. لن يتمكن المستخدمون من الوصول إليها مباشرةً عبر الإنترنت، وهذا مناسب لأن Nginx سيتولى استقبال الاتصالات العامة.
افتح جلسة طرفية أخرى على الخادم واختبر الاستجابة:
curl -I http://127.0.0.1:8000/إذا أعاد التطبيق استجابة HTTP، فهذا مؤشر على أن Gunicorn استطاع تشغيل Django. لكن نجاح هذا الاختبار لا يثبت أن جميع الصفحات تعمل، أو أن قاعدة البيانات والملفات والبريد الإلكتروني مضبوطة بصورة كاملة.
أوقف Gunicorn التجريبي باستخدام Ctrl+C قبل الانتقال إلى إعداد الخدمة الدائمة.
7.3 فهم إعداد Workers
يستطيع Gunicorn تشغيل عدة Workers، وكل Worker يعالج الطلبات وفق نموذج التنفيذ الذي يستخدمه. في الإعداد الافتراضي، تُستخدم Workers من نوع sync، وهي مناسبة لكثير من تطبيقات Django التقليدية.
يمكن بدء الاختبار بثلاثة Workers، لكن العدد الفعلي يعتمد على عدد أنوية المعالج، وحجم الذاكرة، وطبيعة الطلبات، ووقت الاستجابة، واستخدام قاعدة البيانات. لا توجد قيمة واحدة تناسب جميع التطبيقات.
إذا كان لديك تطبيق يقوم بعمليات حسابية ثقيلة أو يحتفظ باتصالات طويلة أو ينتظر خدمات خارجية، فقد تحتاج إلى تقييم نوع Worker وإعداداته. أما إذا كنت تستخدم Django ASGI وتحتاج إلى خصائص غير متزامنة، فقد يكون خادم ASGI مثل Uvicorn أو Daphne مناسباً وفق بنية التطبيق. هذا الدليل يركز على WSGI باستخدام Gunicorn.
8. إعداد systemd لتشغيل Gunicorn باستمرار
تشغيل Gunicorn من الطرفية مناسب للاختبار، لكنه ليس طريقة إدارة عملية طويلة الأمد في الإنتاج. فإذا أغلقت الجلسة أو أعيد تشغيل الخادم، قد تتوقف العملية. لذلك سنستخدم systemd لإنشاء خدمة تدير تشغيل Gunicorn، وتعيد تشغيله عند فشله وفق سياسة محددة، وتتيح لنا التحكم فيه باستخدام systemctl.
8.1 إنشاء ملف الخدمة
أنشئ ملفاً جديداً:
sudo nano /etc/systemd/system/gunicorn.serviceضع الإعداد التالي، مع تعديل اسم وحدة WSGI إذا لزم الأمر:
[Unit]
Description=Gunicorn daemon for Django project
After=network.target
[Service]
User=djangoapp
Group=www-data
WorkingDirectory=/srv/myproject
EnvironmentFile=/etc/myproject.env
ExecStart=/srv/myproject/venv/bin/gunicorn \
--workers 3 \
--bind 127.0.0.1:8000 \
--access-logfile - \
--error-logfile - \
config.wsgi:application
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetتوضح هذه الإعدادات أن الخدمة تعمل بالمستخدم djangoapp، وأن مجلد العمل هو مجلد المشروع، وأن متغيرات البيئة تُحمّل من الملف الذي أنشأناه سابقاً. كما يرسل Gunicorn سجلات الوصول والأخطاء إلى مخرجات الخدمة، بحيث يمكن الاطلاع عليها عبر journalctl.
استخدمنا المجموعة www-data لتسهيل التعاون مع Nginx عند الحاجة إلى الوصول إلى موارد مشتركة. لكن لا يعني ذلك أن Nginx يجب أن يمتلك صلاحية الكتابة على جميع ملفات التطبيق. ينبغي تحديد الصلاحيات الفعلية وفق الملفات التي يحتاج كل مكون إلى قراءتها أو كتابتها.
8.2 تفعيل الخدمة وتشغيلها
بعد حفظ الملف:
sudo systemctl daemon-reload
sudo systemctl enable --now gunicornتحقق من الحالة:
sudo systemctl status gunicornإذا ظهرت الخدمة بحالة active (running)، فهذا يعني أن systemd يشغّل العملية حالياً. لكن من الأفضل أيضاً التحقق من السجلات:
sudo journalctl -u gunicorn -n 50 --no-pagerولمتابعة السجلات مباشرةً:
sudo journalctl -u gunicorn -fإذا فشل التشغيل، ابحث عن أول خطأ واضح في السجلات. قد يكون السبب مسار WSGI غير صحيح، أو مكتبة مفقودة، أو متغير بيئة غير موجود، أو خطأ في إعداد قاعدة البيانات، أو مشكلة صلاحيات.
8.3 أوامر إدارة الخدمة
يمكن إدارة Gunicorn باستخدام مجموعة من الأوامر القياسية:
sudo systemctl start gunicorn
sudo systemctl stop gunicorn
sudo systemctl restart gunicorn
sudo systemctl reload-or-restart gunicorn
sudo systemctl status gunicornعند تعديل ملف الخدمة نفسه، يجب تنفيذ:
sudo systemctl daemon-reloadأما عند تعديل كود Django أو ملف إعداداته، فعادةً ما تحتاج إلى إعادة تشغيل Gunicorn حتى تُحمّل العملية الجديدة التغييرات. لا ينبغي الخلط بين إعادة تحميل إعدادات systemd وإعادة تشغيل تطبيق Python.
9. تثبيت Nginx وإعداده كـ Reverse Proxy
بعد أن أصبح Gunicorn يعمل كخدمة، يمكننا إعداد Nginx لاستقبال الطلبات العامة وتمريرها إلى التطبيق. سنبدأ بتثبيت Nginx، ثم ننشئ إعداداً خاصاً بالنطاق، ونحدد كيفية التعامل مع الملفات الثابتة والوسائط والطلبات الديناميكية.
9.1 تثبيت Nginx
نفّذ:
sudo apt update
sudo apt install -y nginxثم فعّل الخدمة:
sudo systemctl enable --now nginxتحقق من الحالة:
sudo systemctl status nginxإذا كان الجدار الناري مفعلاً، فتأكد من السماح باتصالات HTTP وHTTPS. ويمكن اختبار الوصول إلى عنوان IP الخاص بالخادم من المتصفح، لكن ظهور صفحة Nginx الافتراضية لا يعني أن إعداد التطبيق اكتمل.
9.2 إنشاء إعداد الموقع
أنشئ ملف إعداد باسم النطاق:
sudo nano /etc/nginx/sites-available/myprojectأضف الإعداد التالي:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
client_max_body_size 20M;
location /static/ {
alias /srv/myproject/staticfiles/;
access_log off;
expires 30d;
add_header Cache-Control "public";
}
location /media/ {
alias /srv/myproject/media/;
expires 7d;
add_header Cache-Control "public";
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}يحدد server_name النطاقات التي سيستجيب لها هذا الموقع. أما client_max_body_size فيحدد الحد الأقصى لحجم جسم الطلب، وهو مهم عند رفع الملفات. يجب أن تتوافق قيمته مع حدود التطبيق والاحتياجات الفعلية، وألا تكون أكبر من اللازم دون سبب.
في إعداد location /static/، يستخدم Nginx التوجيه alias للوصول مباشرةً إلى مجلد الملفات الثابتة الذي جمعه Django. وينطبق الأمر نفسه على media، بشرط أن تكون الملفات موجودة في المسار المحدد وأن تسمح صلاحيات النظام لـ Nginx بقراءتها.
أما location / فيمرر الطلبات إلى Gunicorn. وتسمح رؤوس proxy_set_header بنقل معلومات مهمة مثل اسم المضيف وعنوان IP الأصلي والبروتوكول الذي استخدمه العميل.
9.3 تفعيل الموقع
أنشئ رابطاً رمزياً داخل مجلد sites-enabled:
sudo ln -s \
/etc/nginx/sites-available/myproject \
/etc/nginx/sites-enabled/myprojectإذا كان إعداد الموقع الافتراضي يتعارض مع النطاق أو يلتقط الطلبات غير المقصودة، فقد تحتاج إلى تعطيله:
sudo rm /etc/nginx/sites-enabled/defaultلا تنفذ هذا الأمر دون التأكد من أن الخادم لا يعتمد على الموقع الافتراضي لخدمة أخرى.
قبل إعادة تحميل Nginx، اختبر الإعداد:
sudo nginx -tإذا ظهر أن الاختبار ناجح، أعد تحميل الخدمة:
sudo systemctl reload nginxإذا فشل الاختبار، لا تعِد تحميل إعداد غير صالح. اقرأ رسالة الخطأ، وحدد الملف والسطر المشار إليهما، ثم صحح المشكلة.
9.4 التحقق من اتصال Nginx بـ Gunicorn
يمكنك اختبار النطاق:
curl -I http://example.comإذا كان DNS مضبوطاً ويشير إلى الخادم، يفترض أن تصل إلى Nginx، الذي يمرر الطلب إلى Gunicorn. قد تظهر أخطاء مختلفة حسب موضع المشكلة:
502 Bad Gateway: غالباً تعذر على Nginx الاتصال بالخادم الخلفي، أو لم يتلق استجابة صالحة.404 Not Found: قد يكون المسار غير موجود في Django، أو أن إعداد الملفات الثابتة لا يشير إلى المكان الصحيح.400 Bad Request: قد يكون اسم المضيف غير مسموح به فيALLOWED_HOSTS.500 Internal Server Error: حدث استثناء أثناء تنفيذ التطبيق، ويجب مراجعة سجلات Django وGunicorn.
لا تعتمد على رمز الحالة وحده لتحديد السبب. راجع سجلات Nginx وسجلات Gunicorn، لأن الرسائل التفصيلية عادةً ما تشير إلى الطبقة التي حدثت فيها المشكلة.
10. ضبط DNS وربط اسم النطاق بالخادم
حتى يستطيع المستخدمون الوصول إلى التطبيق من خلال اسم نطاق، يجب أن يشير DNS إلى عنوان IP الخاص بالخادم. إذا كان نطاقك example.com، فستحتاج عادةً إلى سجل A يشير إلى عنوان IPv4، ويمكنك إضافة سجل AAAA إذا كان الخادم متاحاً عبر IPv6 ومهيأ لاستقبال الاتصالات عليه.
مثال توضيحي لسجلات DNS:
النوع | الاسم | القيمة |
|---|---|---|
A | @ | عنوان IPv4 للخادم |
A | www | عنوان IPv4 للخادم |
AAAA | @ | عنوان IPv6 للخادم، إن توفر |
AAAA | www | عنوان IPv6 للخادم، إن توفر |
يمكن التحقق من حل النطاق باستخدام:
dig example.com
dig www.example.comأو:
nslookup example.comقد يستغرق انتشار تغييرات DNS بعض الوقت، وفق إعدادات TTL والتخزين المؤقت لدى مزودي الشبكات. لذلك، إذا لم يعمل النطاق مباشرةً بعد تعديل السجلات، فتحقق أولاً من أن القيم المنشورة صحيحة، ثم افحص نتائج الاستعلام من أكثر من محلل DNS عند الحاجة.
من المهم أن يتطابق اسم النطاق في DNS مع server_name داخل Nginx ومع ALLOWED_HOSTS داخل Django. وجود اختلاف بين هذه الإعدادات قد يؤدي إلى عدم وصول الطلبات إلى الموقع الصحيح أو رفضها داخل التطبيق.
11. إعداد HTTPS باستخدام Let's Encrypt
يُعد HTTPS ضرورياً لتشفير الاتصال بين المتصفح والخادم. فهو يساعد على حماية بيانات تسجيل الدخول والجلسات والمعلومات التي يرسلها المستخدمون، كما يسمح بتفعيل خصائص متعلقة بالمتصفح تتطلب سياقاً آمناً.
يمكن استخدام Let's Encrypt للحصول على شهادة TLS مجانية، وإدارة تجديدها تلقائياً باستخدام Certbot. قبل البدء، تأكد من أن النطاق يشير إلى الخادم وأن المنفذين 80 و443 متاحان من الإنترنت.
11.1 تثبيت Certbot
على Ubuntu، يمكن تثبيت Certbot وإضافة Nginx المناسبة من مستودعات النظام:
sudo apt update
sudo apt install -y certbot python3-certbot-nginxتحقق من وجود الأداة:
certbot --versionتختلف توصيات تثبيت Certbot أحياناً بين إصدارات Ubuntu وطرق التوزيع المختلفة. إذا كانت وثائق Let's Encrypt أو Certbot توصي بطريقة تثبيت محددة لإصدار نظامك، فاتبعها، ولا تثبت نسختين متعارضتين من الأداة.
11.2 طلب الشهادة
بعد ضبط Nginx وDNS، نفّذ:
sudo certbot --nginx \
-d example.com \
-d www.example.comاتبع التعليمات الظاهرة في الطرفية. قد يُطلب منك إدخال بريد إلكتروني والموافقة على الشروط، كما قد يُعرض عليك خيار إعادة توجيه HTTP إلى HTTPS.
إذا نجحت العملية، سيقوم Certbot بإعداد الشهادة وتعديل إعدادات Nginx وفقاً للطريقة التي يدعمها إصدار الأداة لديك.
تحقق من الإعداد:
sudo nginx -t
sudo systemctl reload nginxثم افتح:
https://example.comوتحقق من أن الشهادة صالحة وأن المتصفح لا يعرض تحذيرات متعلقة بالاتصال.
11.3 اختبار تجديد الشهادة
شهادات Let's Encrypt محدودة المدة، ولذلك يجب التأكد من أن آلية التجديد تعمل. اختبرها باستخدام:
sudo certbot renew --dry-runهذا الأمر يجرب عملية التجديد دون استبدال الشهادة الفعلية. إذا نجح الاختبار، فهذا مؤشر على أن آلية التجديد مهيأة بصورة صحيحة، لكن من المفيد التحقق أيضاً من المؤقت أو المهمة التي تشغّل Certbot دورياً على نظامك.
لا تفترض أن نجاح إصدار الشهادة مرة واحدة يعني أن التجديد سيعمل إلى الأبد. قد تتغير إعدادات DNS أو الجدار الناري أو ملفات Nginx، وقد تؤدي هذه التغييرات إلى فشل التجديد لاحقاً.
11.4 إعداد Django للعمل خلف HTTPS
عندما يكون Nginx هو الذي ينهي اتصال TLS، يتواصل Gunicorn مع Nginx عبر HTTP محلياً. لذلك قد يحتاج Django إلى معرفة أن الطلب الأصلي وصل عبر HTTPS، حتى يتعامل بصورة صحيحة مع الروابط الآمنة وعمليات إعادة التوجيه وبعض آليات الحماية.
في إعدادات Django:
SECURE_PROXY_SSL_HEADER = ( "HTTP_X_FORWARDED_PROTO", "https",)هذا الإعداد يفترض أن Nginx يرسل قيمة موثوقة للرأس X-Forwarded-Proto، وأن Gunicorn لا يستقبل اتصالات عامة يمكن للمستخدم التحكم فيها. لذلك من المهم أن يكون Gunicorn مرتبطاً بعنوان داخلي فقط، وأن يتولى Nginx ضبط هذا الرأس بنفسه.
يمكن أيضاً تفعيل إعدادات الأمان المناسبة:
CSRF_COOKIE_SECURE = TrueSESSION_COOKIE_SECURE = Trueوتحديد نطاقات CSRF المسموح بها:
CSRF_TRUSTED_ORIGINS = [ "https://example.com", "https://www.example.com",]إذا كان التطبيق يستقبل طلبات من نطاقات إضافية، فأضفها بصورة صريحة بعد التأكد من الحاجة إليها. لا تستخدم إعدادات واسعة تسمح لأي نطاق دون دراسة المخاطر.
أما SECURE_SSL_REDIRECT فيمكن استخدامه لإجبار التطبيق على HTTPS:
SECURE_SSL_REDIRECT = Trueلكن عندما تتولى Nginx عملية إعادة التوجيه بالكامل، يمكن أن يكون من الأفضل ضبطها هناك، أو تنسيق الإعدادين بعناية. تجنب إنشاء حلقة إعادة توجيه بسبب عدم تعرف Django على البروتوكول الأصلي.
12. تحسين إعدادات Nginx لتطبيق Django
الإعداد الأساسي السابق يكفي كنقطة انطلاق لكثير من المشاريع الصغيرة، لكنه ليس بالضرورة مثالاً نهائياً لكل تطبيق. يمكن تحسين إعدادات Nginx وفق حجم الملفات، وعدد الاتصالات، وطبيعة الطلبات، ومتطلبات التخزين المؤقت، وسياسات الأمان.
12.1 تقديم الملفات الثابتة بكفاءة
عندما يطلب المستخدم ملف CSS أو JavaScript، لا توجد حاجة غالباً إلى تشغيل Django لمعالجة هذا الطلب. يستطيع Nginx تقديم الملف مباشرةً من القرص، ما يقلل الحمل على Gunicorn ويوفر موارد التطبيق للطلبات الديناميكية.
يجب أن يتطابق المسار الموجود في إعداد Nginx مع STATIC_ROOT. إذا كان Django يجمع الملفات إلى:
STATIC_ROOT = BASE_DIR / "staticfiles"وكان BASE_DIR هو /srv/myproject، فالمسار الفعلي يصبح:
/srv/myproject/staticfiles/وهذا هو المسار الذي استخدمناه في إعداد Nginx.
يمكنك اختبار الملف الثابت مباشرةً بعد جمعه، والتأكد من أن Nginx يستطيع قراءته. إذا كانت الملفات تظهر محلياً لكنها لا تظهر في الإنتاج، فقد تكون المشكلة في collectstatic أو في المسارات أو في الصلاحيات أو في إعدادات التخزين التي يستخدمها Django.
12.2 التخزين المؤقت للملفات الثابتة
يمكن ضبط مدة تخزين مؤقت طويلة للملفات الثابتة التي تحتوي على أسماء تتغير عند تحديث محتواها، مثل الملفات ذات البصمة أو الـ Hash في الاسم. لكن لا ينبغي تطبيق سياسة طويلة بلا تمييز على جميع الملفات إذا كانت أسماء الملفات لا تتغير عند تحديث المحتوى.
إذا كانت ملفاتك تحمل أسماء ثابتة مثل app.css، فقد يستمر المتصفح في استخدام نسخة قديمة بعد نشر إصدار جديد. أما عند استخدام أسماء تحتوي على بصمة للمحتوى، فيمكن اعتماد مدة تخزين أطول لأن تغيير المحتوى ينتج عنه اسم مختلف.
مثال على سياسة مناسبة للملفات ذات الأسماء المميزة:
location /static/ {
alias /srv/myproject/staticfiles/;
access_log off;
expires 1y;
add_header Cache-Control "public, immutable";
}لا تستخدم immutable إلا عندما تكون ملفاتك بالفعل ذات أسماء تتغير عند تغير المحتوى. إذا كنت لا تستخدم بصمات للملفات، فاختر مدة تخزين أقصر أو أضف آلية لإبطال التخزين المؤقت عند النشر.
12.3 ضغط الاستجابات باستخدام Gzip
يمكن أن يقلل الضغط حجم بعض الاستجابات النصية، مثل HTML وCSS وJavaScript وJSON. يدعم Nginx Gzip، ويمكن تفعيله في إعدادات عامة أو في كتلة HTTP بحسب تنظيم ملفات النظام.
مثال:
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_types
text/plain
text/css
application/json
application/javascript
application/xml
image/svg+xml;ينبغي وضع الإعداد في المكان المناسب داخل ملفات Nginx، مثل كتلة http، وليس داخل أي موضع عشوائي. كما يجب تجنب ضغط أنواع الملفات التي تكون مضغوطة أصلاً، مثل معظم ملفات الصور والفيديو.
الضغط ليس بديلاً عن تحسين حجم الأصول نفسها. فتصغير ملفات JavaScript، وتحسين الصور، وتقليل عدد الطلبات، وتجنب تحميل المكتبات غير الضرورية، كلها خطوات قد يكون تأثيرها أكبر من تفعيل Gzip وحده.
12.4 تحديد حجم الطلبات والمهلات الزمنية
يمكن ضبط الحد الأقصى لحجم الطلبات:
client_max_body_size 20M;ويجب أن يتوافق هذا الحد مع قيود التطبيق، مثل إعدادات رفع الملفات، وحدود مزود التخزين، والموارد المتاحة على الخادم.
كما يمكن ضبط المهلات:
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;لكن رفع المهلات إلى قيم كبيرة ليس علاجاً عاماً للبطء. إذا كانت بعض الصفحات تحتاج إلى وقت طويل جداً، فقد يكون من الأفضل نقل العمليات الثقيلة إلى مهام خلفية باستخدام Celery أو نظام مهام آخر، بدلاً من إبقاء اتصال HTTP مفتوحاً لفترة طويلة.
12.5 حماية ملفات البيئة والملفات الداخلية
يجب ألا يتمكن المستخدم من تنزيل ملفات الإعدادات أو المستودع أو البيئة الافتراضية عبر الويب. في إعدادات Nginx، لا تجعل جذر الموقع يشير إلى مجلد المشروع كاملاً، بل استخدم التوجيهات الصريحة التي تحتاج إليها، كما فعلنا في إعداد static وmedia.
يمكن إضافة قواعد تمنع الوصول إلى الملفات المخفية وبعض الامتدادات الحساسة، مع الانتباه إلى أن هذه القواعد ليست بديلاً عن اختيار جذر مناسب وصلاحيات ملفات صحيحة.
مثال:
location ~ /\.(?!well-known) {
deny all;
}هذا المثال يمنع الوصول إلى كثير من الملفات والمجلدات المخفية، مع استثناء مسار well-known الذي قد تحتاج إليه بعض تحديات التحقق أو الخدمات. راجع توافقه مع إعداداتك قبل اعتماده.
13. ضبط Django للأمان في الإنتاج
الأمان ليس خطوة تنتهي بمجرد تركيب شهادة TLS. فالتطبيق يتعامل مع حسابات المستخدمين والبيانات والجلسات وعمليات رفع الملفات، وقد يحتوي على واجهات API أو لوحة إدارة أو تكاملات خارجية. لذلك ينبغي مراجعة إعدادات Django ومكونات النظام معاً.
13.1 تشغيل فحص النشر
يوفر Django أمراً مخصصاً لفحص بعض إعدادات الإنتاج:
python manage.py check --deployينبغي تشغيل هذا الأمر باستخدام إعدادات الإنتاج الفعلية، وليس إعدادات التطوير. وقد يعرض تحذيرات حول إعدادات الأمان، مثل HTTPS وملفات تعريف الارتباط والمفتاح السري وDEBUG.
تعامل مع نتائج الفحص بوصفها قائمة مراجعة، وليس شهادة بأن التطبيق آمن بالكامل. فالأمر لا يراجع كل منطق الأعمال، ولا يستطيع معرفة جميع المخاطر الناتجة عن المكتبات الخارجية أو البنية الشبكية أو الصلاحيات.
13.2 إعداد HSTS بحذر
يساعد HTTP Strict Transport Security المتصفح على تفضيل HTTPS للنطاق بعد استلام رأس HSTS صحيح. يمكن ضبطه في Django أو Nginx، لكن يجب التأكد من أن جميع النطاقات الفرعية المعنية تعمل عبر HTTPS قبل تفعيل الخيارات الأوسع.
مثال في Django:
SECURE_HSTS_SECONDS = 3600SECURE_HSTS_INCLUDE_SUBDOMAINS = FalseSECURE_HSTS_PRELOAD = Falseهذه قيمة قصيرة نسبياً كبداية للاختبار، وليست إعداداً نهائياً بالضرورة. بعد التأكد من استقرار HTTPS، يمكن التفكير في مدة أطول وفق سياسة النشر.
يجب التعامل بحذر شديد مع includeSubDomains وpreload، لأنهما قد يؤثران في النطاقات الفرعية الأخرى، وقد يصعب التراجع عن بعض آثار نشر النطاق في قوائم التحميل المسبق للمتصفحات.
13.3 حماية ملفات تعريف الارتباط
يمكن ضبط خصائص ملفات تعريف الارتباط الخاصة بالجلسة وCSRF:
SESSION_COOKIE_HTTPONLY = TrueSESSION_COOKIE_SECURE = TrueCSRF_COOKIE_SECURE = Trueيساعد HttpOnly على منع وصول JavaScript إلى ملف تعريف ارتباط الجلسة عبر الواجهات المعتادة للمتصفح. لكنه لا يمنع جميع أنواع الهجمات، ولا يلغي الحاجة إلى حماية التطبيق من XSS.
أما Secure فيجعل المتصفح يرسل ملف تعريف الارتباط عبر اتصالات HTTPS فقط. لذلك لا تفعّله في بيئة محلية غير آمنة دون فهم أثره على الاختبار، بينما يكون مناسباً عادةً للإنتاج الذي يعمل عبر HTTPS.
13.4 إدارة الأسرار وصلاحيات الملفات
يجب أن تكون كلمات المرور والمفاتيح خارج مستودع Git، وألا تظهر في سجلات التطبيق أو تقارير الأخطاء أو رسائل البريد الإلكتروني التشخيصية.
راجع صلاحيات المجلدات:
sudo find /srv/myproject -maxdepth 2 -type d -lsوتأكد من أن مستخدم Gunicorn يستطيع قراءة الكود والاعتماديات، وأنه يمتلك صلاحية الكتابة فقط في الأماكن التي يحتاج إليها، مثل مجلدات الوسائط أو الملفات المؤقتة إذا كان التطبيق يستخدمها.
لا تمنح مستخدم التطبيق صلاحيات واسعة مثل chmod -R 777 لمعالجة مشكلات الوصول. هذا النوع من الحلول قد يخفي المشكلة الأصلية لكنه يفتح المجال لتعديلات غير مصرح بها على الملفات.
14. إدارة ملفات Media وعمليات رفع الملفات
تختلف الملفات الثابتة عن ملفات Media في مصدرها وطريقة تحديثها. الملفات الثابتة تأتي عادةً مع التطبيق أو مكتبات الواجهة، بينما تنشأ ملفات Media نتيجة تفاعل المستخدمين مع التطبيق، مثل الصور الشخصية والمرفقات والمستندات.
إذا كان مشروعك يسمح برفع الملفات، فمن الضروري التفكير في ثلاثة أمور: أين تُخزن الملفات، ومن يستطيع قراءتها، وكيف يتم التحقق من نوعها وحجمها ومحتواها قبل قبولها.
14.1 إعداد مسارات Media
يمكن استخدام الإعدادات التالية في Django:
MEDIA_URL = "/media/"MEDIA_ROOT = BASE_DIR / "media"يجب التأكد من وجود المجلد ومن صلاحيات الكتابة عليه:
sudo mkdir -p /srv/myproject/media
sudo chown -R djangoapp:www-data /srv/myproject/media
sudo chmod 750 /srv/myproject/mediaهذا المثال يسمح للمستخدم المالك بالوصول إلى المجلد، ويسمح للمجموعة بالوصول وفق الصلاحيات المحددة. لكن اختيار الصلاحيات النهائي يجب أن يتوافق مع طريقة تشغيل Nginx وGunicorn واحتياجات التطبيق.
إذا كان Nginx يقدم الملفات من هذا المجلد، فيجب أن تكون لديه صلاحية اجتياز المجلدات وقراءة الملفات المطلوبة. قد يتطلب ذلك ضبط صلاحيات المجلدات الأب أيضاً، وليس فقط مجلد media.
14.2 إعداد نموذج رفع الملفات
مثال بسيط لنموذج Django:
from django.db import modelsclass Document(models.Model): title = models.CharField(max_length=200) file = models.FileField(upload_to="documents/") created_at = models.DateTimeField(auto_now_add=True)بعد إنشاء النموذج:
python manage.py makemigrations
python manage.py migrateيمكن للتطبيق الآن تخزين مسار الملف في قاعدة البيانات، بينما تُحفظ بيانات الملف نفسها في التخزين الذي تحدده.
لكن لا ينبغي الاعتماد على امتداد الملف وحده للتحقق من أمانه. يجب تحديد أنواع الملفات المقبولة، والتحقق من الحجم، واستخدام مكتبات مناسبة لفحص المحتوى عندما تتطلب طبيعة المشروع ذلك. وقد يكون من الأفضل تخزين الملفات التي يرفعها المستخدمون خارج جذر الويب، ثم تقديمها من خلال آلية تتحقق من الصلاحيات، خصوصاً إذا كانت الملفات خاصة.
14.3 تقديم الملفات الخاصة بطريقة آمنة
إذا كانت الملفات عامة، فقد يكون تقديمها مباشرةً من Nginx مناسباً. أما إذا كانت الملفات خاصة، فلا ينبغي جعلها متاحة لأي شخص يعرف رابطها، ما لم يكن هذا هو السلوك المقصود.
يمكن استخدام Django للتحقق من صلاحية المستخدم قبل السماح بتنزيل الملف، أو استخدام تخزين خارجي يدعم الروابط المؤقتة الموقعة. ويمكن أيضاً إعداد Nginx باستخدام آلية مثل X-Accel-Redirect لتقديم الملفات بعد أن يتحقق التطبيق من صلاحية الوصول.
الفكرة الأساسية هي الفصل بين عملية اتخاذ قرار السماح بالوصول وبين عملية نقل الملف. فقد يكون Django مسؤولاً عن التحقق، بينما يتولى Nginx إرسال الملف بكفاءة بعد صدور قرار السماح.
15. تحسين أداء Gunicorn وتحديد عدد العمال
يؤثر إعداد Gunicorn في عدد الطلبات التي يستطيع التطبيق معالجتها بالتوازي، وفي استهلاك الذاكرة والمعالج. لكن زيادة عدد العمال ليست دائماً الحل الأفضل؛ فقد يؤدي ذلك إلى استهلاك ذاكرة أكبر، أو زيادة الضغط على قاعدة البيانات، أو تقليل الاستقرار إذا كان الخادم محدود الموارد.
15.1 تقدير عدد Workers
يمكن البدء بعدد قليل من العمال ثم إجراء اختبارات أداء ومراقبة الاستهلاك. من الشائع استخدام قاعدة تقريبية مرتبطة بعدد أنوية المعالج، لكن يجب التعامل معها كنقطة بداية فقط، وليس كقانون ثابت.
مثلاً، إذا كان الخادم يحتوي على نواتين، فقد تبدأ بثلاثة Workers، ثم تقيس النتائج. وإذا كان كل Worker يستهلك ذاكرة كبيرة بسبب تحميل نماذج أو بيانات كثيرة، فقد تحتاج إلى عدد أقل.
يمكن معرفة عدد الأنوية باستخدام:
nprocومراقبة الذاكرة باستخدام:
free -hأما قياس أداء التطبيق فيحتاج إلى أدوات اختبار مناسبة، مثل wrk أو ab أو أدوات اختبار تحميل أخرى، مع الحرص على عدم إجراء اختبارات ضغط على خادم إنتاجي دون خطة واضحة.
15.2 استخدام Threads
يدعم Gunicorn إعداد عدد من Threads مع بعض أنواع العمال. يمكن تجربة ذلك في التطبيقات التي تنتظر عمليات إدخال وإخراج، لكن يجب قياس أثره على التطبيق والمكتبات المستخدمة.
مثال:
gunicorn config.wsgi:application \
--workers 3 \
--threads 2 \
--bind 127.0.0.1:8000لا تفترض أن مضاعفة عدد Threads تعني مضاعفة الأداء. فالنتيجة تعتمد على طبيعة التطبيق، وطريقة استخدام قاعدة البيانات، والعمليات التي تنتظر خدمات خارجية، وقيود التنفيذ في Python.
15.3 إعادة تدوير Workers
يمكن ضبط Gunicorn لإعادة تدوير العمال بعد معالجة عدد محدد من الطلبات، وهو خيار قد يساعد في بعض التطبيقات التي يتزايد استهلاك الذاكرة فيها بمرور الوقت.
مثال:
gunicorn config.wsgi:application \
--workers 3 \
--max-requests 1000 \
--max-requests-jitter 100 \
--bind 127.0.0.1:8000max-requests يحدد عدد الطلبات قبل إعادة تشغيل Worker، بينما يضيف max-requests-jitter اختلافاً عشوائياً محدوداً لتقليل احتمال إعادة تشغيل جميع العمال في الوقت نفسه.
لكن إعادة تدوير العمال ليست علاجاً لتسرب الذاكرة الحقيقي. إذا كان التطبيق يستهلك الذاكرة بصورة متزايدة بسبب خطأ في الكود، فمن الأفضل تحديد السبب وإصلاحه.
15.4 المهام الطويلة والعمليات الخلفية
إذا كان تطبيقك ينفذ عمليات ثقيلة مثل إنشاء تقارير كبيرة، أو معالجة صور، أو استيراد ملفات ضخمة، أو استدعاء خدمات خارجية بطيئة، فقد لا يكون تنفيذها داخل طلب HTTP مناسباً.
يمكن نقل هذه العمليات إلى نظام مهام خلفية مثل Celery مع Redis أو RabbitMQ. عندها يستقبل Django الطلب، ويتحقق من البيانات، ويضع المهمة في قائمة انتظار، ثم تعالجها Worker مستقلة.
هذا التصميم يساعد على تقليل زمن استجابة واجهة المستخدم، وفصل العمليات الطويلة عن خادم الويب، وإدارة إعادة المحاولة والمهام المتأخرة. لكنه يضيف مكونات أخرى تحتاج إلى مراقبة وإدارة، لذلك يجب إدخاله عندما تكون هناك حاجة فعلية إليه.
16. مراقبة التطبيق والسجلات وتشخيص الأخطاء
النشر لا ينتهي بمجرد ظهور الصفحة الرئيسية في المتصفح. قد يعمل التطبيق بصورة طبيعية في البداية ثم تظهر أخطاء بعد ارتفاع عدد المستخدمين أو بعد تحديث قاعدة البيانات أو عند نفاد مساحة التخزين. لذلك ينبغي إعداد مراقبة بسيطة منذ اليوم الأول.
16.1 سجلات Gunicorn
بما أننا أرسلنا سجلات Gunicorn إلى مخرجات الخدمة، يمكن مراجعتها عبر:
sudo journalctl -u gunicorn -n 100 --no-pagerلعرض الأخطاء فقط بصورة عملية، يمكن استخدام:
sudo journalctl -u gunicorn -p errقد تحتوي السجلات على رسائل استثناءات Python، أو أخطاء تحميل التطبيق، أو مشكلات اتصال بقاعدة البيانات. لكن من المهم ألا تتضمن سجلاتك بيانات شخصية أو كلمات مرور أو رموز مصادقة.
16.2 سجلات Nginx
غالباً ما توجد سجلات Nginx في:
/var/log/nginx/access.log
/var/log/nginx/error.logيمكن متابعة سجل الأخطاء مباشرةً:
sudo tail -f /var/log/nginx/error.logأما سجل الوصول فيساعد على معرفة الطلبات التي وصلت إلى الخادم، والرموز التي أعادها، وحجم الاستجابة وزمن المعالجة إذا كانت إعدادات التسجيل تتضمن هذه المعلومات.
عندما يظهر 502 Bad Gateway، افحص سجل Nginx أولاً، ثم تحقق من أن Gunicorn يعمل وأن المنفذ المحلي يستجيب. أما إذا ظهرت 500 من التطبيق، فراجع سجلات Gunicorn وDjango.
16.3 فحص حالة الخدمات
يمكن استخدام:
sudo systemctl status nginx
sudo systemctl status gunicorn
sudo systemctl status postgresqlوتحقق من استخدام الموارد:
topأو:
htopإذا لم تكن htop مثبتة:
sudo apt install -y htopراقب استهلاك الذاكرة والمعالج، وعدد العمليات، وحالة الخدمات. ومن المفيد أيضاً مراقبة مساحة القرص:
df -hفامتلاء القرص قد يؤدي إلى فشل تسجيل السجلات، أو تعطل قاعدة البيانات، أو عدم قدرة التطبيق على حفظ الملفات المؤقتة.
16.4 إعداد Logging داخل Django
يمكن ضبط نظام تسجيل داخل المشروع لتجميع الأخطاء والمعلومات المهمة. مثال مبسط:
LOGGING = { "version": 1, "disable_existing_loggers": False, "handlers": { "console": { "class": "logging.StreamHandler", }, }, "root": { "handlers": ["console"], "level": "INFO", },}هذا الإعداد يرسل السجلات إلى مخرجات العملية، والتي يمكن لـ systemd جمعها. في التطبيقات الكبيرة، قد تحتاج إلى تسجيل أكثر تفصيلاً، مع تحديد مستويات التسجيل لكل وحدة، وإضافة معرف للطلب، واستخدام خدمة مركزية لتجميع السجلات.
احرص على عدم تسجيل كلمات المرور أو محتوى الطلبات الحساسة بالكامل. تسجيل الخطأ مفيد، لكن تسجيل البيانات الخاصة دون ضرورة يمكن أن يخلق مشكلة أمنية وخصوصية.
17. نشر تحديثات Django دون فوضى
بعد نشر التطبيق لأول مرة، ستحتاج غالباً إلى تحديث الكود وإضافة ميزات وإصلاح أخطاء. ومن الأفضل وضع خطوات نشر واضحة، بدلاً من تعديل الملفات يدوياً في كل مرة.
17.1 تحديث الكود باستخدام Git
يمكنك الانتقال إلى مجلد المشروع:
cd /srv/myproject
git status
git pull origin mainاستخدم الفرع الذي تعتمد عليه عملية النشر، وليس بالضرورة main. من الأفضل التأكد من أن المستودع يحتوي على الإصدار الذي تريد نشره، وأن التغييرات قد اجتازت الاختبارات.
في المشاريع الأكبر، قد يكون من الأفضل استخدام إصدارات أو Tags محددة بدلاً من سحب أحدث التغييرات مباشرةً من فرع متحرك.
17.2 تحديث الاعتماديات
إذا تغير ملف requirements.txt:
source /srv/myproject/venv/bin/activate
pip install -r /srv/myproject/requirements.txtمن الأفضل تنفيذ التحديثات بصورة قابلة للتكرار، وتجنب ترقية جميع المكتبات دون مراجعة. فقد تؤدي ترقية غير متوقعة إلى تغيير سلوك التطبيق أو كسر توافق بعض المكتبات.
17.3 تنفيذ migrations وجمع الملفات
بعد تحديث الكود، تحقق من المشروع:
python manage.py check
python manage.py migrate
python manage.py collectstatic --noinputيجب تنفيذ هذه الأوامر باستخدام إعدادات الإنتاج وبيئة Python الصحيحة. وقبل migrations الحساسة، خذ نسخة احتياطية واختبر التغييرات في بيئة شبيهة بالإنتاج.
في بعض عمليات النشر، من المهم تنسيق إصدار التطبيق مع مخطط قاعدة البيانات حتى لا يتوقف الإصدار الحالي عن العمل أثناء تحديث الجداول. يمكن اعتماد استراتيجية توسعة ثم إزالة، حيث تُضاف التغييرات بصورة متوافقة أولاً، ثم يُحدّث التطبيق، ثم تُزال الأعمدة أو السلوكيات القديمة بعد التأكد من عدم الحاجة إليها.
17.4 إعادة تشغيل Gunicorn
بعد اكتمال التحديث:
sudo systemctl restart gunicornثم تحقق من الحالة والسجلات:
sudo systemctl status gunicorn
sudo journalctl -u gunicorn -n 50 --no-pagerاختبر التطبيق عبر النطاق، وتحقق من الصفحات الرئيسية، وتسجيل الدخول، والواجهات البرمجية، وعمليات رفع الملفات أو أي وظيفة أساسية يعتمد عليها المستخدمون.
إذا كان التطبيق مهماً ويستقبل عدداً كبيراً من الطلبات، فقد تحتاج إلى استراتيجية نشر أكثر تقدماً تقلل التوقف، مثل Blue-Green Deployment أو Rolling Deployment. لكن لا تحتاج إلى إدخال هذه التعقيدات قبل أن تتطلبها طبيعة الخدمة.
18. النسخ الاحتياطي واستعادة البيانات
قد يكون التطبيق مستقراً، لكن خطأً في قاعدة البيانات أو حذفاً غير مقصود أو مشكلة في الخادم يمكن أن يؤدي إلى فقدان بيانات مهمة. لذلك ينبغي التفكير في النسخ الاحتياطي منذ البداية، وليس بعد وقوع المشكلة.
18.1 النسخ الاحتياطي لقاعدة PostgreSQL
يمكن إنشاء نسخة احتياطية باستخدام pg_dump:
pg_dump \
-h 127.0.0.1 \
-U myproject_user \
-d myproject \
-F c \
-f /srv/backups/myproject.dumpأنشئ مجلد النسخ الاحتياطي أولاً، واضبط صلاحياته. كما يجب إدارة كلمة المرور بطريقة آمنة، وعدم وضعها مباشرةً في سجل الأوامر أو داخل ملفات عامة.
يمكن استعادة نسخة بهذا الشكل:
pg_restore \
-h 127.0.0.1 \
-U myproject_user \
-d myproject \
--clean \
/srv/backups/myproject.dumpهذا مثال توضيحي، ويجب التعامل بحذر مع --clean لأنه قد يحذف كائنات موجودة قبل الاستعادة. لا تستخدمه على قاعدة إنتاجية نشطة دون فهم أثره والتحقق من خطة الاستعادة.
18.2 النسخ الاحتياطي للملفات
إذا كان التطبيق يستخدم MEDIA_ROOT محلياً، فيجب أن تتضمن خطة النسخ الاحتياطي ملفات الوسائط المهمة أيضاً. نسخة قاعدة البيانات وحدها لا تكفي لاستعادة الصور والمستندات المرفوعة إذا كانت مخزنة خارج قاعدة البيانات.
يمكن استخدام أدوات مثل rsync أو أنظمة نسخ احتياطي متخصصة، مع الاحتفاظ بنسخ خارج الخادم نفسه. فإذا تعطل القرص أو فُقد الخادم بالكامل، فقد تكون النسخة الموجودة عليه غير قابلة للاسترجاع.
18.3 اختبار الاستعادة
وجود ملف نسخة احتياطية لا يعني بالضرورة أنك تستطيع استخدامه. يجب اختبار الاستعادة دورياً في بيئة منفصلة، والتحقق من سلامة الجداول والملفات، وأن التطبيق يستطيع العمل باستخدام البيانات المستعادة.
ومن المفيد تحديد أهداف واضحة:
RPO: مقدار البيانات التي يمكن قبول فقدانها زمنياً.
RTO: الوقت المقبول لإعادة الخدمة بعد وقوع حادث.
إذا كان التطبيق يستطيع تحمل فقدان يوم من البيانات، فقد تختلف خطة النسخ الاحتياطي عن تطبيق مالي أو خدمة تعتمد على تحديثات لحظية. يجب أن تعكس الخطة قيمة البيانات وطبيعة العمل.
19. إعداد نشر آلي باستخدام GitHub Actions
عندما يتكرر نشر التحديثات، يمكن أتمتة بعض الخطوات باستخدام CI/CD. فبدلاً من تنفيذ الاختبارات والتحديثات يدوياً في كل مرة، يمكن بناء مسار ينفذ الاختبارات عند رفع التغييرات، ثم ينشر الإصدار عند تحقق شروط معينة.
يمكن أن يتضمن مسار النشر:
تثبيت إصدار Python المطلوب.
تثبيت الاعتماديات.
تشغيل الاختبارات.
فحص الكود وأدوات الجودة.
تجهيز إصدار قابل للنشر.
الاتصال بالخادم أو منصة النشر.
تحديث التطبيق وتشغيل migrations عند الحاجة.
إعادة تشغيل Gunicorn.
تنفيذ اختبار تحقق بعد النشر.
مثال أولي لاختبارات المشروع:
name: Django CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Django checks
run: |
python manage.py check
- name: Run tests
run: |
python manage.py testهذا المثال يوضح مرحلة CI فقط. لا يتضمن إعداد قاعدة بيانات اختبار، أو أسرار الاتصال، أو خطوة النشر الفعلية. إذا كان مشروعك يحتاج إلى PostgreSQL أثناء الاختبارات، فيجب إضافة خدمة قاعدة بيانات أو استخدام إعداد اختبار مناسب.
عند إضافة النشر، لا تضع كلمة مرور SSH أو المفتاح الخاص داخل ملف YAML. استخدم مخازن الأسرار في منصة CI، وامنح مفتاح النشر صلاحيات محدودة. ومن الأفضل أن يكون المستخدم المخصص للنشر مختلفاً عن مستخدم إدارة النظام، وأن يكون مسموحاً له بتنفيذ الإجراءات المطلوبة فقط.
لا تنفذ migrations تلقائياً في كل بيئة أو عند كل حدث دون مراجعة. بعض تغييرات قاعدة البيانات تحتاج إلى خطوات إضافية أو توقيت محدد، وقد تسبب توقفاً إذا نفذت في لحظة غير مناسبة.
20. أخطاء شائعة عند نشر Django باستخدام Nginx وGunicorn
حتى عند اتباع الخطوات السابقة، قد تواجه بعض المشكلات. المهم هو تشخيصها وفق الطبقة التي تحدث فيها، وعدم تغيير عدة إعدادات في الوقت نفسه دون معرفة السبب.
20.1 خطأ 502 Bad Gateway
يظهر هذا الخطأ غالباً عندما لا يستطيع Nginx الاتصال بـ Gunicorn، أو عندما يتلقى استجابة غير صالحة من الخادم الخلفي.
ابدأ بالتحقق من الخدمة:
sudo systemctl status gunicornثم اختبر المنفذ:
curl -I http://127.0.0.1:8000إذا لم يستجب المنفذ، راجع سجلات Gunicorn:
sudo journalctl -u gunicorn -n 100 --no-pagerتحقق من أن proxy_pass يشير إلى العنوان والمنفذ الصحيحين. وإذا كنت تستخدم Unix Socket، فتأكد من تطابق مساره وصلاحياته بين Nginx وGunicorn.
20.2 خطأ DisallowedHost
إذا ظهر خطأ متعلق بـ DisallowedHost، فتحقق من قيمة ALLOWED_HOSTS:
ALLOWED_HOSTS = [ "example.com", "www.example.com",]تأكد من أن النطاق الذي يصل به المستخدم موجود في القائمة، وأنك لم تخلط بين اسم المضيف وعنوان URL الكامل. فلا تضع https:// داخل ALLOWED_HOSTS، لأن المطلوب هو أسماء المضيفين وليس البروتوكول.
20.3 ظهور CSS وJavaScript دون تنسيق
إذا كانت الصفحة تظهر دون تنسيق، فغالباً توجد مشكلة في الملفات الثابتة. تحقق من:
قيمة
STATIC_URL.قيمة
STATIC_ROOT.نجاح
collectstatic.المسار المحدد في
alias.صلاحيات القراءة على المجلدات والملفات.
وجود أخطاء 404 في أدوات المطور بالمتصفح.
عدم وجود مشكلة في إعدادات التخزين المخصصة للملفات الثابتة.
يمكن اختبار الملف الثابت مباشرةً من الخادم:
ls -lah /srv/myproject/staticfiles/ثم جرّب طلب أحد الملفات من خلال Nginx. إذا كان الملف موجوداً لكن الاستجابة 404، فراجع تطابق المسار وإعدادات الموقع.
20.4 مشكلة الملفات المرفوعة
إذا نجحت عملية رفع الملف، لكن لم يستطع المستخدم فتحه، فقد تكون المشكلة في صلاحيات MEDIA_ROOT أو في إعداد location /media/ أو في مكان التخزين الفعلي.
تحقق من أن الملف موجود:
ls -lah /srv/myproject/media/ثم تأكد من أن Nginx يستطيع الوصول إلى المسار. وإذا كانت الملفات خاصة، فلا تعالج المشكلة بجعل مجلد الوسائط عاماً دون مراجعة متطلبات الخصوصية والصلاحيات.
20.5 حلقة إعادة التوجيه بين HTTP وHTTPS
قد تظهر حلقة إعادة توجيه إذا كان Nginx يستقبل HTTPS، ثم يمرر الطلب إلى Gunicorn عبر HTTP، بينما يعتقد Django أن الطلب الأصلي غير آمن، فيعيد توجيهه إلى HTTPS مرة أخرى.
تحقق من أن Nginx يرسل:
proxy_set_header X-Forwarded-Proto $scheme;وتأكد من ضبط:
SECURE_PROXY_SSL_HEADER = ( "HTTP_X_FORWARDED_PROTO", "https",)إذا كانت Nginx هي نقطة الدخول الموثوقة الوحيدة، وكان Gunicorn غير متاح للعامة، يمكن أن يكون هذا الإعداد مناسباً. أما إذا كانت هناك طبقة Proxy أخرى أمام Nginx، فيجب دراسة سلسلة الرؤوس وطريقة التحقق منها.
20.6 خطأ Permission denied
قد يظهر هذا الخطأ عندما لا يستطيع Gunicorn قراءة ملفات المشروع أو عندما لا يستطيع Nginx الوصول إلى الملفات الثابتة أو الوسائط.
تحقق من صلاحيات المجلدات الأب أيضاً:
namei -l /srv/myproject/staticfilesهذا الأمر يساعد على معرفة صلاحيات كل عنصر في المسار. تذكر أن امتلاك صلاحية قراءة الملف وحده لا يكفي، إذ يحتاج المستخدم أيضاً إلى صلاحية اجتياز المجلدات التي تحتوي عليه.
تجنب استخدام chmod 777 كحل سريع. حدّد المستخدم الذي يحتاج إلى الوصول، ثم امنحه أقل قدر من الصلاحيات اللازمة.
20.7 خطأ قاعدة البيانات
قد يفشل Django في بدء التشغيل إذا كانت قاعدة البيانات غير متاحة أو كانت بيانات الاتصال خاطئة. افحص حالة الخدمة:
sudo systemctl status postgresqlواختبر الاتصال باستخدام عميل PostgreSQL. إذا كانت قاعدة البيانات خارج الخادم، فتحقق من قواعد الشبكة، وإعدادات المصادقة، واسم المضيف، والمنفذ، وشهادة TLS إذا كان الاتصال يتطلبها.
من الأفضل أن تكون رسائل الخطأ مفيدة بما يكفي للتشخيص، دون تضمين كلمات المرور أو معلومات حساسة في السجلات.
21. قائمة مراجعة نهائية قبل إطلاق التطبيق
قبل الإعلان عن إطلاق مشروع Django، من المفيد إجراء مراجعة منظمة لجميع مكونات البنية. قد يعمل الموقع من جهازك، لكنه يفشل عند استخدام نطاق مختلف أو عند رفع ملف كبير أو عند إعادة تشغيل الخادم. تساعد قائمة المراجعة على اكتشاف هذه المشكلات قبل أن يواجهها المستخدمون.
قائمة تحقق النشر
0 / 18 مكتمل
تحديث نظام Ubuntu وتثبيت الحزم الضرورية
إنشاء مستخدم Linux مخصص للتطبيق
تثبيت Python وإنشاء البيئة الافتراضية
تثبيت الاعتماديات وإضافة Gunicorn
ضبط متغيرات البيئة والمفتاح السري
ضبط اتصال قاعدة البيانات وتشغيل migrations
إنشاء مستخدم الإدارة عند الحاجة
تشغيل collectstatic والتحقق من الملفات
اختبار Gunicorn محلياً
إنشاء خدمة systemd وتفعيلها
إعداد Nginx واختبار الإعداد
ضبط DNS واسم النطاق
إصدار شهادة HTTPS واختبار التجديد
ضبط إعدادات Django الأمنية
اختبار تسجيل الدخول والواجهات والملفات
مراجعة السجلات وصلاحيات الملفات
إعداد النسخ الاحتياطي واختبار الاستعادة
تجهيز خطة تحديث وتراجع عن الإصدارات
إعادة تعيين نسخ القائمة
22. مثال عملي لتسلسل نشر المشروع بالكامل
لنجمع الخطوات الأساسية في تسلسل عملي يوضح كيف يمكن الانتقال من مشروع موجود في مستودع Git إلى تطبيق يعمل عبر HTTPS. هذا المثال يفترض أنك أنشأت المستخدم والمجلدات، وضبطت DNS، وأن إعدادات Django وملف البيئة جاهزان.
# 1. الدخول إلى مستخدم التطبيق
sudo -iu djangoapp
cd /srv/myproject
# 2. تفعيل البيئة الافتراضية
source venv/bin/activate
# 3. تثبيت الاعتماديات
pip install -r requirements.txt
pip install gunicorn
# 4. فحص إعدادات Django
python manage.py check
python manage.py check --deploy
# 5. تطبيق migrations
python manage.py migrate
# 6. جمع الملفات الثابتة
python manage.py collectstatic --noinput
# 7. الخروج إلى المستخدم الإداري
exit
# 8. تشغيل خدمة Gunicorn
sudo systemctl enable --now gunicorn
sudo systemctl status gunicorn
# 9. اختبار إعداد Nginx
sudo nginx -t
# 10. إعادة تحميل Nginx
sudo systemctl reload nginx
# 11. إصدار الشهادة إذا لم تكن موجودة
sudo certbot --nginx \
-d example.com \
-d www.example.com
# 12. اختبار تجديد الشهادة
sudo certbot renew --dry-run
هذا التسلسل ليس أمراً واحداً تنفذه دون توقف؛ بل مجموعة خطوات يجب التحقق من نجاح كل مرحلة منها قبل الانتقال إلى التالية. مثلاً، إذا فشل manage.py check، فلا توجد فائدة من إعادة تشغيل Gunicorn قبل إصلاح المشكلة. وإذا فشل nginx -t، فلا ينبغي إعادة تحميل الخدمة حتى يصح الإعداد.
من المفيد أيضاً الاحتفاظ بتوثيق قصير يتضمن اسم خدمة systemd، ومسارات المشروع، وأماكن السجلات، وملفات الإعداد، وطريقة إعادة التشغيل. عندما تعود إلى المشروع بعد عدة أسابيع أو يحتاج شخص آخر إلى صيانته، سيختصر هذا التوثيق وقتاً كبيراً.
23. هل تحتاج إلى Docker بدلاً من التثبيت المباشر؟
التثبيت المباشر لـ Django وGunicorn وNginx على Ubuntu مناسب للتطبيقات الصغيرة والمتوسطة التي تريد إدارتها على خادم واحد. لكنه ليس الخيار الوحيد. يمكن استخدام Docker لتغليف التطبيق واعتمادياته، وتشغيله في حاوية مستقلة، وإدارة البيئة عبر Docker Compose.
في التثبيت المباشر، يعتمد التطبيق على نظام التشغيل الموجود على الخادم، وعلى إعدادات البيئة الافتراضية، وعلى ملفات الخدمة التي أنشأتها. أما Docker فيوفر طبقة عزل إضافية ويساعد على توحيد بيئة التشغيل بين التطوير والاختبار والإنتاج.
الجانب | التثبيت المباشر | Docker |
|---|---|---|
الإعداد الأولي | إعداد Linux والخدمات يدوياً | إعداد Docker وملفات الحاويات |
عزل الاعتماديات | باستخدام venv | باستخدام صور وحاويات |
تحديث التطبيق | Git وتحديث البيئة وإعادة التشغيل | بناء صورة جديدة وإعادة إنشاء الحاوية |
إدارة الخدمات | systemd وNginx | Docker Compose أو منصة حاويات |
سهولة نقل البيئة | تعتمد على توثيق الخادم | أسهل نسبياً عند ضبط الصور |
تعقيد التشغيل | أقل في المشاريع البسيطة | يحتاج فهماً للحاويات والشبكات والتخزين |
ليس Docker ضماناً للأمان أو الأداء الأفضل بصورة تلقائية. فالحاويات تحتاج أيضاً إلى إدارة أسرار وصلاحيات وشبكات وتحديثات للصور ونسخ احتياطية للبيانات الدائمة. كما أن قاعدة البيانات لا ينبغي أن تُعامل كملف مؤقت داخل حاوية يمكن حذفها دون خطة تخزين.
إذا كنت تتعلم نشر Django لأول مرة، فإن فهم إعداد Nginx وGunicorn وsystemd مباشرةً يمنحك أساساً جيداً لفهم ما يحدث داخل الحاويات أيضاً. وبعد ذلك يمكنك نقل المشروع إلى Docker Compose عندما تحتاج إلى بيئة قابلة لإعادة البناء أو عندما تبدأ في تشغيل خدمات متعددة.
24. التوسع عندما يزداد عدد المستخدمين
عندما يبدأ التطبيق باستقبال عدد أكبر من الطلبات، قد تظهر اختناقات في المعالج أو الذاكرة أو قاعدة البيانات أو الشبكة. لا ينبغي افتراض أن إضافة Workers أو زيادة حجم الخادم ستعالج جميع المشكلات؛ فالأفضل قياس الأداء وتحديد الجزء الذي يستهلك الوقت والموارد.
24.1 تحسين استعلامات قاعدة البيانات
تستهلك الاستعلامات غير الفعالة وقتاً وموارد قد تؤثر في جميع الطلبات. في Django، يمكن أن تساعد أدوات ORM مثل select_related() وprefetch_related() في تقليل عدد الاستعلامات في بعض الحالات.
مثال:
posts = Post.objects.select_related( "author").prefetch_related( "categories")إذا كانت الصفحة تعرض قائمة مقالات مع أسماء المؤلفين والتصنيفات، فقد يؤدي تحميل العلاقات بطريقة مناسبة إلى تقليل عدد مرات الاتصال بقاعدة البيانات.
لكن لا تستخدم هذه الأدوات بصورة عشوائية. فتحميل علاقات كثيرة لا تحتاج إليها الصفحة قد يزيد استهلاك الذاكرة. استخدم أدوات قياس الاستعلامات، وراجع خطة التنفيذ والفهارس، ثم اختبر التحسينات على بيانات قريبة من حجم الإنتاج.
24.2 إضافة Cache
يمكن أن يساعد التخزين المؤقت في تقليل عمليات الحساب والاستعلامات المتكررة، خصوصاً في الصفحات التي تتغير بياناتها ببطء نسبياً.
يدعم Django عدة أنظمة Cache، ويمكن استخدام Redis أو Memcached بحسب طبيعة التطبيق والبنية المتاحة.
مثال مبسط:
from django.views.decorators.cache import cache_page@cache_page(60 * 5)def public_page(request): ...هذا المثال يخزن استجابة الصفحة لمدة خمس دقائق. لكنه ليس مناسباً لكل الصفحات، خصوصاً الصفحات التي تعرض معلومات خاصة بالمستخدم أو تتغير بسرعة أو تحتوي على بيانات تعتمد على الجلسة.
قبل تفعيل التخزين المؤقت، حدد ما الذي يمكن تخزينه، ولمن تكون الاستجابة صالحة، ومتى يجب إبطال النسخة المخزنة. فقد يؤدي Cache غير مضبوط إلى عرض معلومات قديمة أو بيانات مستخدم لمستخدم آخر.
24.3 فصل قاعدة البيانات عن خادم الويب
إذا أصبح الخادم مشغولاً بتشغيل Django وقاعدة البيانات معاً، فقد يساعد نقل قاعدة البيانات إلى خادم منفصل أو خدمة مُدارة. هذا الفصل يسمح بتخصيص موارد مختلفة لكل مكون، لكنه يضيف اعتماداً على الشبكة ويحتاج إلى ضبط آمن للاتصالات.
يجب مراعاة زمن الاتصال، والنسخ الاحتياطية، والمراقبة، والتشفير أثناء النقل، وسياسات الوصول. كما ينبغي تحديد ما إذا كان التطبيق يستطيع التعامل مع انقطاع مؤقت في الاتصال بقاعدة البيانات.
24.4 استخدام Load Balancer
عندما تحتاج إلى تشغيل عدة نسخ من التطبيق، يمكن وضع Load Balancer أمامها لتوزيع الطلبات. يمكن أن تكون هناك عدة خوادم Gunicorn على أجهزة مختلفة، بينما يتولى موازن الحمل توجيه الاتصالات إليها.
لكن تشغيل عدة نسخ يطرح أسئلة إضافية: أين تُخزن الجلسات؟ كيف تتشارك الملفات؟ كيف تتم إدارة المهام الخلفية؟ وكيف تُنفذ migrations دون تعارض؟ يجب حل هذه الأسئلة قبل زيادة عدد الخوادم، لأن التوسع الأفقي لا يقتصر على تكرار عملية Gunicorn.
25. نصائح عملية للحفاظ على استقرار التطبيق
من السهل التركيز على أوامر التثبيت وإعداد الملفات، لكن الاستقرار على المدى الطويل يعتمد على عادات تشغيلية بسيطة ومنتظمة. ومن المفيد تبني مجموعة من الممارسات منذ البداية.
أولاً، لا تحدّث جميع مكونات الخادم في الوقت نفسه دون خطة اختبار. من الأفضل فصل تحديثات نظام التشغيل عن تحديثات التطبيق عن تحديثات قاعدة البيانات، خصوصاً عندما يكون المشروع يعتمد على مكتبات قديمة أو إصدارات محددة من Python.
ثانياً، احتفظ بإصدار معروف بأنه يعمل، واجعل التراجع إليه ممكناً. فإذا أدى إصدار جديد إلى خطأ في تسجيل الدخول أو إلى مشكلة في استعلامات قاعدة البيانات، ينبغي أن تكون لديك طريقة واضحة للعودة إلى الإصدار السابق، مع مراعاة توافق مخطط البيانات.
ثالثاً، راقب المؤشرات الأساسية باستمرار. لا تنتظر أن يبلغك المستخدم بأن الموقع بطيء. تابع زمن الاستجابة، وعدد الأخطاء، واستهلاك الذاكرة، ومساحة القرص، وحالة قاعدة البيانات، ونتائج فحوصات الصحة.
رابعاً، لا تجعل عملية النشر تعتمد على ذاكرة شخص واحد. وثّق طريقة إعداد البيئة، وأسماء الخدمات، ومسارات الملفات، ومتغيرات البيئة المطلوبة دون تضمين قيم الأسرار، وأوامر التشغيل والتراجع.
خامساً، اختبر عمليات الاستعادة فعلياً. فالنسخ الاحتياطي الذي لم يُختبر لا يوفر ضماناً عملياً بأنك ستتمكن من استعادة التطبيق بعد حادث.
وأخيراً، تعامل مع الأمن باعتباره عملية مستمرة. حدّث المكتبات والنظام، وراجع الصلاحيات، وراقب السجلات، وأزل الخدمات والمنافذ غير الضرورية، واستخدم المصادقة متعددة العوامل حيث تتوفر، ولا تمنح صلاحيات إدارية لمجرد أن ذلك يجعل الإعداد أسرع.
الخاتمة
يمثل نشر Django باستخدام Nginx وGunicorn خطوة مهمة للانتقال من بيئة التطوير إلى بيئة إنتاج حقيقية. في هذه البنية، يتولى Django منطق التطبيق، ويشغله Gunicorn عبر واجهة WSGI، بينما يستقبل Nginx الاتصالات العامة ويمرر الطلبات الديناميكية ويقدم الملفات الثابتة. ويساعد systemd على إدارة العملية وإبقائها قيد التشغيل، بينما توفر قاعدة البيانات والتخزين البنية اللازمة لحفظ بيانات التطبيق وملفاته.
تعلم هذه المكونات لا يقتصر على معرفة الأوامر التي يجب تنفيذها. الأهم هو فهم العلاقة بينها، ومعرفة كيف تنتقل الطلبات، وأين تُحفظ السجلات، وكيف تُدار الصلاحيات، وما الذي يحدث عندما تتوقف إحدى الخدمات. فالمطور الذي يفهم هذه التفاصيل يستطيع التعامل مع المشكلات بثقة أكبر، بدلاً من تغيير الإعدادات عشوائياً عند ظهور خطأ.
قد تكون أول عملية نشر تجربة مليئة بالتفاصيل الصغيرة: مسار WSGI غير صحيح، أو ملف بيئة لا يمكن قراءته، أو شهادة لم تصدر بعد، أو إعداد ALLOWED_HOSTS ناقص. هذه الأمور طبيعية أثناء التعلم. ومع كل مشكلة تحلها، ستكتسب فهماً أفضل لكيفية عمل تطبيقات الويب في بيئة الإنتاج.
ابدأ ببنية بسيطة، واختبرها جيداً، ثم أضف التحسينات عندما تظهر الحاجة إليها. لا تحتاج إلى بناء بنية موزعة ومعقدة منذ اليوم الأول. تطبيق Django واحد يعمل بصورة مستقرة، مع إعدادات أمان مناسبة، وسجلات واضحة، ونسخ احتياطية قابلة للاستعادة، يمكن أن يكون أساساً جيداً لخدمة حقيقية قابلة للتطوير لاحقاً.