اذهب إلى المحتوى

إعداد تطبيق ASGI Django للنشر مع Postgres وخادم Nginx مع Uvicorn


Mostafa Almahmoud

يُعد جانغو Django إطار عمل ويب قوي يساعدك على إطلاق مشروعك سواءٌ كان تطبيق بايثون Python أو موقع ويب، ويتضمن خادم تطوير بسيط يُستخدم لاختبار الشيفرة محليًا ولكن من المعلوم أنه عندما يتعلق الأمر بالإطلاق الاحترافي للتطبيق للشركات أو الأعمال فإننا سنحتاج دومًا إلى خادم ويب قوي وأكثر أمانًا.

الطريقة التقليدية لنشر تطبيق جانغو هي استخدام واجهة بوابة خادم الويب Web Server Gateway Interface -أو اختصارًا WSGI- لكن مع ظهور بايثون 3 وتوفُّر ميزة دعم التنفيذ غير المتزامن أصبح بإمكانك تنفيذ تطبيقات بايثون من خلال المستَدعيات غير المتزامنة asynchronous callables مستخدمًا واجهة بوابة خادم غير متزامنة Asynchronous Server Gateway Interface -أو اختصارًا ASGI - ومع كون تقنية ASGI خلفًا لتقنية WSGI فمواصفات ASGI في بايثون قد ورثتها من WSGI وبالتالي يمكن أن تكون بديلًا عنها.

يوفّر جانغو النمط "غير متزامن في الخارج async outside، متزامن في الداخل sync inside" الذي يسمح بأن تكون الشيفرة متزامنة داخليًًا بينما يعالج خادم ASGI الطلبات بصورةٍ غير متزامنة، وبالتالي يمكن لخادم الويب معالجة العديد من الأحداث الواردة من والصادرة إلى كل التطبيقات بمجرد السماح له أن يمتلك مستدعًى callable غير متزامن. يبقى تطبيق جانغو متزامنًا بهدف الحفاظ على التوافقية العكسية backward compatibility وتجنُّب تعقيد الحوسبة التفرعية؛ وهذا يعني أيضًا أن تطبيق جانغو الذي تعمل عليه لا يلزمه أي تغييرات لينتقل من WSGI إلى ASGI.

ستعمل في هذا الدليل على تثبيت وضبط بعض المكونات على نظام أوبنتو 20.04 لدعم وخدمة تطبيقات جانغو؛ إذ ستثبّت قاعدة بيانات PostgreSQL بدلًا من SQLite المثبتة افتراضيًا، وستهيء أيضًا خادم تطبيقات غوني كورن Gunicorn المقترن بالخادم يوفي كورن Uvicorn، الذي يُعد أحد تنفيذات ASGI، وذلك بهدف ربطه مع تطبيقاتك بصورةٍ غير متزامنة. بعد ذلك، ستعدّ الخادم إنجن إكس Nginx ليعكس الخادم الوكيل proxy لخادم التطبيقات غوني كورن، الأمر الذي يعطيك وصولًا إلى مزاياه الأمنية ومزايا الأداء لخدمة تطبيقاتك.

المتطلبات الأساسية

ستحتاج لإكمال هذه المقالة إلى ما يلي:

الخطوة الأولى - تثبيت الحزم البرمجية من مستودعات أدوات أوبنتو

ستعمل بدايةً على تنزيل وتثبيت جميع العناصر التي تحتاجها من مستودعات أدوات أوبنتو، وستستخدم مدير حزم بايثون pip لتثبيت المكونات الإضافية في وقتٍ لاحق.

ستحتاج أولًا لتحديث دليل الحزم المحلي apt، وتبدأ بعدها في تنزيل الحزم وتثبيتها، إذ تعتمد الحزم التي تثبتها على رقم إصدار بايثون الذي سيستخدمه مشروعك.

نفّذ الأمر التالي لتثبيت حزم النظام المطلوبة لمشروعك:

$ sudo apt update
$ sudo apt install python3-venv libpq-dev postgresql postgresql-contrib nginx curl

سيثبّت هذا الأمر مكتبات بايثون اللازمة لإعداد بيئة افتراضية، ونظام قاعدة بيانات Postgres، والمكتبات اللازمة للتفاعل معها، وخادم الويب إنجن إكس Nginx.

ستنشئ في الخطوة التالية قاعدة بيانات PostgreSQL ومستخدمًا لها من أجل استخدامها في تطبيق جانغو الذي ستعمل عليه.

الخطوة الثانية - إنشاء قاعدة بيانات PostgreSQL ومستخدم لها

ستنشئ الآن قاعدة بيانات ومستخدمًا لها من أجل استخدامهما في تطبيق جانغو الذي ستعمل عليه.

يستخدم نظام قاعدة بيانات Postgres في الحالة الطبيعية مخطط استيثاق يُدعى استيثاق النظير peer authentication من أجل الاتصالات المحلية، ويقضي هذا النوع من الاستيثاق بأنه إذا طابق اسم مستخدم username نظام التشغيل اسم مستخدم صالح ضمن قاعدة بيانات Postgres، فسيُعطَى عندئذ صلاحية الدخول إلى قاعدة البيانات دون الحاجة إلى استيثاق إضافي.

لذلك، أثناء تثبيت Postgres، يُنشأ مستخدم نظام التشغيل باسم postgres ليطابق اسم المستخدم الإداري لنظام إدارة قواعد البيانات PostgreSQL، وهو أيضًا باسم postgres، إذ ستحتاج هذا المستخدم لإنجاز المهام الإدارية، علمًا بأنه يمكنك استخدام sudo وتمرير الخيار u- مع اسم المستخدم.

الآن سجّل الدخول إلى جلسة Postgres تفاعلية من خلال الأمر التالي:

$ sudo -u postgres psql

سيظهر لك محث PostgreSQL وهذا يعني أن صار بإمكانك تجهيز متطلباتك.

أولًا، أنشئ قاعدة بيانات للمشروع الذي تعمل عليه:

postgres=# CREATE DATABASE myproject;

ملاحظة: تنتهي كل تعليمة من تعليمات Postgres بفاصلة منقوطة، ولذا إذا واجهتك أي مشاكل فتحقق من أنك ختمت كل التعليمات بفواصل منقوطة.

بعد ذلك، أنشئ مستخدم قاعدة بيانات لمشروعك، واختر كلمة مرور قوية:

postgres=# CREATE USER myprojectuser WITH PASSWORD 'password';

ستعدّل بعد ذلك بعض معاملات الاتصال للمستخدم الذي أنشأته للتو، والهدف من هذه التعديلات هو تسريع عمليات قاعدة البيانات بحيث لا تضطر للاستعلام عن القيم الصحيحة ثم إعدادها في كل مرة يُؤسَّس فيها اتصال.

ستضع التشفير الافتراضي بالقيمة "UTF-8"، وهي التي يتوقعها جانغو، وستحدّد مخطط عزل الإجراءات الافتراضي بالقيمة "read committed" التي تؤدي إلى حجب عمليات القراءة من الإجراءات التي لم تودع بعد uncommitted transactions. أخيرًا، ستحدد المنطقة الزمنية، والقيمة الافتراضية لها في جانغو هي "UTC". جميع هذه التوصيات هي من القائمين على مشروع جانغو.

postgres=# ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
postgres=# ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
postgres=# ALTER ROLE myprojectuser SET timezone TO 'UTC';

يمكنك الآن منح المستخدم الجديد الوصولَ إلى قاعدة البيانات الجديدة ليتمكن من إدارتها:

postgres=# GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;

وعقب انتهائك، اخرج من محث PostgreSQL بالتعليمة التالية:

postgres=# \q

وبهذا نكون أعددنا نظام Postgres بحيث يتمكن جانغو من الاتصال به وإدارة معلومات قاعدة البيانات فيه.

الخطوة الثالثة- إنشاء بيئة بايثون افتراضية للمشروع

الآن بعد أن أصبح لديك قاعدة بيانات أصبح بإمكانك تجهيز ما تبقى من متطلبات المشروع. ستثبت فيما يلي متطلبات بايثون ضمن بيئة افتراضية بهدف تسهيل الإدارة.

أنشئ أولًا مجلدًا تحفظ فيه ملفات المشروع وانتقل إلى داخله:

$ mkdir ~/myprojectdir
$ cd ~/myprojectdir

ثم استخدم أداة البيئة الافتراضية المبنية مسبقًا من بايثون لإنشاء بيئة افتراضية جديدة.

$ python3 -m venv myprojectenv

سينشئ هذا مجلدًا اسمه "myprojectenv" ضمن المجلد "myprojectdir"، إذ سيثبت داخله إصدارًا محليًا من بايثون وإصدارًا محليًا من pip. يمكنك استخدامه لتثبيت وضبط بيئة بايثون معزولة عن المشاريع الأخرى.

ستحتاج قبل تثبيت متطلبات بايثون للمشروع إلى تفعيل البيئة الافتراضية باستخدام الأمر التالي:

$ source myprojectenv/bin/activate

يجب أن يتغير شكل المحث الآن ليشير إلى أنك تعمل داخل البيئة الافتراضية لبايثون. سيبدو هذا مشابهًا لما يلي:

(myprojectenv)user@host:~/myprojectdir$

لا بُد من تثبيت جانغو داخل البيئة الافتراضية، إذ يسمح تثبيته داخل بيئة مخصصة لمشروعك للمشاريع الأخرى وبيئاتها أن تُعالج بمعزلٍ عن بعضها بعضًا. الآن وبعد تفعيل البيئة الافتراضية، استخدم النسخة المحلية من الأمر pip لتثبيت كلٍّ من جانغو وغوني كورن ويوفي كورن وموائم PostgreSQL الذي اسمه "psycopg2".

ملاحظة: استخدم pip بدلًا من pip3 عند تفعيل البيئة الافتراضية، أي عندما يكون المُحث مسبوقًا بالاسم "(myprojectenv)"، حتى لو كنت تستخدم بايثون 3، إذ يُطلق دائمًا يطلق على نسخة الأداة للبيئة الافتراضية اسم pip بغض النظر عن إصدار بايثون.

(myprojectenv) $ pip install django gunicorn uvicorn psycopg2-binary

وبهذا يكون أصبح لديك كل البرمجيات اللازمة لبدء مشروع جانغو.

الخطوة الرابعة - إنشاء وضبط مشروع جانغو جديد

يمكنك الآن بعد الانتهاء من تثبيت مكونات بايثون أن تنشئ ملفات مشروع جانغو الفعلية.

إنشاء مشروع جانغو

نظرًا لأن مجلد المشروع موجود فعلًا، يمكنك أن تطلب من جانغو أن يثبت الملفات فيه، إذ سينشئ مجلدًا في المستوى الثاني مع الشيفرة الفعلية، وهو أمر طبيعي، ويضع سكريبت script الإدارة في هذا المجلد. الفكرة الرئيسية وراك ذلك هو أن تعرّف المجلد صراحةً بدلًا من ترك المجال لجانغو باتخاذ قرارات لها علاقةٌ بمجلدك الحالي نيابةً عنك:

(myprojectenv) $ django-admin startproject myproject ~/myprojectdir

الآن، لا بُدّ أن يحتوي مجلد المشروع ("myprojectdir/~" في مقالتنا) على المكونات التالية:

  • "myprojectdir/manage.py/~": سكربت إدارة مشروع جانغو.
  • "/myprojectdir/myproject/~": حزمة مشروع جانغو التي يجب أن تحتوي على الملفات: "init__.py__" و "asgi.py" و "settings.py" و "urls.py" و "wsgi.py".
  • "/myprojectdir/myprojectenv/~": مجلد البيئة الافتراضية الذي أنشأته من قبل.

ضبط إعدادات المشروع

ستحتاج بعد إنشاء ملفات المشروع إلى ضبط بعض الإعدادات. افتح ملف الإعدادات بأي محرر نصوص تشاء، واستخدمنا هنا محرر النصوص نانو nano:

(myprojectenv) $ nano ~/myprojectdir/myproject/settings.py

ابحث في الملف عن الموجه ALLOWED_HOSTS الذي يعرّف قائمةً بعناوين وأسماء النطاقات للخادم التي يمكن أن تُستخدم للاتصال بنسخة جانغو. لذا ستؤدي أي طلبات واردة بترويسة مضيف وغير موجودة ضمن تلك القائمة إلى ظهور استثناء exception، ويطلب جانغو منك هذا منعًا لصنف معين من الثغرات الأمنية.

أدرج داخل الأقواس المربعة قائمة عناوين IP أو أسماء النطاقات المرافقة لخادم جانغو، وضع كل عنصر بين علامتي اقتباس مع الفصل بين كل عنصرين بفاصلة. للسماح بنطاق كامل وأي نطاقات فرعية، ضع نقطةً في بداية العنصر. لفهم هذا الكلام وضعنا في الشيفرة التالية بعض الأمثلة وسبقناها بعلامة التعليق لتُعامل مثل تعليقات.

ملاحظة: تأكد من إضافة localhost ضمن القائمة لأنك ستحتاج إلى خادم وكيل من أجل الاتصالات من خلال نسخة إنجن إكس محلية.

. . .
# 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']

ثم اعثر على القسم الذي يضبط الوصول إلى قاعدة البيانات، إذ سيبدأ بـكلمة "DATABASES". الضبط الموجود في الملف يخص قاعدة بيانات SQLite التي عدَلنا عن استخدامها إلى البديل PostgreSQL لذا لا بد من ضبط الإعدادات.

غيّر الإعدادات بمعلومات قاعدة بيانات PostgreSQL، وأخبر جانغو بأن يستخدم موائم psycopg2 الذي ثبتَّه بواسطة pip. سيُطلب منك التزويد باسم قاعدة البيانات واسم مستخدم قاعدة البيانات وكلمة المرور لمستخدم قاعدة البيانات، ثم تحديد أن قاعدة البيانات موجودة على الجهاز المحلي؛ أما إعدادات المنفذ PORT، فيمكنك تركها سلسلةً نصيةً فارغة.

. . .

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}

. . .

انتقل بعد ذلك إلى آخر الملف وأضف إعدادًا يشير إلى الموضع الذي يجب أن توضع فيه الملفات الساكنة، وهذا ضروري لكي يتمكن إنجن إكس من معالجة الطلبات على هذه العناصر. يطلب السطر التالي من جانغو أن يضعهم في مجلد اسمه "static" في مجلد المشروع الأساسي:

. . .

STATIC_URL = '/static/'
import os
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

احفظ وأغلق الملف عندما تنتهي.

إتمام الإعداد الأولي للمشروع

يمكنك الآن تهجير migrate مخطط قاعدة البيانات الأولي إلى قاعدة بيانات PostgreSQL باستخدام سكريبت الإدارة:

(myprojectenv) $ ~/myprojectdir/manage.py makemigrations
(myprojectenv) $ ~/myprojectdir/manage.py migrate

أنشئ مستخدمًا إدرايًّا للمشروع على النحو التالي:

(myprojectenv) $ ~/myprojectdir/manage.py createsuperuser

ستحتاج إلى التزويد باسم مستخدم وبريد إلكتروني وتختار كلمة مرور ثم تؤكدها.

يمكنك أن تجمع كل المحتوى الساكن في موقع المجلد الذي هيّأته وذلك على النحو التالي:

(myprojectenv) $ ~/myprojectdir/manage.py collectstatic

ستحتاج إلى تأكيد العملية. بعد ذلك ستُوضع الملفات الساكنة في مجلد اسمه "static" ضمن مجلد المشروع.

يُفترض إذا كنت قد اتبعت دليل إعداد الخادم الأولي أن يكون لديك جدار ناري UFW يحمي خادمك، وستحتاج للسماح بالوصول إلى المنفذ الذي تستخدمه إذا أردت اختبار خادم التطوير.

أنشئ استثناءً للمنفذ 8000 على النحو التالي:

(myprojectenv) $ sudo ufw allow 8000

يمكنك الآن وأخيرًا اختبار مشروعك بتشغيل خادم تطوير جانغو على النحو التالي:

(myprojectenv) $ ~/myprojectdir/manage.py runserver 0.0.0.0:8000

اذهب إلى اسم نطاق الخادم الذي تستخدمه أو عنوان IP متبوعًا برقم المنفذ 8000 في متصفحك:

http://server_domain_or_IP:8000

يفترض الآن أن تظهر لك صفحة فهرس جانغو الافتراضية:

صفحة فهرس جانغو الافتراضية

سيُطلب منك إدخال اسم المستخدم وكلمة المرور اللذين أنشأتهما بالأمر createsuperuser،إذا ألحقت "admin/" بنهاية عنوان URL في شريط العنوان :

إدخال اسم المستخدم وكلمة المرور المنشأتين بالأمر createsuperuser

يمكنك بعد انتهاء التحقق الوصولُ إلى الواجهة الافتراضية لإدارة جانغو:

الواجهة الافتراضية لإدارة جانغو

اضغط على المفتاحين "CTRL+C" بعد انتهاء عملية الاستكشاف في النافذة الطرفية لإيقاف تشغيل خادم التطوير.

اختبار قدرة خادم غوني كورن على تخديم المشروع

سنستخدم في مقالتنا هذه خادم "غوني كورن Gunicorn" جنبًا إلى جنب مع خادم "يوفي كورن Uvicorn" لتشغيل التطبيق. ومع أن غوني كورن يُستخدم عادةً لنشر تطبيقات WSGI لكنه يُزوَّد بواجهة قابلة للتوصيل pluggable لتأمين نشر ASGI، وهو يفعل هذا من خلال السماح لك باستهلاك صنف عامل يظهر بواسطة خادم ASGI (وهو يوفي كورن). ونظرًا لأن الخادم "غوني كورن" هو منتج أعلى نضجًا من الخادم "يوفي كورن" ويزود بضبطٍ أكثر منه، ينصح أنصار الخادم "يوفي كورن" باستخدام الخادم "غوني كورن" مع الصنف العامل للخادم "يوفي كورن" لأنه خادم ومدير عمليات ذي مزايا مكتملة.

ستفحص خادم غوني كورن قبل مغادرة البيئة الافتراضية لتتأكد من أن بإمكانه تخديم التطبيق.

الآن لكي تستخدم الأصناف العاملة لخادم يوفي كورن مع خادم غوني كورن، ادخل إلى مجلد المشروع واستخدم الأمر gunicorn التالي لتحميل وحدة ASGI للمشروع.

(myprojectenv) $ cd ~/myprojectdir
(myprojectenv) $ gunicorn --bind 0.0.0.0:8000 myproject.asgi -w 4 -k uvicorn.workers.UvicornWorker

سيشغّل هذا الأمر خادم غوني كورن على نفس الواجهة التي كان يعمل عليها خادم تطوير جانغو. يمكنك الآن العودة واختبار التطبيق من جديد.

ملاحظة: ليس مطلوبًا استخدام خادم غوني كورن لتشغيل تطبيق ASGI، ولكي تستخدم فقط خادم يوفي كورن، استعن بالأمر التالي:

uvicorn myproject.asgi:application --host 0.0.0.0 --port 8080

ملاحظة: لن يكون في واجهة الإدارة أي تنسيقات CSS مطبقة، لأن الخادم غوني كورن لا يعرف كيف يعثر على محتوى CSS الساكن المسؤول عن هذا.

إذا كانت صفحة ترحيب جانغو لا تزال ظاهرة حتى بعد تنفيذ هذه الأوامر، فإن هذا يعدّ تأكيدًا على أن الخادم غوني كورن قد خدّم الصفحة فعلًا وأنه يعمل على النحو المطلوب، وما قد حصل هو أنك عندما استخدمت الأمر gunicorn، فقد مرّرت وحدة module إلى خادم غوني كورن بتحديد مسار المجلد ذي الصلة إلى الملف asgi.py الخاص بـجانغو والذي يعدّ نقطة الدخول إلى تطبيقك، باستخدام صيغة وحدة بايثون. عُرّف داخل هذا الملف دالةٌ اسمها "application"، ستُستخدم للتواصل مع التطبيق.

إذا أردت معرفة المزيد عن مواصفات ASGI، زر الموقع الرسمي للخادم ASGI.

اضغط بعد انتهائك من الاختبار على المفتاحين "CTRL+C" في نافذة الطرفية لإيقاف تشغيل خادم غوني كورن.

وبهذا تكون انتهيت من ضبط تطبيق جانغو، ويمكنك الآن الخروج من البيئة الافتراضية بكتابة ما يلي:

(myprojectenv) $ deactivate

عندئذٍ سيُحذف مؤشر البيئة الافتراضية في محث النافذة التي تعمل فيها.

الخطوة الخامسة - إنشاء مقبس systemd وملفات خدمة لـخادم غوني كورن

تأكدتَ في القسم السابق من قدرة خادم غوني كورن على التفاعل مع تطبيق جانغو، وستنفّذ في الخطوة الحالية طريقةً أقوى لتشغيل وإطفاء خادم التطبيقات عبر إنشاء خدمة systemd وملفات المقبس socket files.

سيُنشأ مقبس خادم غوني كورن عند الإقلاع وسيستمع إلى الاتصالات، وعندما يحدث اتصال ستشغِّل خدمة systemd تلقائيًّا عمليةَ الخادم غوني كورن لتولي مهمة التعامل مع الاتصال.

الآن ابدأ بإنشاء ملف مقبس systemd لخادم غوني كورن وافتحه بمحرر النصوص الذي تريد وبصلاحيات sudo كما يلي:

$ sudo nano /etc/systemd/system/gunicorn.socket

ستنشئ في داخل الملف القسم [Unit] لوصف المقبس والقسم [Socket] لتعريف موضع المقبس والقسم [Install] للتأكد أن المقبس يُنشأ في الوقت المناسب:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

احفظ الملف، ثم أغلقه عندما تنتهي. بعد ذلك أنشئ ملف خدمة systemd للخادم غوني كورن، وافتحه بصلاحيات sudo في محرر النصوص الذي تستخدمه، وتنبه إلى أنه يجب أن يطابق اسمُ ملف الخدمة استثناءَ اسم ملف المقبس للامتداد:

$ sudo nano /etc/systemd/system/gunicorn.service

ابدأ بالقسم [Unit] الذي يستخدم لتحديد البيانات الوصفية metadata والاعتماديات. ستحدد هنا وصفًا لخدمتك وتخبر نظام التهيئة init system أن يشغل هذا فقط عند الوصول إلى مستوى التشبيك المستهدف، ونظرًا لأن الخدمة تعتمد على المقبس والموجود وصفه في ملف المقبس، فستحتاج إلى تضمين الموجّه Requires للإشارة إلى تلك العلاقة:

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

افتح بعد ذلك القسم [Service]، إذ ستحدّد في هذا القسم المستخدم والمجموعة اللتين تريد أن تُشغّل العملية من أجلهما. ستمنح حساب المستخدم النظامي ملكيةَ العملية لامتلاكه كافة الملفات اللازمة، وستعطي ملكية المجموعة للمجموعة "www-data" ليتمكنَ الخادم إنجن إكس من التواصل بسهولة مع خادم غوني كورن.

بعد ذلك، ستعدّ المجلد العامل وتحدد الأمر الذي ستستخدمه لتشغيل الخدمة، إذ ستحتاج في حالتنا هذه لتحديد المسار الكامل للملف التنفيذي لخادم غوني كورن المثبت ضمن البيئة الافتراضية. ستربِط العملية مع مقبس يونيكس Unix الذي أنشأته ضمن المجلد "run/" لتتمكن العملية من التواصل مع الخادم إنجن إكس، وستُرسل كل سجلات البيانات إلى جهاز الخرج القياسي لتتمكن العملية journald من جمع سجلات الخادم غوني كورن. يمكنك أيضًا أن تحدد أي إعدادات من اختيارك للخادم غوني كورن. وفيما يلي مثالٌ عن تحديد ثلاث عمليات عاملة:

[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 - \
          -k uvicorn.workers.UvicornWorker \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myproject.asgi:application

القسم الأخير هو [Install]، الذي سيخبر systemd عما سيربطه مع هذه الخدمة إذا اخترنا تشغيلها عند الإقلاع. سترغب غالبًا بتشغيل هذه الخدمة عندما يكون نظام المتعدد المستخدمين في حالة عمل:

[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 - \
          -k uvicorn.workers.UvicornWorker \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myproject.asgi:application

[Install]
WantedBy=multi-user.target

وبهذا يكون ملف خدمة systemd قد اكتمل. بإمكانك الآن أن تحفظه وتغلقه.

يمكنك الآن تشغيل وتفعيل مقبس الخادم غوني كورن، إذ سينشئ هذا ملف المقبس في المسار "run/gunicorn.sock/" الآن وعند الإقلاع. عندما يُنشأ اتصال مع ذلك المقبس، ستشغّل systemd الخدمة "gunicorn.service" تلقائيًا لمعالجتها.

$ sudo systemctl start gunicorn.socket
$ sudo systemctl enable gunicorn.socket

الآن وبعد أن أنشأت ملفات المقبس وخدمة 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

إذا أشار الأمر systemctl status إلى أن خطأً ما قد حدث، أو إذا لم تجد الملف gunicorn.sock في المجلد، سيدّل هذا على أن مقبس غوني كورن لم يُنشأ على نحوٍ صحيح. تفّقد سجلات مقبس غوني كورن بالأمر التالي:

$ sudo journalctl -u gunicorn.socket 

ألق نظرةً أخرى على الملف في الرابط التالي لإصلاح أيّ مشاكل قبل المتابعة:

"etc/systemd/system/gunicorn.socket/"

الخطوة السابعة - فحص تنشيط المقبس

ستفحص في هذه الخطوة تنشيط المقبس، فإذا كنت في الوقت الحالي قد شغّلت فقط الوحدة 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 من تطبيقك في الطرفية، والذي يشير إلى أن خادم غوني كورن قد شُغّل وأنه كان قادرًا على تخديم تطبيق جانغو الذي تعمل عليه. يمكن فحص ما إذا كانت خدمة غوني كورن في وضع التشغيل بكتابة الأمر التالي:

$ sudo systemctl status gunicorn

وسيكون الخرج على النحو التالي:

 gunicorn.service - gunicorn daemon
     Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
     Active: active (running) since Thu 2021-06-10 21:03:29 UTC; 13s ago
TriggeredBy:  gunicorn.socket
   Main PID: 11682 (gunicorn)
      Tasks: 4 (limit: 4682)
     Memory: 98.5M
     CGroup: /system.slice/gunicorn.service
             ├─11682 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 -k uvicorn.workers.UvicornWorker --bind unix:/run/gunicorn.sock myproject.asgi:application
             ├─11705 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 -k uvicorn.workers.UvicornWorker --bind unix:/run/gunicorn.sock myproject.asgi:application
             ├─11707 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 -k uvicorn.workers.UvicornWorker --bind unix:/run/gunicorn.sock myproject.asgi:application
             └─11708 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 -k uvicorn.workers.UvicornWorker --bind unix:/run/gunicorn.sock myproject.asgi:application

Jun 10 21:03:29 django gunicorn[11705]: [2021-06-10 21:03:29 +0000] [11705] [INFO] ASGI 'lifespan' protocol appears unsupported.
Jun 10 21:03:29 django gunicorn[11705]: [2021-06-10 21:03:29 +0000] [11705] [INFO] Application startup complete.
Jun 10 21:03:30 django gunicorn[11707]: [2021-06-10 21:03:30 +0000] [11707] [INFO] Started server process [11707]
Jun 10 21:03:30 django gunicorn[11707]: [2021-06-10 21:03:30 +0000] [11707] [INFO] Waiting for application startup.
Jun 10 21:03:30 django gunicorn[11707]: [2021-06-10 21:03:30 +0000] [11707] [INFO] ASGI 'lifespan' protocol appears unsupported.
Jun 10 21:03:30 django gunicorn[11707]: [2021-06-10 21:03:30 +0000] [11707] [INFO] Application startup complete.
Jun 10 21:03:30 django gunicorn[11708]: [2021-06-10 21:03:30 +0000] [11708] [INFO] Started server process [11708]
Jun 10 21:03:30 django gunicorn[11708]: [2021-06-10 21:03:30 +0000] [11708] [INFO] Waiting for application startup.
Jun 10 21:03:30 django gunicorn[11708]: [2021-06-10 21:03:30 +0000] [11708] [INFO] ASGI 'lifespan' protocol appears unsupported.
Jun 10 21:03:30 django gunicorn[11708]: [2021-06-10 21:03:30 +0000] [11708] [INFO] Application startup complete.

إذا كان الخرج من curl أو الخرج من systemctl status يشير إلى أن مشكلة ما قد حدثت فراجع السجلات للحصول على تفاصيل أكثر كما يلي:

$ sudo journalctl -u gunicorn

افحص الملف "etc/systemd/system/gunicorn.service/" بحثًا عن أي مشكلات، وإذا أجريت أي تغييرات على هذا الملف، فأعد تحميل العملية الخفية daemon لكي تعيد قراءة تعريف الخدمة، وأعد تشغيل العملية غوني كورن بكتابة ما يلي:

$ sudo systemctl daemon-reload
$ sudo systemctl restart gunicorn

تأكد من فحص المسائل المذكورة أعلاه بحثًا عن أي أخطاء قبل المتابعة.

الخطوة الثامنة - ضبط إنجن إكس لتمرير الخادم الوكيل إلى غوني كورن

ستحتاج الآن بعد أن فرغت من إعداد غوني كورن إلى ضبط إنجن إكس Nginx لتمرير البيانات إلى العملية. ستعد في هذه الخطوة إنجن إكس أمام غوني كورن لتستفيد من آليات معالجة الاتصال عالية الأداء والمزايا الأمنية سهلة التنفيذ التي يتمتع بها.

ابدأ العمل بإنشاء وفتح كتلة خادم جديد في مجلد sites-available الخاص بخادم إنجن إكس:

$ sudo nano /etc/nginx/sites-available/myproject

افتتح في الداخل كتلة خادم جديدة، وحدّد أن على هذه الكتلة الاستماع إلى المنفذ الاعتيادي 80 والاستجابة لاسم نطاق الخادم أو عنوان IP، كما يلي:

server {
    listen 80;
    server_name server_domain_or_IP;
}

ثم اطلب من إنجن إكس أن يتجاهل أي مشاكل بخصوص العثور على أيقونة مفضلة favicon، وأخبره أيضًا أين سيجد الأصول الساكنة static assets التي جمعتها في المجلد "myprojectdir/static/~". تمتلك جميع هذه الملفات سابقة URI prefix معيارية هي "static/"، لذا يمكنك أن تنشئ كتلة موقع location لوضع هذه الطلبات:

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 المُضمَّن مع تثبيت إنجن إكس، ثم مرّر حركة المرور traffic مباشرةً إلى مقبس غوني كورن :

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

تأكد من خلو ضبط إنجن إكس من الأخطاء الكتابية بكتابة ما يلي:

$ sudo nginx -t

إذا لم يُبلَّغ عن أي أخطاء، استمر وأعد تشغيل إنجن إكس من خلال كتابة:

$ sudo systemctl restart nginx

أخيرًا ستحتاج إلى فتح الجدار الناري أمام حركة المرور العادية على المنفذ 80، ونظرًا لعدم وجود حاجة إلى الوصول إلى خادم التطوير، فيمكنك إزالة القاعدة لفتح المنفذ 8000 أيضًا:

$ sudo ufw delete allow 8000
$ sudo ufw allow 'Nginx Full'

يفترض أن تكون الآن قادرًا على الذهاب إلى نطاق خادمك أو عنوان IP له لرؤية صفحة الترحيب لجانغو التي تظهر عليها صورة الصاروخ.

ملاحظة: الخطوة التالية بعد أن تفرغ من إعداد إنجن إكس هي تأمين حركة المرور الذاهبة إلى الخادم باستخدام SSL/TLS؛ وهذا مهم إذ ستُرسل جميع المعلومات بما فيها كلمات المرور عبر الشبكة بصيغة نص صرف plain text دون استخدامه، وأبسط طريقة للحصول على شهادة SSL لتأمين المرور إذا كان لديك اسم نطاق هي استخدام موقع Let's Encrypt. اتبع هذا الدليل لإعداد Let's Encrypt مع إنجن إكس على أوبنتو 20.04، واتبع خطوات الإجراء باستخدام كتلة خادم إنجن إكس التي أنشأتها في هذه المقالة.

الخطوة التاسعة - استكشاف أخطاء إنجن إكس وغوني كورن وإصلاحها

إذا لم تُظهِر الخطوة الأخيرة تطبيقك، فستحتاج حينئذٍ إلى استكشاف الأخطاء في التثبيت وإصلاحها. وفيما يلي نعرض أبرز الأخطاء التي يمكن أن تظهر:

يظهر إنجن إكس الصفحة الافتراضية بدلًا من إظهار تطبيق جانغو

إذا كان إنجن إكس يعرض الصفحة الافتراضية بدلًا من الإحالة إلى تطبيقك، فهذا يعني غالبًا أنك تحتاج إلى ضبط قيمة "server_name" ضمن الملف:

"etc/nginx/sites-available/myproject/"

بحيث يشير إلى عنوان IP أو اسم النطاق للخادم؛ إذ يستخدم إنجن إكس القيمة "server_name لتحديد كتلة الخادم التي سيستخدمها للاستجابة للطلبات. إذا استلمت صفحة إنجن إكس الافتراضية، فهذه إشارة إلى أن إنجن إكس لم يكن قادرًا على مطابقة الطلب مع كتلة الخادم صراحةً ولذلك انسحب إلى الكتلة الافتراضية المعرّفة في الملف:

"etc/nginx/sites-available/default/"

يجب أن يكون اسم الخادم "server_name" في كتلة خادم المشروع محددًا أكثر من الموجود في كتلة الخادم الافتراضي الذي جرى اختياره.

يعرض إنجن إكس الخطأ 502 Bad Gateway Error بدلا من عرض تطبيق جانغو

يشير الخطأ 502 إلى أن إنجن إكس غير قادر على إحالة الطلب. كثيرًا ما تُظهِر مشكلات الضبط نفسها بإبراز الخطأ 502، لذلك فإننا نحتاج إلى مزيد من المعلومات لنستشكف الأخطاء ونصلحها بالطريقة الصحيحة.

وأول مكان يجب أن نبحث فيه عن مزيد من المعلومات هو سجل أخطاء إنجن إكس، الذي سيخبرك عمومًا عن الظروف التي سببت المشكلات أثناء حدث الإحالة. تابع سجلات الخطأ لإنجن إكس بكتابة ما يلي:

$ sudo tail -F /var/log/nginx/error.log

والآن أنشئ طلبًا جديدًا في متصفحك لتوليد خطأ جديد (حاول إنعاش refresh الصفحة). يفترض أن تستلم رسالة خطأ جديدة تظهر في السجل، وإذا نظرت إلى الرسالة فيفترض أن تساعدك على تضييق نطاق المشكلة.

قد تستلم الرسالة التالية:

connect() to unix:/run/gunicorn.sock failed (2: No such file or directory)

يشير هذا إلى أن إنجن إكس لم يستطع العثور على الملف "gunicorn.sock" في الموضع المُعطى. ينبغي أن تقارن موقع "proxy_pass" المعرف ضمن الملف "etc/nginx/sites-available/myproject/" مع الموقع الفعلي للملف "gunicorn.sock" الذي ولدته وحدة "gunicorn.socket" التابعة لخدمة systemd.

إذا لم تعثر على الملف ضمن المجلد، فهذا يعني غالبًا أن ملف مقبس خدمة systemd لم يستطع إنشاءه، لذلك عُد إلى القسم الذي فحصنا فيه ملف المقبس لغوني كورن لكي تتابع في خطوات استكشاف أخطاء غوني كورن وإصلاحها.

connect() to unix:/run/gunicorn.sock failed (13: Permission denied)

يشير هذا إلى أن إنجن إكس لم يستطع الاتصال بمقبس غوني كورن بسبب مشكلات تتعلق بالصلاحيات، إذ يمكن أن يحدث هذا عندما يستخدِم الإجراءُ المستخدِم الجذر بدلًا من مستخدم "sudo"؛ فمع قدرة systemd على إنشاء ملف مقبس غوني كورن، لا يكون إنجن إكس قادرًا على الوصول إليه.

يمكن أن يحدث هذا إذا كان هناك أذونات محدودة في أي نقطة بين المجلد الجذر "/" والملف "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 بدلًا من ---). ينبغي أن تكون العملية إنجن إكس قادرة على الوصول إلى المقبس بنجاح.

إذا لم تمتلك أي من المجلدات التي تؤدي إلى المقبس أذونات قراءة وتنفيذ عامّة، لن يتمكن إنجن إكس من الوصول إلى المقبس دون السماح بأذونات قراءة وتنفيذ عامة، أو التأكد من أن ملكية المجموعات معطاة لمجموعة يكون إنجن إكس جزءًا منها.

جانغو يعرض رسالة could not connect to server: Connection refused

إحدى الرسائل المتوقع أن تتلقاها من جانغو عند محاولة الوصول إلى أجزاء من التطبيق في متصفح الويب هي:

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. تأكد من أن نسخة Postgres تعمل بكتابة ما يلي:

$ sudo systemctl status postgresql

إذا لم تكن تعمل، يمكنك تشغيلها وتفعيلها تلقائيًا عند الإقلاع -إذا لم تكن مضبوطة مسبقًا لذلك- من خلال كتابة ما يلي:

$ sudo systemctl start postgresql
$ sudo systemctl enable postgresql

إذا بقي لديك مشكلات، تأكد من أن إعدادات قاعدة البيانات المعرفة في الملف التالي صحيحة:

"myprojectdir/myproject/settings.py/~"

المزيد من استكشاف الأخطاء وإصلاحها

لمزيد من استكشاف الأخطاء وإصلاحها، يمكنك الاستعانة بالسجلات لأنها تساعد في تضييق نطاق الأسباب الجذرية. افحص كلًا منها بالترتيب وابحث عن رسائل تشير إلى مناطق المشكلة.

يمكن أن تساعدك السجلات التالية:

  • افحص سجلات العملية إنجن إكس من خلال كتابة الأمر التالي:
sudo journalctl -u nginx
  • افحص سجلات الوصول إلى إنجن إكس من خلال كتابة الأمر التالي:
sudo less /var/log/nginx/access.log
  • افحص سجلات أخطاء إنجن إكس من خلال كتابة الأمر التالي:
sudo less /var/log/nginx/error.log
  • افحص سجلات تطبيق غوني كورن من خلال كتابة الأمر التالي:
sudo journalctl -u gunicorn
  • افحص سجلات مقبس غوني كورن من خلال كتابة الأمر التالي:
sudo journalctl -u gunicorn.socket

أثناء تحديثك للضبط أو التطبيق الخاص بك، ستحتاج غالبًا لإعادة تشغيل العمليات لتُضبط مع التغييرات التي أجريتها.

إذا حدّثتَ تطبيق جانغو يمكنك إعادة تشغيل عملية "غوني كورن" لتطبيق التغييرات من خلال كتابة:

$ sudo systemctl restart gunicorn

إذا غيرت مقبس غوني كورن أو ملفات الخدمة، أعد تحميل العفريت daemon ثم أعد تشغيل العملية من خلال كتابة:

$ sudo systemctl daemon-reload
$ sudo systemctl restart gunicorn.socket gunicorn.service

إذا غيّرت ضبط كتلة الخادم إنجن إكس، افحص الضبط ثم إنجن إكس من خلال كتابة:

$ sudo nginx -t && sudo systemctl restart nginx

ستساعدك هذه الأوامر لتطبيق التغييرات التي تجريها أثناء الضبط.

الخاتمة

أعددت في هذا الدليل مشروع جانغو لخادم ASGI في بيئته الافتراضية الخاصة، وهيأت خادمي غوني كورن ويوفي كورن لترجمة طلبات العميل بنسق غير متزامن ليتمكن جانغو من التعامل مع هذه الطلبات. بعد ذلك، أعددت إنجن إكس ليأخذ دور خادم وكيل عكسي لمعالجة اتصالات العميل وخدمة المشروع الصحيح اعتماداً على طلب العميل.

يبسّط جانغو عملية إنشاء الكائنات والتطبيقات من خلال تزويدك بكثير من القطع المشتركة، مما يسمح لك بالتركيز على العناصر المتفردة unique. وهكذا بالاستفادة من سلسلة الأدوات العامة التي وردت في هذه المقالة، أصبح بإمكانك خدمة التطبيقات التي ستنشئها مستقبلًا من خادم وحيد.

ترجمة - وبتصرف - للمقالة How To Set Up an ASGI Django App with Postgres, Nginx, and Uvicorn on Ubuntu 20.04 لصاحبيها Mason Egger و Erin Glass.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...