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

يُعّد جانغو Django واحدًا من أقوى أطر العمل البرمجية التي تسهل عملية تطوير تطبيقات الويب المبنية باستعمال بايثون Python، وذلك لما يتمتع به من مميزاتٍ مفيدة مثل استخدامه تقنية ORM أي ربط الكائنات العلاقية object-relational mapper وتوفيره آلية للتحقق من هوية المستخدمين وواجهة مرنة قابلة للتخصيص لإدارة تطبيقك، بالإضافة إلى امتلاكه خاصية التخزين المؤقت وتشجيعه على تصميم الكود النظيف من خلال مصمم عناوين URL Dispatcher ونظام القوالب Template system.

سنعرض في هذا المقال التعليمي كيفية بناء تطبيق جانغو اسمه Polls قابل للتوسع والنقل بحاويات دوكر Docker، وما يلزم من تعديلات ليعمل بفعالية داخل الحاوية ويتكيف مع متغيرات بيئة التشغيل، كما سنتطرق إلى ضبط تسجيل أحداث التطبيق، وتخزين أو تفريغ أصوله الساكنة static assets وهي صفحات جافاسكريبت وملفات التنسيق CSS في مساحة التخزين الكائني object storage لما له من أثر إيجابي على إدارتها إدارة مركزية سلسة ومنظمة بالأخص في بيئة متعددة الحاويات.

وسننشئ بعد تنفيذ التعديلات المطلوبة -المستوحاة من منهجية Twelve-Factor الخاصة بتطوير تطبيقات ويب سحابية قابلة للتوسع والعمل ضمن الحاويات- على عينة من جانغو Polls صورة التطبيق image ونشغلّها ضمن حاوية دوكر.

بالنتيجة ستغدو في نهاية هذا المقال قادرًا على وضع تطبيق جانغو في حاوية قابلة للنقل، وفي مقالات لاحقة ستتعلم استخدام Docker Compose مع جانغو وخادم Nginx كوكيل عكسي، ومن ثم تطبيق هذه البنى التي تعرفت عليها في حاويات عنقودية مثل كوبيرنتس Kubernetes.

أخيرًا ننصحك بمتابعة خطوات العمل الواردة هنا بالتسلسل لضمان فهم كافة التغييرات التي سنُجريها على التطبيق، ولكن في حال رغبت بتخطي ذلك فيمكنك الحصول على كود التطبيق المعدل من القسم الخاص بتطبيقات polls-docker من مستودع GitHub.

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

  • بناء تطبيق جانغو بخادم Gunicorn ووضعه ضمن حاوية دوكر
  • توسيع تطبيق جانغو وتأمينه عبر حاوية دوكر وخادم Nginx وخدمة Let's Encrypt
  • نشر تطبيق جانغو آمن وقابل للتوسيع باستخدام كوبيرنتس

متطلبات بيئة العمل

ستحتاج المتطلبات التالية لتتمكن من التطبيق العملي لهذا المقال:

  1. تهيئة خادم بنظام تشغيل أوبونتو (استعملنا في المقال إصدار 18.04)، يمكنك الاستعانة بالمقال التهيئة الأولية لخادم أوبونتو 18.04 لتهيئته.
  2. تثبيت دوكر على الخادم، استعن بمقال كيفية تثبيت دوكر واستخدامه على دبيان فآلية العمل متشابهة في النظامين، دون أن تنسى إضافة مستخدم أوبونتو الخاص بك إلى مجموعة العمل دوكر ليأخذ صلاحيات العمل اللازمة.
  3. توفير خدمة تخزين كائني متوافقة مع S3 تؤمن المساحة التخزينية اللازمة لملفات وأصول تطبيق جانغو الساكنة بالإضافة إلى مجموعة من مفاتيح الوصول لإدارة هذه المساحة، وقد استُخدمت خدمة DigitalOcean Space من ديجيتال أوشن DigitalOcean في هذا المقال ويمكنك اختيار خدمة التخزين من المزود الأنسب لك مع إجراء تغييرات بسيطة تلائمها على خطوات الإعداد.
  4. نظام إدارة قواعد بيانات متوافق مع جانغو لإنشاء قاعدة بيانات التطبيق، وقد اخترنا DigitalOcean Managed PostgreSQL cluster استرشد بخطواتنا مع تغيير ما يلزم لقاعدة البيانات الخاصة بك.

الخطوة 1: إنشاء قاعدة البيانات PostgreSQL مع مستخدم قاعدة البيانات

يتلخص ما سنقوم به في هذه الخطوة بثلاث نقاط هي الاتصال بخادم قواعد بيانات PostgreSQL من خادم أوبونتو، وإنشاء قاعدة بيانات ومستخدم ضمنها خاص بتطبيق جانغو، ومن ثم ضبط إعداداتها لتعمل بكفاءة مع التطبيق، ولتحقيق ذلك سنبدأ بتثبيت عميل قاعدة بيانات PostgreSQL من مستودع برامج أوبونتو ولكن بعد تحديث دليل مدير الحزم apt، وفق الأمرين:

sudo apt update
sudo apt install postgresql-client

ثم نؤكد العملية بالضغط على الحرف Y وزر الإدخال Enter، وبعد إتمام التحميل والتثبيت ننتقل للمرحلة التالية وهي إنشاء قاعدة البيانات والمستخدم الخاص بتطبيق جانغو.

تتطلب هذه العملية معرفة محددات الاتصال مع PostgreSQL cluster مثل عنوان المضيف واسم قاعدة البيانات ورقم البوابة كذلك اسم المستخدم وكلمة المرور، ويمكنك الحصول على كافة هذه البيانات من لوحة التحكم الخاصة بخدمة PostgreSQL cluster ضمن مربع خاص بمحددات الاتصال إن كنت تستخدم نظام إدارة قواعد بيانات سحابي (مثل DigitalOcean أو غيره).

والآن باستخدام هذه المحددات ونسخة العميل من PostgreSQL التي سبق تثبيتها، يمكنك تسجيل الدخول إلى PostgreSQL cluster وفق الأمر التالي:

psql -U username -h host -p port -d database --set=sslmode=require

ستُطلب منك كلمة المرور، أدخلها، واضغط زر الإدخال Enter، لتنتقل إلى موجه أوامر PostgreSQL الذي يخولك التحكم بقاعدة البيانات.

أنشئ قاعدة بيانات خاصة بمشروعك باسم polls وفق التعليمة التالية (مع التنويه إلى أن كافة تعليمات psql يجب أن تنتهي بفاصلة منقوطة):

CREATE DATABASE polls;

ثم توجه إلى قاعدة البيانات polls الخاصة بك كما يلي:

\c polls;

وأنشئ ضمنها مستخدم قاعدة بيانات لمشروعك باسم sammy مثلًا -أو أي اسم- كما يلي واحرص على حمايته بكلمة مرور قوية:

CREATE USER sammy WITH PASSWORD 'password';

اضبط بعد ذلك القيم الافتراضية لمحددات اتصال المستخدم المنشأ بالتوافق مع توصيات جانغو بحيث لا يتطلب الأمر الاستعلام عنها وإعادة تعيينها مع كل تأسيس جديد للاتصال ما سينعكس إيجابًا على تسريع عمليات قاعدة البيانات، والمحددات المطلوب ضبطها هي: الترميز encoding الذي يأخذ القيمة الافتراضية UTF-8، وآلية عزل العمليات transaction isolation scheme الذي يضبط إلى القيمة "read committed" ومهمته منع قراءة العمليات غير المكتملة uncommitted transactions، وأخيرًا المنطقة الزمنية نثبتها على UTC، وفق التعليمات التالية:

ALTER ROLE sammy SET client_encoding TO 'utf8';
ALTER ROLE sammy SET default_transaction_isolation TO 'read committed';
ALTER ROLE sammy SET timezone TO 'UTC';

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

GRANT ALL PRIVILEGES ON DATABASE polls TO sammy;

وبعدها اخرج من موجه أوامر PostgreSQL باستخدام:

\q

جهزنا بذلك قاعدة البيانات ليُديرها تطبيق جانغو، لننتقل للخطوة الثانية المتمثلة بنسخ كود التطبيق Polls من GitHub وتحديد حزم اعتماديات بايثون اللازمة له.

الخطوة 2: استنساخ مستودع التطبيق والتصريح عن اعتماديات عمله

سنستخدم لعملية الاستنساخ مستودع التطبيق django-polls الذي يتضمن الكود الكامل لتطبيق Polls المجهز لأغراض البرنامج التعليمي في مشروع جانغو.

بدايةً سجل الدخول إلى خادم أوبونتو وأنشئ مجلدًا يدعى polls-project ومن ثم استخدم الأمر git لاستنساخ مستودع django-polls من GitHub:

mkdir polls-project
cd polls-project
git clone https://github.com/do-community/django-polls.git

انتقل إلى django-polls واستعرض محتوياته بتعليمة ls:

cd django-polls
ls

وسينتج الخرج التالي:

LICENSE  README.md  manage.py  mysite  polls  templates

الذي يبين لك محتويات تطبيق django-polls، وهي العناصر المبينة أدناه، نعددها لك بإيجاز ويمكنك تعلم المزيد عنها بالاطلاع على التوثيق الخاص بإنشاء المشاريع من توثيقات جانغو الرسمية:

  1. manage.py: أداة سطر الأوامر الرئيسية لتنفيذ أوامر معالجة التطبيق.
  2. polls: يتضمن أكواد التطبيق polls.
  3. mysite: يتضمن أكواد وإعدادات جانغو project-scope.
  4. templates: يتضمن قوالب خاصة بواجهة الإدارة.

أنشئ بعدها ملفًا نصيًا باسم requirements.txt ضمن نفس المسار polls-project/django-polls الذي تكلمنا عنه، وافتحه باستخدام محرر النصوص والصق ضمنه اعتماديات بايثون التالية، ثم احفظ التغييرات على الملف وأغلقه:

boto3==1.9.252
botocore==1.12.252
Django==2.2.6
django-storages==1.7.2
docutils==0.15.2
gunicorn==19.9.0
jmespath==0.9.4
psycopg2==2.8.3
python-dateutil==2.8.0
pytz==2019.3
s3transfer==0.2.1
six==1.12.0
sqlparse==0.3.0
urllib3==1.25.6

كما تلاحظ فقد أوردنا بدقة كافة اعتماديات بايثون التي يحتاج إليها المشروع ومن بينها إضافة django-storages الخاصة بتخزين ملفات وأصول الموقع الساكنة على مساحة التخزين الكائني، وإضافة gunicorn الخاصة بخادم WSGI، كذلك مُحول psycopg2 لمواءمة التخاطب مع قاعدة بيانات PostgreSQL.

والآن بعد أن أنهينا استنساخ التطبيق وحددنا ما يلزمه من اعتماديات لنبدأ بتعديله ليصبح قابلًا للنقل.

الخطوة 3: تكييف جانغو مع متغيرات البيئة

توصي منهجية Twelve-Factor باستخراج بيانات الإعدادات المدمجة مع كود التطبيق التي لا يمكن تعديلها إلاّ بتعديل الكود، وفصلها عن الكود ما يسمح للتطبيق بالتكيّف وتغيير سلوكه تبعًا لمتغيرات بيئة التشغيل، يوافقها في ذلك توصيات إعداد الحاويات لدى كلٍ من دوكر وكوبيرنتس Kubernetes، لذا سنتبع هذا النمط ونعدّل ملف الإعدادات django-polls/mysite/settings.py الخاص بتطبيق جانغو -وهو وحدة بايثون- بحيث يحصل على البيانات من بيئة التشغيل الفعلية المحلية عوضًا عن قراءتها من الكود، وذلك باستخدام الدالة getenv مع الوحدة os. لنبدأ بالتطبيق عمليًا!

سنجعل المحددات المدمجة مع الكود ضمن الملف settings.py تأخذ قيمها من متغيرات البيئة عبر استدعاء الدالة os.getenv وتحديد اسم المتغير المرتبط ضمن قوسين، مع العلم بإمكانية تمرير قيمة ثانية للدالة تسمى بالقيمة الافتراضية أو الاحتياطية تُرجعها الدالة في حال كان المتغير غير معرف، لاحظ التالي:


SECRET_KEY = os.getenv('DJANGO_SECRET_KEY')
DEBUG = os.getenv('DJANGO_DEBUG', False)
ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', '127.0.0.1').split(',')

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

بنفس الطريقة للمتغير أو المحدد DEBUG سيبحث عن متغير بيئة يدعى DJANGO_DEBUG ويأخذ قيمته، وفي حال لم يكن معرفًا فإنه سيأخذ القيمة الاحتياطية false التي ستحمينا من فقدان أي بيانات قد تكون حساسة ما لم نضبط المتغير إلى قيمة true عمدًا.

أما بالنسبة للمتغير أو المحدد ALLOWED_HOSTS فسيُحضر قيمة متغير البيئة المرتبط به DJANGO_ALLOWED_HOSTS ويأخذ قيمته بعد فصله إلى قائمة بايثون باستخدام الدالة ()split مع اعتماد , كفاصل، وفي حال أن المتغير لم يكن معرفًا يأخذ المحدد القيمة 127.0.0.1 الاحتياطية.

فور انتهائك من تعديل المحددات الثلاثة السابقة انتقل لتعديل محدد قاعدة البيانات DATABASES ضمن نفس الملف بحيث يأخذ أيضًا قيمته الافتراضية default من متغيرات البيئة، وذلك وفق التالي:

. . . 
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.{}'.format(
             os.getenv('DATABASE_ENGINE', 'sqlite3')
         ),
         'NAME': os.getenv('DATABASE_NAME', 'polls'),
         'USER': os.getenv('DATABASE_USERNAME', 'myprojectuser'),
         'PASSWORD': os.getenv('DATABASE_PASSWORD', 'password'),
         'HOST': os.getenv('DATABASE_HOST', '127.0.0.1'),
         'PORT': os.getenv('DATABASE_PORT', 5432),
         'OPTIONS': json.loads(
             os.getenv('DATABASE_OPTIONS', '{}')
         ),
     }
 }
 . . .

لاحظ أننا استخدمنا json.loads لتعيين قيمة محدد قاعدة البيانات وفق الصيغة:

DATABASES['default']['OPTIONS']

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

دون أن تنسى تفعيل مكتبة json ضمن ملف الإعدادات settings.py وفق التالي حتى تتمكن من استخدام هذا النوع من المتغيرات:

"""
Django settings for mysite project.

Generated by 'django-admin startproject' using Django 2.1.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""
import os
import json
. . .

نشير أيضًا إلى محدد آخر يستدعي الاهتمام هو اسم قاعدة البيانات:

DATABASES['default']['NAME']

فهو يدل على اسم قاعدة البيانات في معظم نظم إدارة قواعد البيانات العلاقية، أما في حال استخدامك قاعدة بيانات SQLite فإنه سيشير إلى ملف قاعدة البيانات لذا انتبه لهذه النقطة.

وفي ختام التعديل لا بد لنا من الإشارة إلى أن الملف setting.py هو في النهاية ملف بايثون وبالتالي يمكنك قراءته والتعديل عليه بالطريقة التي تفضلها وما اتبعناه لهذا الغرض كان إحدى سبل التعديل المتاحة.

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

الخطوة 4: تفريغ المكونات الساكنة Offloading Static Assets

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

أما عن ضبط المحددات الخاصة بهذه الوحدة سنترك بعضها مدمجًا مع الكود hard-coded ونمرر بعضها الآخر لحاويات تطبيق جانغو عبر متغيرات البيئة بنفس الطريقة التي اتبعناها في الخطوة /3/ السابقة للتعامل مع محددات قاعدة البيانات.

يتطلب هذا النوع من التخزين تثبيت حزمة django-storages التي تدعم وجود واجهات خلفية بعيدة للتطبيق تُخزن عليها المكونات والأصول الساكنة بما فيها وحدات التخزين الكائني المتوافقة مع S3 مثل DigitalOcean Space المستخدمة في مقالنا أو غيرها، ويمكنك الاستعانة بمقالة أجنبية أخرى تشرح عملية التفريغ في الخطوة رقم 7 منها.

سنبدأ بتعديل الملف django-polls/mysite/settings.py وهو نفس الملف الذي تعاملنا معه في الفقرة السابقة بإضافة التطبيق storages على قائمة التطبيقات التي يستخدمها جانغو وذلك من خلال المحدد INSTALLED_APPS كما يلي:

. . .
INSTALLED_APPS = [
    . . .
    'django.contrib.staticfiles',
    'storages',
]
. . .

تذكر أن التطبيق storages هو أحد الاعتماديات التي حددناها في الملف requirements.txt الذي ورد في الخطوة /1/ من الإعداد (وذلك من خلال django-storages).

وبعد INSTALLED_APPS سنعدّل المحدد STATIC_URL كما يلي:

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

# Moving static assets to DigitalOcean Spaces as per:
# https://www.digitalocean.com/community/tutorials/how-to-set-up-object-storage-with-django
AWS_ACCESS_KEY_ID = os.getenv('STATIC_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('STATIC_SECRET_KEY')

AWS_STORAGE_BUCKET_NAME= os.getenv('STATIC_BUCKET_NAME')
AWS_S3_ENDPOINT_URL = os.getenv('STATIC_ENDPOINT_URL')
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
AWS_DEFAULT_ACL = 'public-read'

STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

STATIC_URL='{}/{}/'.format(AWS_S3_ENDPOINT_URL, AWS_LOCATION)
STATIC_ROOT = 'static/'

كما أسلفنا في مقدمة الفقرة، فإن بعض المحددات ستبقى مدمجة مع الكود hard-coded وهي:

  1. STATICFILES_STORAGE: يحدد الواجهة الخلفية التي يستخدمها جانغو لتفريغ الملفات الساكنة والقيمة S3Boto3Storage لهذا المحدد ستعمل مع كافة خدمات التخزين المتوافقة مع S3 بما فيها DigitalOcean Spaces.
  2. AWS_S3_OBJECT_PARAMETERS: يحدد معايير التخزين المؤقت cache control الخاصة بمكونات التطبيق الساكنة.
  3. AWS_LOCATION: يحدد مسار تخزين كامل الملفات الساكنة على خدمة التخزين الكائني وهذا المسار يسمى static.
  4. AWS_DEFAULT_ACL: يحدد قائمة التحكم بالوصول ACL لملفات التطبيق الساكنة، واختيار القيمة public-read لهذا المحدد يعني أن الملفات قابلة للقراءة من قبل العامة.
  5. STATIC_URL: يحدد العنوان الرئيس للملفات الساكنة Base URL ويتشكل بجمع مكونين الأول هو endpoint URL ويشير إلى خدمة التخزين الكائني والثاني هو مسار وجود الملفات الساكنة للتطبيق.
  6. STATIC_ROOT: يحدد المسار المحلي لتجميع الساكنة قبل نسخها إلى خدمة التخزين الكائني.

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

  1. AWS_ACCESS_KEY_ID: يأخذ قيمته من متغير البيئة STATIC_ACCESS_KEY_ID الذي يشير إلى معرف مفتاح الوصول الخاص بخدمة التخزين (وهي DigitalOcean Spaces في حالتنا).
  2. AWS_SECRET_ACCESS_KEY: يأخذ قيمته من المتغير STATIC_SECRET_KEY ويشير إلى المفتاح السري لخدمة التخزين.
  3. AWS_STORAGE_BUCKET_NAME: يأخذ قيمته من المتغير STATIC_BUCKET_NAME ويدل على الموقع الذي ستُرفع ملفات جانغو الساكنة إليه على خدمة التخزين.
  4. AWS_S3_ENDPOINT_URL: يأخذ قيمته من المحدد STATIC_ENDPOINT_URL ويستخدم للوصول إلى خدمة التخزين الكائني، فسيكون شكله من قبيل هذا العنوان https://nyc3.digitaloceanspaces.com في خدمة DigitalOcean Spaces على سبيل المثال بالطبع مع بعض الاختلافات المتعلقة بالمنطقة التي تحوي ملفاتك.

لا تنسَ حفظ التغيرات على الملف settings.py قبل إغلاقه.

ننهي بذلك الضبط المتعلق بالمكونات الساكنة، وبموجبه سيرفع جانغو ملفاته إلى وحدة التخزين الكائني البعيدة في كل مرة تشغل فيها manager.py collectionstatic لتُجمَّع الملفات الساكنة لمشروعك.

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

لننتقل الآن للخطوة التالية وهي ضبط تسجيل أحداث جانغو ضمن مجرى قياسي للخرج والخطأ STDOUT و STDERR تتعامل معه حاوية دوكر من خلال docker logs، وهذا سيكون تعديلنا الأخير على settings.py.

الخطوة 5: ضبط تسجيل الأحداث

إذا كنت تعتمد خادم التطوير المدمج في إطار العمل جانغو ليؤدي دور خادم HTTP لتطبيقك، وفعلت خيار التنقيح بإعطاء DEBUG القيمة true فإن جانغو سيُسجل الأحداث تلقائيًا في مجرى خرج قياسي STDOUT ومجرى خطأ قياسي STDERR، أما الإخلال بأي من الشرطين سواءً باستخدام خادم HTTP مغاير لخادم التطوير أو بإعطاء القيمة false للمحدد DEBUG، وكلاهما شائع في بيئة العمل الفعلية، فستتغير آلية التسجيل للتطبيق فبدلًا من تسجيل كافة الأحداث التي تحمل مستوى الخطورة INFO وما فوق ضمن المجرى القياسي، فإنه سيبعث الأحداث التي تحمل المستوى ERROR أو CRITICAL إلى البريد الإلكتروني لمدير النظام.

هذا السلوك من جانغو لا يعدّ ملائمًا لبيئة الحاويات بالأخص المتعددة مثل Kubernetes (التي تعتمد النظام العنقودي) ففي هذه البيئات تُجمع تسجيلات الأحداث ضمن ملفات قياسية لكلٍ من الخرج والخطأ وتخزن في مسار مركزي على نظام ملفات العقدة بحيث تكون متاحة لأوامر الحاوية مثل kubectl و docker، وسهلة المراقبة والتتبع على فرق التشغيل.

من هذه النقطة تبرز الحاجة لتعديل تسجيل أحداث جانغو ليتماشى مع هذه البنية ويعطي ملفات الخرج المرغوبة، والتطبيق مرن من هذه الناحية، فهو يستخدم وحدة التسجيل logging من مكاتب بايثون القياسية التي تتيح تعريف قاموس مخصص للتسجيل بالتفاصيل والصيغ التي نريدها وذلك باستخدام الدالة logging.config.dictConfig.

لنبدأ التطبيق العملي! افتح الملف django-polls/mysite/settings.py عبر محرر النصوص، واكتب ضمنه تعليمة import إضافية خاصة بـ logging.config ستسمح لك بتجاوز سلوك التسجيل الإفتراضي لتطبيق جانغو عبر تمرير قاموس تسجيل الأحداث التي ترغب بها للدالة dictConfig، كما يلي:

import json
import os
import logging.config
. . .

ثم انتقل إلى نهاية الملف والصق الكود التالي الخاص بإعدادات تسجيل الأحداث:

. . .
# Logging Configuration

# Clear prev config
LOGGING_CONFIG = None

# Get loglevel from env
LOGLEVEL = os.getenv('DJANGO_LOGLEVEL', 'info').upper()

logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'console': {
            'format': '%(asctime)s %(levelname)s [%(name)s:%(lineno)s] %(module)s %(process)d %(thread)d %(message)s',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'console',
        },
    },
    'loggers': {
        '': {
            'level': LOGLEVEL,
            'handlers': ['console',],
        },
    },
})

لنناقش التعليمات السابقة: أعطينا المحدد LOGGING_CONFIG القيمة none وذلك لتعطيل النمط الإفتراضي لتسجيل جانغو، ومنحنا LOGLEVEL قيمة افتراضية هي info مع ترك المجال مفتوح لتجاوزها عند الحاجة بتمرير أي قيمة أخرى نريدها عن طريق متغير البيئة DJANGO_LOGLEVEL.

وأخيرًا استخدمنا الدالة dictConfig لتعيين قاموس تسجيل الأحداث المرغوب الذي ستعتمده الوحدة logging.config، حددنا في القاموس طريقة تنسيق النص عبر formatters بحيث يتضمن مستوى خطورة الحدث ونوع الخط وغيرها من التفضيلات، كذلك حددنا الخرج عن طريق إعداد handlers، ومن ثم وضحنا كل سوية حدث بأي handler ترتبط باستخدام المسجلين loggers.

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

ننهي بذلك التعديلات البرمجية على تطبيق جانغو الذي يدعى Polls وسنبدأ بتهيئة الحاوية عبر ملف Dockerfile في الخطوة التالية.

الخطوة 6: كتابة الملف Dockerfile لحاوية التطبيق

نقدم لك في هذه الفقرة إحدى أبسط الطرق لبناء صورة الحاوية container image اللازمة لتشغيل تطبيق جانغو مع خادم Gunicorn WSGI الخاص به، وكل ما يلزم العملية من إعدادات، مع العلم بوجود طرق أخرى يمكنك اتباعها.

اختيار صورة الحاوية المناسبة للبناء عليها

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

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

تنويه: ستلاحظ أثناء استعراضك صور الحاويات المعتمدة على لغة بايثون أن كل صورة تحمل أكثر من وسم tag وأن الوسوم نفسها توضع على صورٍ مختلفة، فمثلًا الوسم 3alpine يشير إلى تثبيت أحدث إصدار من بايثون 3 على أحدث إصدار من Alpine وفي حال نزل إصدار جديد من أحدهما فسيُعاد تعيين الوسم على الصورة الجديدة أيضًا، لذا اختر الصورة التي تحمل الوسوم الأكثر تحديدًا لتحصل على أقرب صورة للبيئة التي ترغب بتحقيقها، وفي مثالنا الحالي سنستخدم الصورة ذات الوسم 3.7.4-alpine3.10.

سنحدد المستودع ووسم الصورة في ملف Dockerfile الخاص بتطبيقنا وذلك باستخدام التعليمة from، لنخرج أولًا من مجلد التطبيق django-polls حيث كنا نطبق التعليمات في الخطوات السابقة وذلك وفق الأمر التالي:

cd ..

أصبحنا الآن في المجلد الأب polls-project لننشئ ضمنه ملف نصي باستخدام المحرر ونسميه Dockerfile ونكتب فيه التعليمة التالية التي تعدّ نقطة البداية في إنشاء صورة الحاوية التي نجهزها للتطبيق:

FROM python:3.7.4-alpine3.10

كتابة التعليمات التنفيذية الخاصة بإعداد التطبيق للحاوية

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

نضيف الكود التالي إلى الملف Dockerfile بعد تعليمة from السابقة:

. . .

ADD django-polls/requirements.txt /app/requirements.txt

RUN set -ex \
    && apk add --no-cache --virtual .build-deps postgresql-dev build-base \
    && python -m venv /env \
    && /env/bin/pip install --upgrade pip \
    && /env/bin/pip install --no-cache-dir -r /app/requirements.txt \
    && runDeps="$(scanelf --needed --nobanner --recursive /env \
        | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
        | sort -u \
        | xargs -r apk info --installed \
        | sort -u)" \
    && apk add --virtual rundeps $runDeps \
    && apk del .build-deps

ADD django-polls /app
WORKDIR /app

ENV VIRTUAL_ENV /env
ENV PATH /env/bin:$PATH

EXPOSE 8000

سنقدم فيما يلي شرحًا لبعض ما ورد ضمن الكود السابق ولمزيد من المعلومات يمكنك الاطلاع على ملف Dockerfile جاهز لتطبيق جانغو.

تتضمن التعليمة الأولى نسخ الملف requirements.txt إلى المسار app/requirements.txt/ بحيث تصبح اعتماديات التطبيق اللازمة موجودة ضمن نظام ملفات الصورة ليتم استخدامها في تنزيل كافة حزم بايثون الضرورية لعمل التطبيق، لاحظ أننا وضعنا هذه التعليمة في خطوة منفصلة وذلك ليقوم دوكر بتخزين الصورة التي تتضمن requirements.txt في طبقة منفصلة ما يتيح له إعادة استخدامها عند الحاجة عوضًا عن بنائها من جديد (بالطبع على فرض أن الاعتماديات لم تتغير)، ذلك أن دوكر ينشئ طبقة للصورة على نظام الملفات بعد تنفيذ كل تعليمة ADD أو RUN أو COPY.

أما التعليمة الثانية RUN فهي تشتمل على مجموعة من أوامر لينكس يجمع بينها الإشارة && لتنفيذها على التوالي نوردها بإيجاز:

  • تثبيت PostgreSQL وبناء اعتماديتها باستخدام apk مدير حزم Alpine.
  • إنشاء البيئة الافتراضية.
  • تثبيت اعتماديات بايثون المذكورة في الملف requirements.txt وذلك باستخدام pip.
  • إعداد قائمة بالحزم المطلوبة للتشغيل من خلال تحليل متطلبات حزم بايثون المثبتة.
  • إزالة تثبيت أي اعتماديات مبنية ولم تعد لازمة.

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

أما التعليمة الثالثة بعد Run كانت ADD وهي تهدف إلى نسخ كود التطبيق ومسار العمل WORKDIR وذلك لضبط مسار العمل في الصورة على مسار التطبيق.

تعرّف التعليمة الرابعة ENV اثنين من متغيرات البيئة التي ستكون متاحة ضمن الحاوية، المتغير الأول هو VIRTUAL_ENV ويأخذ القيمة env/ أما المتغير الثاني PATH يضبط على المسار env/bin/، وتحاكي هذه العملية الطريقة التقليدية المتبعة لتفعيل البيئة الافتراضية للتطبيق عبر سكريبت env/bin/activate/.

أما العملية الأخيرة في هذه المرحلة فهي تحديد البوابة 8000 للاتصال مع الحاوية في وقت التشغيل عبر تعليمة EXPOSE.

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

تحديد الأمر الافتراضي

يُكتب الأمر الافتراضي لحاوية دوكر في الملف Dockerfile باستخدام تعليمة ENTRYPOINT أو تعليمة CMD أو كلاهما معًا، ويحدد هذا الأمر ما سيُنفذ افتراضيًا عند بدء تشغيل الحاوية.

في حال استخدمنا ENTRYPOINT و CMD معًا فإن الأولى ستحدد الأمر التنفيذي (الإجراء) الذي سينفذ عند تشغيل الحاوية والثانية ستحدد قائمة الوسطاء arguments اللازمة له.

علمًا أن المستخدم يستطيع بسهولة تجاوز الوسطاء المعرّفين في CMD وذلك بتمرير وسطاء غيرهم لأمر تشغيل الحاوية كما يلي:

docker run <image> <arguments>

بينما لا يمكنه بنفس السهولة تجاوز الأمر التنفيذي الوارد في ENTRYPOINT لذا يلجأ الأغلبية لجعل الأمر التنفيذي المعطى لـ ENTRYPOINT على هيئة سكريبت يهيئ البيئة وينفذ الإجراءات التي تتناسب مع قائمة الوسطاء التي يتلقاها.

ذكرنا فيما سبق طريقة استخدام ENTRYPOINT و CMD معًا، أما لو أردنا استخدام إحداهما فقط فسيكون الوضع كالتالي:

  • استخدام ENTRYPOINT لوحدها يحدد الأمر التنفيذي للحاوية فقط دون تحديد قائمة الوسطاء.
  • أما استخدام CMD لوحدها يتيح تحديد الأمر command والوسطاء arguments مع إمكانية تجاوز الوسطاء لاحقًا عبر docker run كما أسلفنا.

لنطبق عمليًا على الصورة التي نبنيها ونناقش المتطلبات التي ستقودنا لاختيار التعليمة المناسبة لبدء التشغيل.

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

تكتب CMD عمومًا بثلاث صيغ:

  1. تمرير الوسطاء لتعليمة ENTRYPOINT:
CMD ["argument 1", "argument 2", . . . ,"argument n"]
  1. بصيغة exec:
CMD ["command", "argument 1", "argument 2", . . . ,"argument n"]
  1. بصيغة shell:
CMD command "argument 1" "argument 2" . . . "argument n"

تستخدم CMD في الصيغة الأولى مع ENTRYPOINT لتمرير قائمة الوسطاء فقط كما ذكرنا، أما في الصيغتين الثانية والثالثة تكون مسؤولة عن تحديد الأمر الذي سينفذ وكذلك الوسطاء، والفرق بينهما أن صيغة exec (وهي الصيغة الموصى بها) تنفذ الأمر المحدد مباشرةً وتمرر قائمة الوسطاء دون القيام بأي عمليات أو معالجة في الصدفة shell، أما في صيغة الصدفة shell فإن قائمة الوسطاء تُمرر إلى sh -c، وقد تكون ضرورية لك في الحالات التي تحتاج فيها لتبديل أحد متغيرات البيئة التي يستخدمها الأمر.

بالنتيجة اعتمدنا الشكل النهائي التالي لتحديد افتراضات تشغيل الصورة، وكُتب ضمن Dockerfile كما يلي:

. . .
CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi:application"]

وبموجبه تشغل الحاوية gunicorn على الجهاز المحلي localhost افتراضيًا وذلك عبر البوابة 8000 باستخدام عمال workers عدد 3، كما أنها تشغل الدالة application الموجودة ضمن الملف wsgi.py في المسار mysite.

وبذلك أصبحنا جاهزين للمرحلة التالية وهي استخدام الأمر docker build لبناء صورة التطبيق والأمر docker run لتشغيل الحاوية.

بناء صورة دوكر

يبني docker build الصورة باستخدام كل من ملف dockerfile الذي يتضمن إعدادات البناء، وسياق "context" الذي يشير إلى مسار مجموعة الملفات التي يجب أن تتوفر لعفريت دوكر Docker daemon لينجز عملية البناء.

المتغير "context" يطابق في معظم الأحيان المسار الحالي الذي نكون فيه عند تنفيذ الأمر docker build ويُشار إلى المسار الحالي في لينكس برمز النقطة . .

للتنفيذ العملي، ابحث عن مسار الملف Dockerfile ونفذ ضمنه التالي مع تمرير اسم الصورة بعد الراية t- بالإضافة إلى تمرير المسار الحالي:

docker build -t django-polls:v0 .

لاحظ أننا أعطينا الاسم django-polls للصورة أما v0 فهي تشير للإصدار رقم 0 من الصورة، حيث أن عفريت دوكر الذي يعمل في الخلفية يبني الصورة عبر سلسلة من الطبقات بناءً على بنية تعليمات ملف Dockerfile.

سنحصل على الخرج التالي فور إكتمال تنفيذ الأمر docker build:

Successfully built 8260b58f5713
Successfully tagged django-polls:v0

نظريًا يمكنك تشغيل الحاوية عبر الأمر docker run بعد انتهاء عملية البناء بنجاح، ولكن في الواقع سيفشل الأمر run ما لم تعرف داخل الحاوية كافة متغيرات البيئة التي حددناها خارجيًا في الخطوات السابقة مثل SECRET_KEY ومحددات قاعدة البيانات ضمن الملف setting.py وغيرها، وهو ما سنقوم به في الخطوة التالية.

الخطوة 7: ضبط متغيرات بيئة التشغيل واختبار التطبيق

يوفر دوكر عدة طرق لتعريف المتغيرات داخل الحاوية، ولكون حالتنا تتطلب إعادة تعريف كامل المتغيرات التي عملنا عليها في الخطوة /1/، سنتبع الطريقة env-file التي تتيح تمرير ملف يتضمن كامل متغيرات البيئة وقيمة كل منها.

لتنفيذ ذلك سننشئ ملفًا نصيًا يدعى env ضمن المجلد polls-project مع لصق القائمة التالية ضمنه وهي تحتوي كامل المتغيرات وقيمها:

DJANGO_SECRET_KEY=your_secret_key
DEBUG=True
DJANGO_ALLOWED_HOSTS=your_server_IP_address
DATABASE_ENGINE=postgresql_psycopg2
DATABASE_NAME=polls
DATABASE_USERNAME=sammy
DATABASE_PASSWORD=your_database_password
DATABASE_HOST=your_database_host
DATABASE_PORT=your_database_port
STATIC_ACCESS_KEY_ID=your_space_access_key_id
STATIC_SECRET_KEY=your_space_secret_key
STATIC_BUCKET_NAME=your_space_name
STATIC_ENDPOINT_URL=https://nyc3.digitaloceanspaces.com
DJANGO_LOGLEVEL=info

لا تنسَ تغيير القيم المطلوبة بما يناسب إعداداتك وفق التالي:

  1. بالنسبة للمفتاح السري DJANGO_SECRET_KEY اختر له قيمة فريدة صعبة التخمين كما توصي توثيقات دوكر تقدم لك الخطوة /5/ من مقال كيفية إعداد تطبيق جانغو عبر DigitalOcean Managed Databases and Spaces الأجنبي إحدى طرق جانغو لتوليد المفاتيح.
  2. ضع عنوان IP الخاص بالخادم أوبونتو مقابل DJANGO_ALLOWED_HOSTS ويمكنك وضع الرمز * لأغراض الاختبار فقط وليس ضمن بيئة العمل الفعلية فهو يسمح باتصال أي عنوان IP.
  3. اكتب اسم مستخدم قاعدة البيانات وكلمة المرور كما حددتهما في الخطوات السابقة مقابل DATABASE_USERNAME و DATABASE_PASSWORD.
  4. كذلك الأمر بالنسبة لاعدادات الاتصال مع قاعدة البيانات من حيث اسم مضيف قاعدة البيانات وبوابة اتصال قاعدة البيانات ضع هذه البيانات مقال كل من DATABASE_HOST و DATABASE_PORT.
  5. أما المحددات STATIC_ACCESS_KEY_ID و STATIC_SECRET_KEY و STATIC_BUCKET_NAME و STATIC_ENDPOINT_URL فهي تتعلق بخدمة التخزين الكائني الخارجية، اضبطها بما يتناسب مع الخدمة التي تستخدمها.

قبل تشغيل الأمر docker run احفظ التغيرات على الملف السابق env وتأكد أن المحدد DEBUG يحمل القيمة false وأن سجلات الأحداث مضبوطة بحيث تعطي التفاصيل المرغوبة.

وبعدها نفذ أمر التشغيل للإصدار v0 من الصورة django-polls بطريقة تتجاوز الإعدادات الافتراضية التي حددناها سابقًا ضمن الملف Dockerfile (باستخدام CMD) وتنشئ أثناء التشغيل مخطط قاعدة البيانات الخاص بالتطبيق عبر manage.py makemigrations و manage.py migrate:

docker run --env-file env django-polls:v0 sh -c "python manage.py makemigrations && python manage.py migrate"

سيظهر الخرج التالي بعد التنفيذ:

No changes detected
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying polls.0001_initial... OK
  Applying sessions.0001_initial... OK

ننفذ بعدها أمر تشغيل جديد لحاوية التطبيق نستدعي خلاله سطر الأوامر الخاص بالصدفة Shell كما يلي:

docker run -i -t --env-file env django-polls:v0 sh

تظهر عندها نافذة صدفة shell ضمن الحاوية وننشئ من خلالها المستخدم الذي يحمل صلاحية مدير تطبيق جانغو:

python manage.py createsuperuser

يلي ذلك إدخال بيانات المستخدم وهي اسم المستخدم وكلمة المرور والبريد الإلكتروني، ومن ثم الخروج من الحاوية وإنهاء عملها باستخدام الأمر Ctrl+D.

أما العملية الأخيرة هي جمع الملفات الساكنة الخاصة بالتطبيق collectstatic ورفعها على خدمة التخزين الكائني:

docker run --env-file env django-polls:v0 sh -c "python manage.py collectstatic --noinput"

لنحصل على الخرج التالي:

121 static files copied.

يمكننا الآن تشغيل التطبيق بالإعدادات الافتراضية المحددة ضمن Dockerfile:

docker run --env-file env -p 80:8000 django-polls:v0

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

[2019-10-17 21:23:36 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2019-10-17 21:23:36 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1)
[2019-10-17 21:23:36 +0000] [1] [INFO] Using worker: sync
[2019-10-17 21:23:36 +0000] [7] [INFO] Booting worker with pid: 7
[2019-10-17 21:23:36 +0000] [8] [INFO] Booting worker with pid: 8
[2019-10-17 21:23:36 +0000] [9] [INFO] Booting worker with pid: 9

كما ذكرنا فإن التشغيل هذه المرة تم وفق الإعدادات الافتراضية ضمن ملف Dockerfile وهي كما حددناها سابقًا:

gunicorn --bind :8000 --workers 3 mysite.wsgi:application

وبذلك فإن البوابة 80 الخاصة بالخادم أوبونتو ستوجه الحركة إلى البوابة 8000 في الحاوية django-polls:v0.

يمكنك الآن فتح التطبيق polls عبر متصفح الإنترنت بكتابة عنوان URL الخاص به، وانتبه أنك ستحصل على الخطأ 404 (لم يتم العثور على الصفحة) في حال اكتفيت بالعنوان التالي مثلًا:

 http://your_server_ip/ 

بسبب عدم وجود بيانات للتطبيق تحت الجذر / ، لذا اكتب العنوان التالي:

http://your_server_ip/polls

01-img-polls_app.png

اكتب الرابط http://your_server_ip/admin للوصول إلى واجهة الدخول للوحة التحكم الخاصة بالتطبيق:

واجهة الدخول للوحة التحكم

أدخل بيانات المستخدم مدير التطبيق المنشأ سابقًا باستخدام الأمر createsuperuser وستظهر أمامك واجهة الإدارة والتحكم الخاصة بالتطبيق.

واجهة الإدارة والتحكم الخاصة بالتطبيق

لاحظ أن تسليم المكونات الساكنة للتطبيق admin و polls يتم من خدمة التخزين الكائني ويمكنك اتباع اختبار جودة والتأكد من صحة إحضار الملفات.

اضغط Ctrl+c في نافذة كتابة الأوامر السطرية التي تشغل حاوية دوكر لإنهاء عمل الحاوية.

خاتمة

استعرضنا في هذا المقال طريقة تكييف تطبيق جانغو ليعمل بفعالية ضمن بيئة تطوير سحابية أصلية cloud-native معتمدة على الحاويات container-based، وكتابة ملف Dockerfile محلي ومبسط لصورة الحاوية ومن ثم تشغيله عبر محرك دوكر، ويمكنك رؤية التغييرات التي طبقناها وتأثيراتها باستعراض قسم polls-docker من مستودع GitHub فهو يتضمن كافة تعديلات التطبيق polls التي ناقشناها في هذا المقال.

إن ما تعلمته هنا يمهد لك الطريق لتجهيز بيئة متعددة الحاويات فيمكنك جمع حاوية Django/Gunicorn التي عملنا عليها مع حاوية تتضمن خادم وكيل عكسي Nginx لمعالجة طلبات HTTP بكفاءة، والحصول على شهادات TLS لتشفير هذه الطلبات عبر حاوية ثالثة تتضمن Certbot، وفي النهاية إدارة هذه الحاويات جميعًا من خلال Docker Compose.

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

أخيرًا يمكنك تسجيل الحاوية التي أعددتها على Dockerhub وجعلها متوفرة لأي نظام يحتوي دوكر سواء أكان خوادم أوبونتو أو بيئات افتراضية أو نظم حاويات عنقودية مثل Kubernetes.

ترجمة -وبتصرف- للمقال How to Build a Django and Gunicorn Application with Docker لصاحبيه Hanif Jetha و Justin Ellingwood.

اقرأ أيضًا


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

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

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



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

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

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

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   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.


×
×
  • أضف...