المقدمة
في هذا الدليل، سنقوم بإعداد تطبيق بسيط WSGI يخدمه uWSGI. سنستخدم خادم الويب Nginx كبروكسي عكسي لخادم التطبيق لتوفير معالجة اتصال أكثر قوة. سنقوم بتثبيت وتكوين هذه المكونات على خادم Ubuntu 14.04.
التعاريف والمفاهيم
توضيح بعض المصطلحات
قبل أن نبدأ، يجب أن نتناول بعض المصطلحات المربكة المرتبطة بالمفاهيم المترابطة التي سنتعامل معها. هناك ثلاث مصطلحات منفصلة تظهر قابلة للتبادل، ولكن لها معانٍ متميزة:
- WSGI: مواصفة بيثون تعرف واجهة قياسية للتواصل بين تطبيق أو إطار عمل وخادم تطبيق/ويب. تم إنشاؤها لتبسيط وتوحيد التواصل بين هذه المكونات من أجل التوافق والتبادلية. يعرف هذا أساسا واجهة برمجة تطبيقات يمكن استخدامها عبر بروتوكولات أخرى.
- uWSGI: خادم تطبيقات يهدف إلى توفير مجموعة كاملة لتطوير ونشر تطبيقات الويب والخدمات. العنصر الرئيسي هو خادم التطبيقات الذي يمكنه التعامل مع التطبيقات بلغات مختلفة. يتواصل مع التطبيق باستخدام الطرق المحددة في مواصفات WSGI ومع خوادم الويب الأخرى عبر مجموعة متنوعة من البروتوكولات الأخرى. هذه هي القطعة التي تترجم الطلبات من خادم الويب التقليدي إلى تنسيق يمكن للتطبيق معالجته.
- uwsgi: بروتوكول سريع وثنائي يتم تنفيذه بواسطة خادم uWSGI للتواصل مع خادم الويب الأكثر تميزًا بالمزايا. هذا هو بروتوكول الأسلاك، وليس بروتوكول النقل. إنه الطريقة المفضلة للتحدث إلى خوادم الويب التي تقوم بوساطة الطلبات إلى uWSGI.
متطلبات تطبيق WSGI
تحدد مواصفات WSGI الواجهة بين خادم الويب وأجزاء التطبيق في الكومة. في هذا السياق، “خادم الويب” يشير إلى خادم uWSGI، الذي يتولى ترجمة طلبات العميل إلى التطبيق باستخدام مواصفات WSGI. يبسط هذا التواصل وينشئ مكوناتٍ فصليةً بحيث يمكنك تبديل أي جانب بسهولة دون الكثير من المشاكل.
يجب أن يكون لخادم الويب (uWSGI) القدرة على إرسال الطلبات إلى التطبيق عن طريق تشغيل “callable” محدد. يعد الـ callable نقطة الدخول ببساطة إلى التطبيق حيث يمكن لخادم الويب استدعاء وظيفة مع بعض المعلمات. المعلمات المتوقعة هي قاموس للمتغيرات البيئية و callable المقدم من جانب مكون خادم الويب (uWSGI).
بالرد على ذلك، يقوم التطبيق بإرجاع كائن يمكن استخدامه لتوليد جسم استجابة العميل. كما سيقوم بالاتصال بـ callable المكون لخادم الويب الذي تلقاه كمعلمة. ستكون المعلمة الأولى عند تشغيل callable خادم الويب هي رمز حالة HTTP والثانية ستكون قائمة من الأزواج، يعرف كل منها رأس الاستجابة وقيمته التي سيتم إرسالها مرة أخرى إلى العميل.
مع مكون “خادم الويب” في هذا التفاعل المقدم من خلال uWSGI في هذه الحالة، سنحتاج فقط إلى التأكد من أن تطبيقاتنا تتمتع بالصفات الموصوفة أعلاه. كما سنقوم بإعداد Nginx للتعامل مع طلبات العميل الفعلية وتوجيهها إلى خادم uWSGI.
تثبيت المكونات
للبدء، سنحتاج إلى تثبيت المكونات اللازمة على خادم Ubuntu 14.04 الخاص بنا. يمكننا القيام بذلك أساسًا باستخدام apt
و pip
.
أولاً، قم بتحديث فهرس الحزم الخاص بك باستخدام apt
ومن ثم قم بتثبيت مكتبات التطوير والرؤوس الخاصة بـ Python، ومدير حزم Python pip
، وخادم الويب Nginx والوكيل العكسي:
sudo apt-get update
sudo apt-get install python-dev python-pip nginx
بمجرد اكتمال تثبيت الحزمة، ستكون لديك وصول إلى مدير الحزم pip
للبايثون. يمكننا استخدام ذلك لتثبيت حزمة virtualenv
، التي سنستخدمها لعزل بيئة البايثون لتطبيقنا عن أي بيئات أخرى قد تكون موجودة على النظام:
sudo pip install virtualenv
بمجرد الانتهاء من هذا، يمكننا البدء في إنشاء الهيكل العام لتطبيقنا. سنقوم بإنشاء البيئة الافتراضية المناقشة أعلاه وسنقوم بتثبيت خادم التطبيقات uWSGI ضمن هذه البيئة:
إعداد مجلد التطبيق وبيئة افتراضية
سنبدأ بإنشاء مجلد لتطبيقنا. يمكن أن يحتوي هذا المجلد على مجلد متداخل يحتوي على رمز التطبيق الفعلي في تطبيق أكثر اكتمالًا. بالنسبة لأغراضنا، سيحتوي هذا الدليل ببساطة على بيئتنا الافتراضية ونقطة الدخول WSGI الخاصة بنا:
mkdir ~/myapp/
بعد ذلك، انتقل إلى الدليل حتى نتمكن من إعداد البيئة لتطبيقنا:
cd ~/myapp
أنشئ بيئة افتراضية باستخدام الأمر virtualenv
. سنطلق عليها myappenv
للبساطة:
virtualenv myappenv
A new Python environment will be set up under a directory called myappenv
. We can activate this environment by typing:
source myappenv/bin/activate
يجب أن يتغير المؤشر الخاص بك ليشير إلى أنك الآن تعمل داخل البيئة الافتراضية. سيبدو شيئًا مشابهًا لهذا:
(myappenv)username@host:~/my_app$
إذا كنت ترغب في مغادرة هذه البيئة في أي وقت، يمكنك ببساطة كتابة:
deactivate
إذا كنت قد قمت بإلغاء تنشيط بيئتك، أعِد تنشيطها مرة أخرى للمتابعة مع الدليل.
بهذا البيئة النشطة، ستُحتجز جميع حزم Python المثبتة ضمن هرم الدليل هذا. لن تتداخل مع بيئة Python النظامية. بمراعاة هذا، يمكننا الآن تثبيت خادم uWSGI في بيئتنا باستخدام pip
. الحزمة المستخدمة لهذا تُسمى uwsgi
(هذا لا يزال خادم uWSGI وليس بروتوكول uwsgi
):
pip install uwsgi
يمكنك التحقق من توافره الآن عن طريق كتابة:
uwsgi --version
إذا عاد رقم الإصدار، فإن خادم uWSGI متاح للاستخدام.
إنشاء تطبيق WSGI
بعد ذلك، سنقوم بإنشاء تطبيق WSGI بسيط للغاية باستخدام متطلبات مواصفة WSGI التي ناقشناها سابقًا. لإعادة التأكيد، يجب أن يكون المكون التطبيق الذي يجب علينا توفيره يتمتع بالخصائص التالية:
-
– يجب أن يوفر واجهة من خلال استدعاء (دالة أو بنية لغوية أخرى يمكن استدعاؤها)
- – يجب أن يأخذ الاستدعاء كمعاملات قاموس يحتوي على أزواج مفتاح-قيمة شبيهة بالمتغيرات البيئية، واستدعاء قابل للوصول على الخادم (uWSGI).
- – يجب أن يعيد استدعاء التطبيق iterable ينتج الجسم الذي سيتم إرساله إلى العميل.
- – يجب أن يستدعي التطبيق الاستدعاء الخاص بالخادم الويب مع حالة HTTP ورؤوس الطلب.
سنكتب تطبيقنا في ملف يسمى wsgi.py
في دليل التطبيق الخاص بنا:
nano ~/myapp/wsgi.py
داخل هذا الملف، سنقوم بإنشاء أبسط تطبيق يتوافق مع WSGI. كما هو الحال مع جميع أكواد Python، تأكد من الانتباه إلى التباين:
الشفرة أعلاه تشكل تطبيق WSGI كاملاً. بشكل افتراضي، سيبحث uWSGI عن دالة تسمى application
، وهذا هو السبب في أننا قمنا بتسمية دالتنا application
. كما ترى، تأخذ دالتنا معلمتين.
المعلمة الأولى التي سميناها environ
لأنها ستكون مثل القاموس للمتغيرات البيئية. الثانية تسمى start_response
وهي الاسم الذي سيستخدمه التطبيق داخليًا للإشارة إلى دالة خادم الويب (uWSGI) التي يتم إرسالها. تم اختيار اسمي هذه المعلمتين ببساطة بسبب استخدامهما في الأمثلة الموجودة في مواصفات PEP 333 التي تعرف تفاعلات WSGI.
يجب على تطبيقنا أن يقوم بهذه المعلومات ويقوم بأمرين. أولاً، يجب عليه استدعاء الدالة التي تلقاها برمز حالة HTTP وأي رؤوس يريد إرسالها. في هذه الحالة، نقوم بإرسال استجابة “200 OK” ونقوم بتعيين رأس Content-Type
إلى text/html
.
ثانيًا، يحتاج إلى العودة بمتكرر لاستخدامه كجسم للاستجابة. هنا، اكتفينا باستخدام قائمة تحتوي على سلسلة واحدة من HTML. السلاسل هي متكررة أيضًا، ولكن داخل قائمة، سيتمكن uWSGI من معالجة السلسلة بأكملها بتكرار واحد.
في سيناريو العالم الحقيقي، من المحتمل أن يتم استخدام هذا الملف كرابط لبقية كود التطبيق الخاص بك. على سبيل المثال، تتضمن مشاريع Django ملف wsgi.py
بشكل افتراضي الذي يترجم الطلبات من خادم الويب (uWSGI) إلى التطبيق (Django). تظل واجهة WSGI المبسطة هي نفسها بغض النظر عن مدى تعقيد كود التطبيق الفعلي. هذه واحدة من قوى الواجهة.
احفظ وأغلق الملف عندما تنتهي.
للاختبار الكود، يمكننا تشغيل uWSGI. سنخبره بأن يستخدم HTTP في الوقت الحالي وأن يستمع على المنفذ 8080
. سنمرر له اسم السيناريو (تمت إزالة اللاحقة):
uwsgi --socket 0.0.0.0:8080 --protocol=http -w wsgi
الآن، إذا قمت بزيارة عنوان IP الخاص بخادمك أو اسم النطاق في متصفح الويب الخاص بك تليه :8080
، يجب أن ترى نص العنوان من المستوى الأول الذي مررناه كجسم في ملفنا wsgi.py
:
قم بإيقاف الخادم باستخدام CTRL-C عندما تتحقق من أن هذا يعمل.
لقد انتهينا من تصميم تطبيقنا الفعلي في هذه النقطة. يمكنك إلغاء تنشيط بيئتنا الظاهرية إذا رغبت في ذلك:
deactivate
قم بتكوين ملف تكوين uWSGI
في المثال أعلاه، بدأنا الخادم uWSGI يدوياً ومررنا له بعض المعلمات عبر سطر الأوامر. يمكننا تجنب ذلك عن طريق إنشاء ملف تكوين. يمكن لخادم uWSGI قراءة التكوينات بتنسيقات متنوعة، ولكننا سنستخدم تنسيق .ini
للبساطة.
لنواصل باستخدام التسمية التي استخدمناها حتى الآن، سنسمي الملف myapp.ini
ونضعه في مجلد التطبيق:
nano ~/myapp/myapp.ini
بداخله، نحتاج إلى إنشاء قسم يسمى [uwsgi]
. هذا القسم هو المكان الذي ستعيش فيه جميع عناصر التكوين الخاصة بنا. سنبدأ بتحديد تطبيقنا. يحتاج خادم uWSGI إلى معرفة مكان استدعاء التطبيق. يمكننا تحديد الملف والدالة داخله:
[uwsgi]
module = wsgi:application
نريد تحديد عملية uwsgi
الأولية كرئيسية ثم إنشاء عدد من عمليات العمل. سنبدأ بخمسة عمال:
[uwsgi]
module = wsgi:application
master = true
processes = 5
في الواقع، سنقوم بتغيير البروتوكول الذي يستخدمه uWSGI للتحدث مع العالم الخارجي. عندما كنا نقوم باختبار تطبيقنا، حددنا --protocol=http
حتى نتمكن من رؤيته من متصفح الويب. نظرًا لأننا سنقوم بتكوين Nginx كخادم وكيل عكسي أمام uWSGI، يمكننا تغيير هذا. يقوم Nginx بتنفيذ آلية توجيه بروكسي لـ uwsgi
، وهي بروتوكول ثنائي سريع يمكن أن يستخدمه uWSGI للتحدث مع خوادم أخرى. بروتوكول uwsgi
هو في الواقع البروتوكول الافتراضي لـ uWSGI، لذا من خلال حذف تحديد البروتوكول، سيعود تلقائيًا إلى uwsgi
.
بما أننا نقوم بتصميم هذا التكوين للاستخدام مع Nginx، فسنقوم أيضًا بتغيير استخدام منفذ الشبكة واستخدام مقبس Unix بدلاً من ذلك. هذا أكثر أمانًا وأسرع. سيتم إنشاء المقبس في الدليل الحالي إذا استخدمنا مسارًا نسبيًا. سنطلق عليه اسم myapp.sock
. سنقوم بتغيير الأذونات إلى “664” بحيث يمكن لـ Nginx الكتابة فيه (سنقوم ببدء uWSGI بمجموعة www-data
التي يستخدمها Nginx). سنضيف أيضًا الخيار vacuum
، الذي سيقوم بإزالة المقبس عند توقف العملية:
[uwsgi]
module = wsgi:application
master = true
processes = 5
socket = myapp.sock
chmod-socket = 664
vacuum = true
نحتاج إلى خيار نهائي لأننا سنقوم بإنشاء ملف Upstart لبدء تطبيقنا عند التمهيد. يوجد اختلاف في آراء Upstart و uWSGI حول ما يجب أن يفعله إشارة SIGTERM بالنسبة لتطبيق. لحل هذا الاختلاف حتى يمكن معالجة العمليات كما هو متوقع مع Upstart، نحتاج فقط إلى إضافة خيار يسمى die-on-term
بحيث يقوم uWSGI بإيقاف عملية البرنامج بدلاً من إعادة تحميله:
[uwsgi]
module = wsgi:application
master = true
processes = 5
socket = myapp.sock
chmod-socket = 664
vacuum = true
die-on-term = true
احفظ وأغلق الملف عند الانتهاء. هذا الملف التكويني معد الآن للاستخدام مع سكربت Upstart.
إنشاء ملف Upstart لإدارة التطبيق
يمكننا تشغيل نسخة uWSGI عند التمهيد بحيث يكون تطبيقنا متاحًا دائمًا. سنضع هذا في الدليل /etc/init
الذي يتحقق منه Upstart. سنطلق عليه اسم myapp.conf
:
sudo nano /etc/init/myapp.conf
أولاً، يمكننا البدء بوصف الخدمة واختيار مستويات تشغيل النظام التي يجب عليها التشغيل تلقائيًا فيها. تشمل مستويات التشغيل العادية للمستخدمين المستويات من 2 إلى 5. سنخبر Upstart بإيقاف الخدمة عندما تكون على أي مستوى تشغيل خارج هذه المجموعة (مثل عند إعادة تشغيل النظام أو في وضع المستخدم الواحد):
description "uWSGI instance to serve myapp"
start on runlevel [2345]
stop on runlevel [!2345]
بعد ذلك، سنخبر Upstart بالمستخدم والمجموعة التي يجب تشغيل العملية بها. نريد تشغيل التطبيق تحت حسابنا الخاص (نحن نستخدم demo
في هذا الدليل، ولكن يجب عليك استبدال اسم المستخدم الخاص بك). نريد تعيين المجموعة للمستخدم www-data
الذي يستخدمه Nginx ومع ذلك. هذا ضروري لأن خادم الويب يحتاج إلى القدرة على قراءة وكتابة على القابس الذي ستنشئه ملف .ini
الخاص بنا:
description "uWSGI instance to serve myapp"
start on runlevel [2345]
stop on runlevel [!2345]
setuid demo
setgid www-data
بعد ذلك، سنقوم بتشغيل الأوامر الفعلية لبدء uWSGI. نظرًا لأننا قمنا بتثبيت uWSGI في بيئة افتراضية، يتوجب علينا بذل بعض الجهد الإضافي. يمكننا ببساطة تقديم المسار الكامل إلى البرنامج التنفيذي uWSGI، ولكن بدلاً من ذلك، سنقوم بتنشيط البيئة الافتراضية. سيجعل ذلك الأمر أسهل إذا اعتمدنا على برنامج إضافي مثبت في البيئة.
للقيام بذلك، سنستخدم كتلة script
. بداخلها، سنغير إلى دليل التطبيق الخاص بنا، وننشط البيئة الافتراضية (يجب علينا استخدام .
في السيناريوهات بدلاً من source
)، ونبدأ نسخة uWSGI التي تشير إلى ملف .ini
الخاص بنا:
description "uWSGI instance to serve myapp"
start on runlevel [2345]
stop on runlevel [!2345]
setuid demo
setgid www-data
script
cd /home/demo/myapp
. myappenv/bin/activate
uwsgi --ini myapp.ini
end script
بهذا، يكتمل نص ال Upstart الخاص بنا. قم بحفظ الملف وإغلاقه عند الانتهاء.
الآن، يمكننا بدء الخدمة عن طريق كتابة:
sudo start myapp
يمكننا التحقق من أنه تم بدء التشغيل بكتابة:
ps aux | grep myapp
demo 14618 0.0 0.5 35868 5996 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14619 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14620 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14621 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14622 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 14623 0.0 0.5 42680 5532 ? S 15:02 0:00 uwsgi --ini myapp.ini
demo 15520 0.0 0.0 11740 936 pts/0 S+ 15:53 0:00 grep --color=auto myapp
سيبدأ هذا تلقائيًا عند التمهيد. يمكنك إيقاف الخدمة في أي وقت عن طريق كتابة:
sudo stop myapp
تكوين Nginx للوكيل لـ uWSGI
في هذه النقطة، لدينا تطبيق WSGI ولقد تم التحقق من قدرة uWSGI على قراءته وخدمته. لقد قمنا بإنشاء ملف تكوين ونص تشغيل Upstart. سيستمع عملنا لـ uWSGI على مقبس وسيتواصل باستخدام بروتوكول uwsgi
.
نحن الآن في النقطة التي يمكننا فيها العمل على تكوين Nginx كوكيل عكسي. يحتوي Nginx على القدرة على الوكيل باستخدام بروتوكول uwsgi
للتواصل مع uWSGI. هذا بروتوكول أسرع من HTTP وسيؤدي إلى أداء أفضل.
تكوين Nginx الذي سنقوم بإعداده بسيط للغاية. قم بإنشاء ملف جديد داخل دليل sites-available
ضمن التسلسل الهرمي لتكوين Nginx. سنطلق على ملفنا myapp
لمطابقة اسم التطبيق الذي كنا نستخدمه:
sudo nano /etc/nginx/sites-available/myapp
داخل هذا الملف، يمكننا تحديد رقم المنفذ واسم النطاق الذي يجب أن يستجيب له هذا الكتلة الخادم. في حالتنا، سنستخدم المنفذ الافتراضي 80:
server {
listen 80;
server_name server_domain_or_IP;
}
منذ أن نرغب في إرسال جميع الطلبات على هذا النطاق أو عنوان IP إلى تطبيق WSGI الخاص بنا، سنقوم بإنشاء كتلة موقع واحدة للطلبات التي تبدأ بـ /
، والتي يجب أن تتطابق مع كل شيء. داخلها، سنستخدم التوجيه include
لتضمين عدد من المعلمات مع القيم الافتراضية المعقولة من ملف في دليل تكوين Nginx الخاص بنا. الملف الذي يحتوي على هذه القيم يسمى uwsgi_params
. بعد ذلك، سنمرر حركة المرور إلى مثيل uWSGI الخاص بنا عبر بروتوكول uwsgi
. سنستخدم المقبس يونكس الذي قمنا بتكوينه سابقًا:
server {
listen 80;
server_name server_domain_or_IP;
location / {
include uwsgi_params;
uwsgi_pass unix:/home/demo/myapp/myapp.sock;
}
}
هذا هو كل ما نحتاجه فعليًا لتطبيق بسيط. هناك بعض التحسينات التي يمكن إجراؤها لتطبيق أكثر اكتمالا. على سبيل المثال، قد نقوم بتعريف عدد من خوادم uWSGI المتواجدة في الرأس، ثم نمررها إلى ذلك. قد نقوم بتضمين بعض المعلمات الإضافية لـ uWSGI. قد نتعامل أيضًا مع أي ملفات ثابتة من Nginx مباشرة ونمرر فقط الطلبات الديناميكية إلى مثيل uWSGI.
لكننا لا نحتاج إلى أي من تلك الميزات في تطبيقنا ذو الثلاثة أسطر، لذا يمكننا حفظ وإغلاق الملف.
قم بتمكين تكوين الخادم الذي أنشأناه للتو عن طريق ربطه بدليل sites-enabled
:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled
تحقق من ملف التكوين لاكتشاف أخطاء البنية النحوية:
sudo service nginx configtest
إذا أفاد بعدم اكتشاف أي مشاكل، أعد تشغيل الخادم لتنفيذ التغييرات:
sudo service nginx restart
بمجرد إعادة تشغيل Nginx، يجب أن تتمكن من الانتقال إلى اسم النطاق الخاص بالخادم أو عنوان IP (بدون رقم المنفذ) ورؤية التطبيق الذي قمت بتكوينه:
الاستنتاج
إذا كنت قد وصلت إلى هنا، فقد قمت بإنشاء تطبيق WSGI بسيط ولديك بعض الفهم حول كيفية تصميم تطبيقات أكثر تعقيدًا. لقد قمنا بتثبيت حاوية/خادم التطبيق uWSGI داخل بيئة افتراضية مخصصة لخدمة تطبيقنا. لقد قمنا بإنشاء ملف تكوين ونص تشغيل لـ Upstart لتأتيمين هذه العملية. أمام خادم uWSGI، قمنا بإعداد وكيل عكسي Nginx يمكنه التحدث مع عملية uWSGI باستخدام بروتوكول الأسلاك uwsgi
.
يمكنك بسهولة رؤية كيف يمكن توسيع هذا عند إعداد بيئة إنتاج فعلية. على سبيل المثال، يتمتع uWSGI بالقدرة على إدارة تطبيقات متعددة باستخدام شيء يُسمى “وضع الإمبراطور”. يمكنك توسيع تكوين Nginx لتوازن الحمل بين مثيلات uWSGI، أو لمعالجة الملفات الثابتة لتطبيقك. عند تقديم تطبيقات متعددة، قد يكون من الافضل لمصلحتك تثبيت uWSGI على نطاق عالمي بدلاً من ذلك داخل بيئة افتراضية، اعتمادًا على احتياجاتك. جميع المكونات مرنة إلى حد ما، لذا يجب أن تتمكن من ضبط تكوينها لاستيعاب العديد من السيناريوهات المختلفة.