جانغو هو أشهر أطر العمل البرمجية المكتوبة بلغة بايثون، إذ يساعدك هذا الإطار المميز على تطوير تطبيقات الويب ووضعها في الخدمة بسرعة ويُسر، ويحتوي جانغو خادم تطوير مُضمّن يتيح لك اختبار الكود محليًا والتأكد من عمله إلّا أنه مبسط يلبي أغراض التجربة فقط، فأي تطبيق يُطلق في بيئة العمل الفعلية مهما بلغت بساطته يتطلب خادم ويب بمواصفات أعلى وحماية أكبر.
سنُحاكي في هذا المقال بيئة عمل كاملة لتطبيق جانغو مبنية على خادم أوبونتو إصدار 20.04، ونعرض كيفية تثبيت وإعداد المكونات اللازمة لعمله على الخادم، بدءًا من بناء قاعدة بيانات PostgreSQL بدلًا من SQLite (قاعدة بيانات جانغو الافتراضية)، ومن ثم إعداد خادم التطبيق من نوع Gunicorn، وصولًا إلى تهيئة خادم وكيل عكسي من نوع Nginx يعمل أمام Gunicorn ويقدم لتطبيقنا كل ما يملكه من مميزات تدعم أمانه وأدائه.
متطلبات بيئة العمل والأهداف
ستحتاج إلى خادم بنظام تشغيل أوبونتو وقد استعملنا في هذا المقال الإصدار 20.04، مع مستخدم –ليس مسؤول- ولكنه يتمتع بصلاحيات عالية sudo
، بالإضافة إلى تفعيل جدار الحماية الافتراضي على الخادم، ويمكنك الاستعانة بمقال التهيئة الأولية لخادم أوبونتو.
سنثبت جانغو في بيئةٍ افتراضية لما تقدمه من مرونة تتيح لنا التعامل مع التطبيق ومتطلباته الخارجية بطريقة منفصلة أي كل على حدة، وسنبدأ بإعداد قاعدة البيانات وبمجرد الانتهاء منها وتشغيل التطبيق، سنثبت Gunicorn ليتعامل مع طلبات العملاء الواردة عبر HTTP ويحولها إلى أوامر بايثون يعالجها التطبيق، ومن ثم سنشغل خادم وسيط عكسي هو Nginx أمام Gunicorn للاستفادة من قدراته العالية في التعامل الفعال والآمن مع حركة البيانات الواردة إلى التطبيق.
لنبدأ التطبيق العملي!
تثبيت الحزم اللازمة من مستودعات أوبونتو
سنُنثبت معظم المكونات اللازمة للعمل من مستودعات أوبونتو، ومن ثم سنستخدم pip
مدير حزم بايثون لتثبيت مكونات أخرى خاصة بالتطبيق.
البداية دومًا مع تحديث apt
دليل الحزم المحلي لأوبونتو، ومن بعدها سنبدأ بتحميل الحزم اللازمة وتثبيتها بما يتوافق مع إصدار بايثون الذي تستخدمه فالأوامر تختلف قليلًا بين بايثون 2 و 3، علمًا أن جانغو 1.11 هو أحدث إصدار من الإصدارات التي تدعم بايثون 2، لذا ننصحك بترقية إطار العمل الذي تعتمده واستخدام بايثون 3 إن كنت تنشئ مشروعًا جديدًا.
إن كنت تستخدم بايثون 3 اكتب الأوامر:
sudo apt update sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl
أما أوامر بايثون 2 فهي:
sudo apt update sudo apt install python-pip python-dev libpq-dev postgresql postgresql-contrib nginx curl
ثبتنا بموجب الأوامر السابقة pip
وملفات تطوير جانغو اللازمة لبناء Gunicorn لاحقًا و نظام قواعد البيانات Postgres مع المكاتب اللازمة للتفاعل معه وأخيرًا خادم الويب Nginx.
إنشاء قاعدة البيانات PostgreSQL ومستخدم التطبيق
يمتلك نظام قواعد بيانات Postgres ميزةً افتراضية تسمى توثيق النِّدّ Peer Authentication وهي خاصة بمصادقة الاتصالات المحلية، وتعني أن وجود مستخدم في نظام التشغيل يطابق اسمه اسم مستخدم Postgres فعال، يمنح هذا المستخدم -أي مستخدم نظام التشغيل- القدرة على الدخول إلى Postgres دون الحاجة إلى توثيق.
وقد أُنشئ مستخدم لنظام التشغيل اسمه postgres
ليتوافق مع مستخدم postgres
المدير لنظام PostgreSQL، وسنستفيد من هذا المستخدم للمهام الإدارية، مع إمكانية استخدام sudo وتمرير اسم المستخدم بعد الراية u-
.
سجل الدخول إلى Postgres عبر الأمر التالي:
sudo -u postgres psql
ستظهر لك نافذة أوامر PostgreSQL، اكتب فيها تعليمة إنشاء قاعدة بيانات المشروع:
CREATE DATABASE myproject;
تنويه: تذكر أن تنهي كافة أوامر Postgres بفاصلة منقوطة لتضمن التنفيذ السليم.
أنشئ بعدها مستخدم قاعدة البيانات واختر له كلمة سر قوية، وفق الأمر التالي:
CREATE USER myprojectuser WITH PASSWORD 'password';
اضبط الآن القيم الافتراضية لمحددات اتصال المستخدم المنشأ بالتوافق مع توصيات جانغو بحيث لا يتطلب الأمر الاستعلام عنها وإعادة تعيينها مع كل تأسيس جديد للاتصال ما سينعكس إيجابًا على تسريع عمليات قاعدة البيانات، والمحددات المطلوب ضبطها هي: الترميز encoding
الذي يأخذ القيمة الافتراضية UTF-8
، وأسلوب عزل العملية transaction isolation scheme
الذي يوضع "read committed" ومهمته منع قراءة العمليات غير المكتملة uncommitted transactions، وأخيرًا المنطقة الزمنية نثبتها على UTC
، وفق التعليمات التالية:
ALTER ROLE myprojectuser SET client_encoding TO 'utf8'; ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed'; ALTER ROLE myprojectuser SET timezone TO 'UTC';
وأخيرًا امنح مستخدم قاعدة البيانات المنشأ صلاحيات الإدارة الكاملة لقاعدة البيانات:
GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;
يمكنك الآن الخروج من موجه أوامر PostgreSQL بكتابة الأمر:
\q
إنشاء بيئة بايثون الافتراضية
بعد أن جهزنا قاعدة بيانات التطبيق سننتقل الآن لتثبيت كافة متطلبات بايثون اللازمة له ضمن بيئة افتراضية سهلة الإدارة، ولكن أولًا علينا تثبيت virtualenv
بعد تحديث pip
، ستختلف أوامر التثبيت اختلافًا بسيطًا بين بايثون 2 وبايثون 3.
إن كنت تستخدم بايثون 3 اكتب الأوامر التالية:
sudo -H pip3 install --upgrade pip sudo -H pip3 install virtualenv
أما لبايثون 2 فاكتب:
sudo -H pip install --upgrade pip sudo -H pip install virtualenv
أنشئ مجلدًا باسم مشروعك، ثم انتقل إلى داخله:
mkdir ~/myprojectdir cd ~/myprojectdir
وأنشئ البيئة الافتراضية ضمنه بكتابة الأمر التالي:
virtualenv myprojectenv
بموجب الأوامر السابقة، سيُنشَأ ضمن مجلد مشروعك myprojectdir
مجلدًا فرعيًا للبيئة الافتراضية اسمه myprojectenv
، وسيُثبت بداخله نسخة محلية من بايثون ومدير الحزم pip
محلي.
تؤمن هذه البيئة الافتراضية بيئة بايثون معزولة لمشروعنا ولكنها تحتاج لتفعيل قبل البدء باستخدامها وتنزيل ما يلزم ضمنها، والتفعيل وفق الأمر التالي:
source myprojectenv/bin/activate
سيتغير بعدها محث سطر الأوامر ليشير إلى أنك تعمل في البيئة الافتراضية وسيبدو مثل:
(myprojectenv)user@host:~/myprojectdir$
يمكنك الآن باستخدام pip
تثبيت جانغو و Gunicorn و psycopg2
الخاص بالتخاطب مع قاعدة البيانات التي بنيناها:
تنويه: يُستخدم pip دومًا داخل البيئة الافتراضية وليس pip3 بغض النظر عن إصدار بايثون المعتمد.
pip install django gunicorn psycopg2-binary
يفترض الآن تثبيت كل المتطلبات اللازمة لبدء مشروع جانغو Django جديد.
إنشاء وإعداد مشروع جانغو جديد
يمكننا الآن إنشاء ملفات المشروع بعد أن أعددنا متطلبات بايثون اللازمة له ضمن البيئة الافتراضية.
إنشاء مشروع جانغو Django
لدينا بالفعل مجلدًا لمشروعنا هو myprojectdir ولذا سنكتبه في أمر إنشاء المشروع حتى يثبت جانغو الملفات ضمنه بدلًا من اختياره مجلدًا آخر تلقائيًا، وعند تنفيذ الأمر سيُنشئ مجلدًا فرعيًا ضمن مجلد المشروع يحمل نفس الاسم ليحتوي على الشيفرة البرمجية أو كود التطبيق ويضاف فيه أيضًا السكربت الخاص بالإدارة:
django-admin startproject myproject ~/myprojectdir
وبناءً على ذلك سيتضمن مجلد المشروع (وهو في حالتنا myprojectdir/~
) العناصر التالية:
-
myprojectdir/manage.py/~
: سكربت الإدارة الخاص بجانغو. -
/myprojectdir/myproject/~
: حزمة مشروع جانغو، ويجب أن تتضمن الملفات التالية: - init__.py__
- settings.py
- urls.py
- asgi.py
- wsgi.py
-
/myprojectdir/myprojectenv/~
: مجلد البيئة الافتراضية المنشأة في الخطوة السابقة.
ضبط إعدادات مشروع جانغو
سنعمل في هذه الفقرة على ثلاث إعدادات هي قائمة العناوين وأسماء النطاقات المسموح باستخدامها للاتصال مع جانغو، وقاعدة البيانات المعتمدة، ومسار ملفات التطبيق الساكنة.
افتح ملف الإعدادات باستعمال محرر النصوص لنبدأ بهذه التعديلات بالترتيب:
nano ~/myprojectdir/myproject/settings.py
ابحث عن المحدد ALLOWED_HOSTS
إذ سنبدأ تعديلاتنا به، ويحميك هذا المحدد من الاتصالات غير المشروعة، ووظيفته تعريف الخوادم المسموح باستخدامها للاتصال بالتطبيق، وتُعرف باستخدام عنوان IP الخادم أو اسم النطاق أو اسم النطاق الفرعي، وأي طلب يرد للتطبيق يحمل في ترويسته اسم مضيف غير معرف ضمن هذا المحدد فسيُمنع.
تُكتب قائمة عناوين IP أو أسماء النطاقات المسموح لها بالاتصال ضمن الأقواس المربعة، وكل عنوان أو اسم منها ضمن اشارتي اقتباس مفردتين، ويفصل بين كل قيمة وأخرى فاصلة، ولو رغبت بالإشارة إلى النطاق الرئيس وكل ما يحوي من نطاقات فرعية فاستخدم النقطة قبل اسم النطاق، انظر الأمثلة التالية لمزيد من الإيضاح:
. . . # The simplest case: just add the domain name(s) and IP addresses of your Django server # ALLOWED_HOSTS = [ 'example.com', '203.0.113.5'] # To respond to 'example.com' and any subdomains, start the domain with a dot # ALLOWED_HOSTS = ['.example.com', '203.0.113.5'] ALLOWED_HOSTS = ['your_server_domain_or_IP', 'second_domain_or_IP', . . ., 'localhost']
تنويه: تأكد من إضافة الخادم المحلي localhost
على قائمة الأجهزة المسموح لها بالاتصال إذ إننا سننشئ عليه وكيل محلي Nginx.
المحدد التالي للضبط هو DATABASES
الخاص بقواعد البيانات، ابحث عنه ضمن ملف الإعدادات وسنُعدّله ليناسب قاعدة بيانات PostgreSQL التي أعددناها بدلًا من SQLite قاعدة البيانات الافتراضية لجانغو.
تتمثل تعديلاتنا عليه في جعل جانغو يستخدم المحول psycopg2
(سبق أن ثبتناه باستخدام pip
) ليناسب التخاطب مع قاعدة البيانات الجديدة PostgreSQL، ومن ثم كتابة اسم قاعدة البيانات الصحيح واسم مستخدم قاعدة البيانات وكلمة المرور الخاصة به، وأيضًا تحديد مضيف قاعدة البيانات وهو في حالتنا الخادم المحلي، أما بالنسبة لبوابة الاتصال مع قاعدة البيانات port
فيمكنك تركها فارغة، انظر التعليمات أدناه:
. . . DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'myproject', 'USER': 'myprojectuser', 'PASSWORD': 'password', 'HOST': 'localhost', 'PORT': '', } } . . .
أما التعديل الأخير على الملف settings.py فهو إضافة مسار ملفات التطبيق الساكنة، وهذا التعديل ضروري ليتمكن Nginx من معرفة مكان هذه الملفات والتعامل مع الطلبات الواردة بخصوصها، انتقل الآن إلى نهاية الملف واكتب الأسطر التالية التي ستحدد مكان وجود هذه الملفات ويُشار إليه بـ static
:
. . . STATIC_URL = '/static/' import os STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
والآن احفظ الملف وأغلقه.
إكمال الإعداد الأولي للمشروع
بعد أن ضبطنا إعدادات قاعدة البيانات يمكننا الآن تهجير مخطط قاعدة البيانات الأولي إلى قاعدة بيانات PostgreSQL المعتمدة في مشروعنا وذلك باستخدام سكريبت الإدارة:
~/myprojectdir/manage.py makemigrations ~/myprojectdir/manage.py migrate
ومن بعدها سننشئ حساب المستخدم المسؤول عن إدارة التطبيق بكتابة الأمر التالي:
~/myprojectdir/manage.py createsuperuser
وسيُطلب منك النظام إدخال اسم مستخدم لهذا الحساب وكلمة مرور وعنوان بريد إلكتروني.
أما الآن سننفذ عملية جمع المكونات الساكنة للتطبيق static assets في المسار الذي حددناه في الفقرة السابقة وذلك وفق الأمر التالي:
~/myprojectdir/manage.py collectstatic
سيُطلب منك تأكيد العملية، وبعدها ستُجمع الملفات الساكنة ضمن مجلد مشروعك في المكان الذي أطلقنا عليه اسم static
.
كما ورد في متطلبات بيئة العمل في بداية المقال فإن الخادم أوبونتو يجب أن يتضمن جدار ناري فعال هو الجدار الناري الأساسي للينكس UFW (يسمى الجدار الناري غير المعقد)، ولذا يتحتم علينا فتح البوابة التي سنستخدمها في هذا الجدار (وهي البوابة 8000) حتى نتمكن من الاتصال مع خادم تطوير جانغو واختباره.
يتم فتح البوابة بالأمر التالي:
sudo ufw allow 8000
والآن يمكنك اختبار تطبيقك بتشغيل خادم التطوير عبر الأمر التالي:
~/myprojectdir/manage.py runserver 0.0.0.0:8000
استعرض التطبيق بكتابة عنوان IP أو اسم النطاق مع البوابة 8000:
ضمن متصفحك:
http://server_domain_or_IP:8000
وستحصل على صفحة الدليل index الافتراضية لجانغو:
ولو أضفت admin/
في نهاية الرابط، سترى شاشة تسجيل الدخول إلى لوحة التحكم:
أدخل اسم المستخدم وكلمة المرور الذي أنشأته عبر استخدام الأمر createsuperuser
فتدخل إلى لوحة التحكم:
أوقف خادم التطوير بالضغط على ctrl-c داخل شاشة الطرفية حالما تنتهي من اختباراتك.
التحقق من قدرة Gunicorn على تخديم المشروع
التحقق من قدرة Gunicorn على تخديم المشروع هو الأمر الأخير الذي سنقوم به ضمن البيئة الافتراضية، وذلك عبر الدخول إلى مجلد المشروع ومن ثم استخدام الأمر gunicorn
لتشغيل وحدة WSGI الخاصة بتطبيقنا:
cd ~/myprojectdir gunicorn --bind 0.0.0.0:8000 myproject.wsgi
سيشغل الأمر السابق Gunicorn على نفس الواجهة التي استخدمها خادم تطوير جانغو ويمكنك اختبار ذلك بإعادة طلب صفحات التطبيق عبر المتصفح.
تنويه: ستلاحظ أن لوحة التحكم غير مُنسقة لأن Gunicorn لا يملك حتى الآن مسار صفحات CSS المسؤولة عن ذلك.
لاحظ أننا استدعينا Gunicorn ومررنا له الوحدة التي تتضمن ملف wsgi.py -الذي يعدّ نقطة الدخول إلى تطبيقنا- مع تحديد موقع هذا الملف (حددناه عبر تنفيذ الأمر في مكان وجود الملف أي في مجلد المشروع)، ويحتوي الملف wsgi.py على دالة تسمى التطبيق application هي المسؤولة عن التواصل مع التطبيق. يمكنك معرفة المزيد حول مواصفات WSGI.
اضغط على الاختصار ctrl-c داخل شاشة الطرفية عندما تنتهي من الاختبار لإيقاف خادم Gunicorn.
يمكنك الآن إيقاف البيئة الافتراضية بالأمر التالي بعد أن انتهت إعدادات جانغو:
deactivate
وستلاحظ اختفاء محث البيئة الافتراضية من نافذة سطر الأوامر.
إنشاء ملفات تمهيد Gunicorn
اختبرنا في الفقرة السابقة تفاعل Gunicorn مع جانغو وتأكدنا من عمله، والآن سننشئ ملفات التمهيد systemd الخاصة به (المِقبس socket والخدمة)، لاستخدامها في إيقاف وتشغيل خادم التطبيق بطريقة أفضل.
سيُنشَأ عند الإقلاع مِقبس Gunicorn ويبقى جاهزًا يستمع بانتظار الاتصالات، وعند ورود أي اتصال، فإن systemd سيشغل تلقائيًا عملية Gunicorn لتتعامل مع هذا الاتصال.
لنبدأ بإنشاء ملف التمهيد لمِقبس Gunicorn بكتابة الأمر التالي مع امتيازات sudo:
sudo nano /etc/systemd/system/gunicorn.socket
بنية هذا الملف تتضمن ثلاثة مقاطع سنكتبها ضمنه، وهي: المقطع [Unit]
مخصص لوصف المِقبس، والمقطع [Socket]
يبين موقع المِقبس، أما المقطع [Install]
فيحرص على إنشاء المقبس في الوقت الصحيح، وبذلك يصبح الملف بالشكل التالي:
[Unit] Description=gunicorn socket [Socket] ListenStream=/run/gunicorn.sock [Install] WantedBy=sockets.target
يمكنك الآن إغلاق الملف بعد حفظ التغييرات.
ومن ثم أنشئ ملف التمهيد لخدمة Gunicorn وفق الأمر التالي وأيضًا مع امتيازات sudo، لاحظ أن الاسم والمسار متطابقان بين ملف المقبس وملف الخدمة باستثناء اللاحقة فهي التي تحدد نوع الملف:
sudo nano /etc/systemd/system/gunicorn.service
تتضمن بنية ملف الخدمة ثلاثة مقاطع أيضًا هي: [Unit]
و [Service]
و [Install]
.
يُخصص المقطع [Unit]
لبيانات التعريف والاعتماديات، ونكتب ضمنه وصف الخدمة ونخبر نظام التمهيد أن تشغيل هذه الخدمة مرتبط بالوصول إلى هدف الشبكة الذي يعتمد على المقبس (عند ورود اتصال على المقبس كما ذكرنا سابقًا) وهذه العلاقة بين المقبس والخدمة سنذكرها ضمن المحدد Requires
كما يلي:
[Unit] Description=gunicorn daemon Requires=gunicorn.socket After=network.target
نحدد ضمن المقطع [Service]
من يتمتع بصلاحية تشغيل الخدمة من مستخدمين ومجموعات عمل، وهم في حالتنا مستخدم التطبيق sammy
وهو مالك كافة الملفات المرتبطة، ومجموعة العمل www-data
التي ستتيح لخادم Nginx الاتصال بسهولة مع Gunicorn.
ونوضح بعده مجلد العمل والأمر الذي سيُستخدم لتشغيل الخدمة: بكتابة المسار الكامل لملف Gunicorn التنفيذي (الذي تم تثبيته في بيئتنا الافتراضية)، وربط العملية بمقبس يونيكس الذي أنشأناه ضمن المسار المحدد في run/
(انظر المقطع [Socket]
في ملف المقبس) حتى تتمكن العملية من التواصل مع Nginx.
وأخيرًا سنكتب ضمن المقطع التعليمات اللازمة لتُسجل كافة الأحداث في ملفات خرج قياسية حتى تتمكن journald
من التقاط تسجيلات الأحداث الصادرة عن Gunicorn، وننوه إلى إمكانية ضبط تعديلات أخرى خاصة بـ Gunicorn هنا مثل تحديد عدد العمال workers
بثلاثة كما في حالتنا.
انظر الآن إلى ملف الخدمة بعد إضافة المقطع [Service]
:
[Unit] Description=gunicorn daemon Requires=gunicorn.socket After=network.target [Service] User=sammy Group=www-data WorkingDirectory=/home/sammy/myprojectdir ExecStart=/home/sammy/myprojectdir/myprojectenv/bin/gunicorn \ --access-logfile - \ --workers 3 \ --bind unix:/run/gunicorn.sock \ myproject.wsgi:application
أما المقطع [Install]
وهو المقطع الأخير، نستخدمه لإخبار نظام التمهيد بمرحلة بدء التشغيل التي سيرتبط بها تشغيل الخدمة في حال فعلّناها لتعمل تلقائيًا عند الإقلاع، وفي حالتنا ضبطنا الموضوع لتعمل الخدمة عندما يصل نظام التشغيل إلى مرحلة تعدد المستخدمين multi-user.
انظر الآن الشكل النهائي لملف الخدمة، ولاتنسَ حفظ التغييرات عليه قبل الإغلاق:
[Unit] Description=gunicorn daemon Requires=gunicorn.socket After=network.target [Service] User=sammy Group=www-data WorkingDirectory=/home/sammy/myprojectdir ExecStart=/home/sammy/myprojectdir/myprojectenv/bin/gunicorn \ --access-logfile - \ --workers 3 \ --bind unix:/run/gunicorn.sock \ myproject.wsgi:application [Install] WantedBy=multi-user.target
يمكننا الآن تشغيل وتفعيل مقبس Gunicorn وفق الأمرين أدناه، وسيؤدي هذا إلى إنشاء ملف المقبس في المسار run/gunicorn.sock/
الآن عند تشغيلنا اليدوي للمقبس وتلقائيًا عند إقلاع نظام التشغيل، وفور ورود اتصال إلى المقبس سيشغل systemd تلقائيًا الخدمة gunicorn.service
لتتعامل مع الاتصال:
sudo systemctl start gunicorn.socket sudo systemctl enable gunicorn.socket
يمكنك التأكد من نجاح العملية عبر التحقق من وجود ملف المقبس.
اطلع على معلومات إضافية عن systemd من خلال المقالين:
- أساسيات Systemd: العمل مع الخدمات، الوحدات Units، واليوميات Journal إدارة الخدمات على لينكس بأدوات نظام التمهيد systemd
التحقق من ملف المقبس
تأكد من حالة العملية لترى إن كانت فعالة وقابلة للتشغيل عن طريق الأمر التالي:
sudo systemctl status gunicorn.socket
وستحصل على ما يشبه الخرج التالي في حال كانت فعالة دون أخطاء:
● gunicorn.socket - gunicorn socket Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor prese> Active: active (listening) since Fri 2020-06-26 17:53:10 UTC; 14s ago Triggers: ● gunicorn.service Listen: /run/gunicorn.sock (Stream) Tasks: 0 (limit: 1137) Memory: 0B CGroup: /system.slice/gunicorn.socket
تحقق الآن من وجود ملف المقبس gunicorn.sock
ضمن المسار run/
بكتابة الأمر التالي:
file /run/gunicorn.sock
ستحصل على الخرج التالي الذي يبين وجود الملف:
/run/gunicorn.sock: socket
أما في حال عدم وجود الملف gunicorn.sock، أو ظهور أي خطأ في خرج التعليمة systemctl status
السابقة، فهذا يعني حدوث خطأ ما منع اكتمال العملية وإنشاء ملف المقبس، يمكنك تبيانه عبر استعراض سجلات الأحداث الخاصة بمقبس Gunicorn من خلال الأمر:
sudo journalctl -u gunicorn.socket
ومن ثم فتح الملف gunicorn.socket ضمن المسار etc/systemd/system/ ثانيةً لتصحيح واستدراك أي مشاكل موجودة بينتها السجلات قبل استكمال العمل.
التحقق من تفعيل المقبس
صحيح أننا شغلنا المقبس gunicorn.socket وتحققنا منه في الخطوات السابقة، ولكن الخدمة gunicorn.service لم تُفعل بعد وهذا طبيعي فهي مرتبطة بالاتصالات الواردة ولن تعمل قبل ورود أول اتصال. ويمكنك التحقق من حالتها بالأمر:
sudo systemctl status gunicorn
وستحصل على الخرج التالي الذي يبين أنها غير فعالة:
● gunicorn.service - gunicorn daemon Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled) Active: inactive (dead)
أرسل الآن اتصال تجريبي عبر curl
لتختبر آلية العمل، وفق التالي:
curl --unix-socket /run/gunicorn.sock localhost
وستحصل بموجبه على صفحة HTML من تطبيقك داخل شاشة الطرفية، ما يعني أن Gunicorn بدأ بالعمل وأنه قادرٌ على تخديم التطبيق، نفذ مجددًا أمر التحقق من حالة الخدمة:
sudo systemctl status gunicorn
ولاحظ اختلاف الخرج التنفيذ السابق قبل وجود اتصالات:
● gunicorn.service - gunicorn daemon Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled) Active: active (running) since Fri 2020-06-26 18:52:21 UTC; 2s ago TriggeredBy: ● gunicorn.socket Main PID: 22914 (gunicorn) Tasks: 4 (limit: 1137) Memory: 89.1M CGroup: /system.slice/gunicorn.service ├─22914 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico> ├─22927 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico> ├─22928 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico> └─22929 /home/sammy/myprojectdir/myprojectenv/bin/python /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunico> Jun 26 18:52:21 django-tutorial systemd[1]: Started gunicorn daemon. Jun 26 18:52:21 django-tutorial gunicorn[22914]: [2020-06-26 18:52:21 +0000] [22914] [INFO] Starting gunicorn 20.0.4 Jun 26 18:52:21 django-tutorial gunicorn[22914]: [2020-06-26 18:52:21 +0000] [22914] [INFO] Listening at: unix:/run/gunicorn.sock (22914) Jun 26 18:52:21 django-tutorial gunicorn[22914]: [2020-06-26 18:52:21 +0000] [22914] [INFO] Using worker: sync Jun 26 18:52:21 django-tutorial gunicorn[22927]: [2020-06-26 18:52:21 +0000] [22927] [INFO] Booting worker with pid: 22927 Jun 26 18:52:21 django-tutorial gunicorn[22928]: [2020-06-26 18:52:21 +0000] [22928] [INFO] Booting worker with pid: 22928 Jun 26 18:52:21 django-tutorial gunicorn[22929]: [2020-06-26 18:52:21 +0000] [22929] [INFO] Booting worker with pid: 22929
وفي حال حصلت على أي أخطاء في خرج systemctl status
أو خرج curl
، تفقد عندها سجلات الأحداث عبر الأمر:
sudo journalctl -u gunicorn
وافتح الملف gunicorn.service ضمن المسار etc/systemd/system/ وصحح المشاكل التي بينتها السجلات قبل إكمال العمل، وانتبه إلى أن أي تعديلات تطبقها عليه لن تصبح سارية المفعول إلّا بعد إعادة تحميل الخدمة ومن ثم إعادة تشغيل Gunicorn وفق الأوامر التالية:
sudo systemctl daemon-reload sudo systemctl restart gunicorn
إعداد Nginx ليؤدي دور وكيل Gunicorn
لنبدأ بإنشاء كتلة خادم جديدة لخادم Nginx ضمن المجلد sites-available.
اكتب أولًا الأمر التالي الذي سيفتح ملفًا نصيًا:
sudo nano /etc/nginx/sites-available/myproject
أنشئ فيه كتلة خادم جديدة وحدد مواصفاتها بحيث تتلقى الطلبات الطبيعية التي ترد إلى البوابة 80 وتطلب اسم نطاقك أو عنوان IP الخادم، وذلك بكتابة التعليمات التالية:
server { listen 80; server_name server_domain_or_IP; }
وبعدها أضف ضمن الكتلة نفسها التعليمات الخاصة بـ location المبينة أدناه، ووظيفتها جعل Nginx يتجاهل الأخطاء التي تنجم عن عدم العثور على رمز الموقع favicon، وإرشاده إلى موقع ملفات التطبيق الساكنة أي المسارmyprojectdir/static/~
الذي اختصرناه إلى "static/"، إذًا أصبحت كتلة الخادم على الشكل التالي:
server { listen 80; server_name server_domain_or_IP; location = /favicon.ico { access_log off; log_not_found off; } location /static/ { root /home/sammy/myprojectdir; } }
والخطوة الأخيرة ستكون إنشاء كتلة {} / location
التي ستتعامل مع بقية الطلبات، والإشارة ضمنها للملف proxy_params القياسي الذي أنُشئ أثناء تثبيت Nginx، ومن ثم تمرير كامل حركة البيانات مباشرةً إلى مقبس Gunicorn:
server { listen 80; server_name server_domain_or_IP; location = /favicon.ico { access_log off; log_not_found off; } location /static/ { root /home/sammy/myprojectdir; } location / { include proxy_params; proxy_pass http://unix:/run/gunicorn.sock; } }
احفظ التغييرات على الملف وأغلقه.
ومن ثم فعل الملف عبر ربطه بالمجلد sites-enabled
باستخدام الأمر:
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled
اختبر صحة تعليمات Nginx من ناحية قواعد الكتابة من خلال الأمر:
sudo nginx -t
أعد تشغيل Nginx إن لم تظهر لك أي أخطاء:
sudo systemctl restart nginx
لنضبط الآن إعدادات الجدار الناري UFW عبر السماح بحركة مرور البيانات عبر البوابة 80، وإغلاق البوابة 8000 التي سمحنا بها سابقًا للتخاطب مع خادم التطوير، فهي لم تعد لازمة.
اكتب الأوامر التالية لضبط الجدار الناري:
sudo ufw delete allow 8000 sudo ufw allow 'Nginx Full'
يمكنك الآن الوصول لتطبيقك باستخدام اسم النطاق أو عنوان IP.
تنويه: الخطوة الأهم بعد إعداد الخادم الوكيل هي تأمين شهادات TLS لتشفير حركة البيانات المتبادلة مع التطبيق وحماية البيانات الحساسة مثل كلمات المرور، لذا احرص على تنفيذ هذه الخطوة، ويمكنك الحصول على شهادات مجانية من خدمة Let’s Encrypt في كان لديك اسم نطاق محجوز، واتبع الخطوات الواردة في مقال كيف تؤمّن خادم ويب NGINX على أوبنتو 16.04 لتثبيت الشهادة فالطريقة نفسها.
استكشاف الأخطاء وإصلاحها في هذه البيئة
إذا لم تُظهر هذه الخطوة الأخيرة تطبيقك، فستحتاج إلى استكشاف أخطاء التثبيت وإصلاحها.
يظهر Nginx الصفحة الافتراضية بدلاً من تطبيق جانغو
في حال أظهر Nginx صفحته الافتراضية بدلًا من تطبيقك، فهذا يعني عادةً حاجتك لضبط server_name
في الملف التالي:
/etc/nginx/sites-available/myproject
ليشير إلى اسم نطاقك أو عنوان IP للخادم، إذ يستخدم Nginx القيمة الموجودة في server_name
ليُحدد أي كتلة خادم ستلبي الطلبات الواردة إليه، وعندما يرجع لك صفحته الافتراضية فهذا يعني أنه لم يتمكن من مطابقة طلبك بدقة مع أي من الكتل الموجودة ضمن ملفاته فيُرجع تلقائيًا الصفحة الموجودة في كتلته الافتراضية في المسار التالي:
/etc/nginx/sites-available/default
لذا احرص على تحديد server_name
في الكتلة الخاصة بمشروعك بدقة وعلى نطاق أضيق من المذكور في الكتلة الافتراضية بحيث لا تسمح لخادم Nginx بتجاهلها والعودة إلى كتلته الافتراضية.
ظهور Nginx الخطأ 502 بدلا من تطبيق جانغو
يوجد مجموعة واسعة من الاحتمالات التي قد تسبب الخطأ 502 Bad Gateway Error، لذا فهو يحتاج إلى معلومات وتقصي أكثر لبيان سببه الفعلي.
سجلات أحداث Nginx هي أهم مصدر معلومات عن الأحداث والأخطاء التي تحصل خلال عمل الوكيل أو الوسيط، ويمكنك تتبعها باستخدام الأمر:
sudo tail -F /var/log/nginx/error.log
والآن حدث الصفحة في متصفح الإنترنت لتولد خطأ جديدًا في السجل واقرأه بدقة من شأنه تضييق نطاق البحث عن سبب الخطأ، وسنعطيك بعض الاحتمالات عن ما يمكن أن يظهر لك من رسائل خطأ.
احتمال الخطأ الأول:
connect() to unix:/run/gunicorn.sock failed (2: No such file or directory)
وهو يعني أن Nginx لم يتمكن من العثور على ملف المقبس gunicorn.sock
في الموقع المحدد، وعندها عليك مقارنة موقع proxy_pass
المذكور في الملف التالي:
/etc/nginx/sites-available/myproject
مع الموقع الفعلي المذكور في ملف المقبس gunicorn.sock
المنشئ بواسطة وحدة تمهيد النظام gunicorn.socket
.
أما في حال لم تجد ملف المقبس gunicorn.sock
في المسار run/
فارجع إلى فقرة التحقق من ملف المقبس واتبع إرشاداتها لمعالجة هذه الحالة.
احتمال الخطأ الثاني:
connect() to unix:/run/gunicorn.sock failed (13: Permission denied)
يدل هذا الخطأ على مشاكل في صلاحيات وصول Nginx إلى مقبس Gunicorn، وغالبًا ما ينجم هذا الخطأ عن تنفيذ إجرائية العمل بمستخدم الجذر بدلًا من مستخدم يملك امتيازات sudo
وعندها فإن systemd سيُنشئ مقبس Gunicorn ولكنّ Nginx لن يتمكن من الوصول إليه.
ومن الممكن أن تظهر نفس المشكلة في حال وجود أي تقييد للصلاحيات على طول المسار الواصل إلى ملف المقبس بدءًا من الجذر /
وصولًا إلى الملف gunicorn.sock
، ويمكنك استعراض صلاحيات الوصول واسم المالك على كافة المجلدات الموجودة على مسار ملف المقبس باستخدام التعليمة namei
وفق التالي:
namei -l /run/gunicorn.sock
وستحصل على المعلومات التفصيلية عن صلاحيات كافة عناصر المسار ضمن الخرج بالشكل التالي:
f: /run/gunicorn.sock drwxr-xr-x root root / drwxr-xr-x root root run srw-rw-rw- root root gunicorn.sock
يبين لك العمود الأول الصلاحيات الممنوحة على العنصر، والعمود الثاني يشير إلى اسم المستخدم المالك، بينما العمود الثالث فهو خاص باسم مجموعة العمل التي ينتمي إليها المالك، وبذلك يمكنك معرفة نوع الوصول المسموح إلى ملف المقبس.
فمثلًا في الخرج السابق، كافة عناصر المسار تحمل صلاحية القراءة والتنفيذ فهي تنتهي بالرمز r-x
بدلًا من ---
وهذا يعني إمكانية الوصول إليها من قبل Nginx فلو كان أي منها لا يحمل هذه الصلاحيات لحصلت مشكلة في الوصول إلى المقبس، وتُحل المشكلة آنذاك بإعطاء صلاحية العمل الناقصة أو بضم مستخدم Nginx إلى مجموعة العمل التي تتضمن مالك العنصر.
ظهور جانغو (لا يمكن الاتصال بالخادم: تم رفض الاتصال)
هي إحدى الرسائل التي قد تتلقاها من جانغو عند محاولتك استعراض بعض أجزاء التطبيق في متصفح الإنترنت:
OperationalError at /admin/login/ could not connect to server: Connection refused Is the server running on host "localhost" (127.0.0.1) and accepting TCP/IP connections on port 5432?
تعني هذه الرسالة أن جانغو لم يتمكن من الوصول إلى قاعدة البيانات، لذا فالخطوة الأولى هي التأكد من إقلاع نسخة قاعدة البيانات Postgres instance باستخدام الأمر التالي:
sudo systemctl status postgresql
وفي حال تبين لك أنها لا تعمل فيمكنك تشغيلها ومن ثم تفعيلها لتعمل تلقائيًا عند الإقلاع (ما لم تكن مفعلة للتشغيل التلقائي) وفق الأمرين:
sudo systemctl start postgresql
sudo systemctl enable postgresql
وإن استمرت المشكلة تأكد من إعدادات قاعدة البيانات الموجودة في الملف التالي وصححها:
~/myprojectdir/myproject/settings.py
المزيد من استكشاف الأخطاء وإصلاحها
استعن دومًا بسجلات الأحداث فهي مصدر مهم للمعلومات وحدد الأخطاء بدقة وابحث عنها، إليك أهم السجلات.
- للتحقق من سجلات عملية Nginx اكتب:
sudo journalctl -u nginx
- للتحقق من سجلات وصول Nginx اكتب:
sudo less /var/log/nginx/access.log
- للتحقق من سجلات خطأ Nginx اكتب:
sudo less /var/log/nginx/error.log
- للتحقق من سجلات أحداث Gunicorn اكتب:
sudo journalctl -u gunicorn
- للتحقق من سجلات أحداث مقبس Gunicorn اكتب:
sudo journalctl -u gunicorn.socket
عند تعديل أو تحديث الإعدادات أو التطبيق يجب إعادة تشغيل العمليات لتصبح التعديلات سارية المفعول.
ففي حال عدلت تطبيق جانغو يمكنك إعادة تشغيل عملية Gunicorn عبر الأمر:
sudo systemctl restart gunicorn
وإن عدلت ملفات مقبس أو خدمة Gunicorn فعليك إعادة تحميل العفريت وإعادة تشغيل الخدمة:
sudo systemctl daemon-reload sudo systemctl restart gunicorn.socket gunicorn.service
أما لو عدلت إعدادات كتلة الخادم لـ Nginx فعليك التحقق من صحة القواعد أولًا ومن ثم إعادة تشغيل Nginx وفق التالي:
sudo nginx -t && sudo systemctl restart nginx
ستفيدك هذه الأوامر في عكس تعديلاتك على عمل البيئة بصورة سليمة.
خاتمة
أعددنا في هذا المقال مشروع جانغو ضمن بيئة افتراضية معزولة ومجهز بخادم Gunicorn يوصل طلبات العملاء إلى جانغو حتى يعالجها، وبخادم Nginx الذي يلعب دور الوكيل العكسي لتحسين أداء التطبيق وأمانه، وقدمنا مجموعة من الأدوات المساعدة التي من شأنها خدمة تطبيقاتك العاملة على خادم واحد.
ترجمة -وبتصرف- للمقال How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu 20.04 لصاحبه Erin Glass.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.