كيف تبني تطبيق Flask وتنشره باستخدام Docker على أوبنتو 18.04


هشام رزق الله

يعتبر دوكر Docker من أشهر التطبيقات مفتوحة المصدر لإنشاء وإدارة ونشر استنساخ التطبيقات باستخدام الحاويات، فالحاوية هي حزمة تحتوي على متطلبات التطبيق للعمل على مستوى نظام التشغيل أي أن التطبيق المنشور باستخدام Docker يعمل في بيئته الخاصة ويُتعامل مع متطلباته بشكل منفصل.

أما Flask فهو إطار ويب مصغّر (micro-framework) مبني باستخدام لغة بايثون، وتعود تسميته بالإطار المصغّر لأنه لا يحتاج إلى أدوات محدّدة أو مكونات إضافيّة لكي يعمل، بالإضافة إلى أنه يتميّز بالخفّة والمرونة وبدرجة عالية من التنظيم مما يجعل المبرمجين يفضلونه على بقية الإطارات.

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

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

لمتابعة هذا الدرس، ستحتاج إلى ما يلي:

  • مستخدم عادي - ليس جذر - مع صلاحيات sudo معّد باستخدام هذا الدليل
  • خادم أوبنتو 18.04 مع Docker، يمكنك إعداده عن طريق اتباع الخطوات في هذا الدرس أو عن طريق استخدام ميّزة التثبيت الخاصة بـ DigitalOcean في هذا الدرس
  • تثبيت Nginx بإتباع الخطوة الأولى من هذا الدرس

الخطوة الأولى - إعداد تطبيق Flask

ستحتاج إلى إنشاء هيكل مجلدات تطبيقك، لذلك أنشئ مجلدًا في ‎/var/www وسمّهِ على سبيل المثال TestApp:

sudo mkdir /var/www/TestApp

ثم انتقل إلى هذا المجلد باستخدام الأمر:

cd /var/www/TestApp

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

sudo mkdir -p app/static app/templates

تشير راية ‎-p إلى أن mkdir سينشئ المجلد مع جميع المجلدات الرئيسية غير الموجودة، وفي هذه الحالة، سينشئ mkdir مجلد الأب app حتى يتمكن من إنشاء مجلدات static و templates داخله.

سيحتوي المجلد app على جميع الملفات المتعلّقة بتطبيق Flask مثل الواجهات (views) والمخططات الأوليّة (blueprints). فالواجهة هي الشيفرة البرمجيّة التي تكتبها للاستجابة إلى طلبات تطبيقك، أما المخططات الأوليّة فتنشئ مكونات التطبيق وتدعم الأنماط الشائعة داخل التطبيق أو عبر تطبيقات مختلفة.

ستضع جميع الأصول مثل الصور وملفات CSS وملفات جافاسكربت في مجلد static ، وأما بالنسبة لقوالب HTML فستضعهم في مجلد templates.

والآن، بعد أن أنشأنا هيكل المجلدات الأساسية، فسننشئ الملفات التي نحتاجها لتشغيل تطبيق Flask. أنشئ أولًا ملف ‎__init__.py داخل مجلد app، سيجعل هذا الملف مفسر بايثون يتعامل مع مجلد app كحزمة.

أنشئ ملف ‎__init__.py باستخدام الأمر التالي:

sudo nano app/__init__.py

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

بعد ذلك، ستضيف شيفرة برمجيّة إلى ‎__init__.py لإنشاء نسخة من Flask واستدعاء المنطق الموجود في ملف views.py، والذي ستنشئه بعد إنهائك لهذا الملف. أضف الشيفرة البرمجيّة التاليّة إلى الملف الجديد:

from flask import Flask
app = Flask(__name__)
from app import views

ستنشئ الآن ملف views.py في مجلد app، سيحتوي هذا الملف على أغلب منطق التطبيق.

sudo nano app/views.py

بعد ذلك أضف هذه الشيفرة البرمجيّة إلى ملف views.py، ستُعيد هذه الشيفرة البرمجيّة السلسلة النصيّة ‎hello world!‎ إلى زوار صفحة الويب.

from app import app

@app.route('/')
def home():
   return "hello world!"

يسمى السطر ‎@app.route‎‏ فوق الدالة بالمزخرِف ويعمل على تعديل الدالة التي بعده، وفي هذه الحالة، سيخبر المزخرِف Flask أي رابط URL ستنفّذ دالة home‎‎()‎، وأما بالنسبة إلى النص hello world المُعاد من الدالة home فسيظهر للمستخدم في المتصفّح.

الآن، بعد الانتهاء من ملف views.py، فنحن مستعدين لإنشاء ملف uwsgi.ini الذي سيحتوي على إعدادات uWSGI لتطبيقنا وهذا الأخير عبارة عن خيار نشر لـ Nginx والذي هو خادم لكل من البروتوكول والتطبيق، أي يخدم بروتوكولات uWSGI و FastCGI و HTTP.

استخدم الأمر التالي لإنشاء هذا الملف:

sudo nano uwsgi.ini

بعد ذلك، أضف هذه الإعدادات إلى الملف لإعداد خادم uWSGI:

[uwsgi]
module = main
callable = app
master = true

تعرّف هذه الشيفرة البرمجية الوحدة التي سيعمل منها تطبيق Flask، والتي هي في هذه الحالة ملف main.py، ولقد أشرنا إليه بـ main. وبالنسبة إلى خيار callable فهو يخبر uWSGI باستخدام نسخة من app المصدّر من التطبيق الرئيسي. ويسمح الخيار master للتطبيق أن يعمل دائمًا ولن يتوقف إلا لبعض الوقت عند إعادة تحميل التطبيق كاملًا.

بعد ذلك، أنشئ ملف main.py ليكون مدخل تطبيقك، فالمدّخل يخبر uWSGI عن كيفية التعامل مع تطبيقك.

sudo nano main.py

والآن، أضف السطر التالي إلى الملف حيث أن هذا الأخير سيستدعي نسخة من Flask يسمى app من حزمة التطبيق التي أنشأناها سابقًا.

from app import app

وفي النهاية، أنشئ الملف requirements.txt لتحديد الاعتماديات التي سيثبتها مدير الحزم pip إلى نشر Docker الخاص بك:

sudo nano requirements.txt

أضف السطر التالي في الملف ‎/var/www/TestApp/app/requirements.txt لإضافة Flask كتبعيّة:

Flask==1.0.2

يحدد هذا السطر نسخة Flask التي يجب تثبيتها، والتي هي في وقت كتابة هذا المقال، النسخة 1.0.2، ويمكنك التأكد من التحديثات على الموقع الرسمي لـ Flask.

والآن لقد أنتهيت من إعداد تطبيق Flask ومستعد لإعداد Docker.

الخطوة الثانية - إعداد Docker

سننشئ في هذه الخطوة ملفين، Dockerfile و start.sh لإنشاء نشر Docker الخاص بك، فملف Dockerfile هو مستند نصي يحتوي على الأوامر التي تُستخدم لتجميع الصورة (image) وأما بالنسبة لملف start.sh فهو سكربت shell الذي سيبني الصورة وسينشئ الحاوية من ملف Dockerfile.

أنشئ أولًا ملف Dockerfile:

sudo nano Dockerfile

بعد ذلك، أضف إعداداتك التي ترغب بها إلى Dockerfile، ستحدّد هذه الأوامر كيف ترغب ببناء الصورة وكيف ستضاف المتطلبات الإضافيّة.

  • الملف ‎/var/www/TestApp/Dockerfile:
FROM tiangolo/uwsgi-nginx-flask:python3.6-alpine3.7
RUN apk --update add bash nano
ENV STATIC_URL /static
ENV STATIC_PATH /var/www/app/static
COPY ./requirements.txt /var/www/requirements.txt
RUN pip install -r /var/www/requirements.txt

في هذا المثال، سنبني صورة Docker (أي image) من صورة موجودة بالفعل وهي tiangolo/uwsgi-nginx-flask ويمكنك إيجادها على DockerHub وتعتبر هذه الصورة من أفضل الصور الموجودة لأنها تدعم العديد من نسخ بايثون بالإضافة إلى أنظمة التشغيل المختلفة.

أول سطرين سيحددان الصورة الرئيسيّة التي ستستخدمها لتشغيل التطبيق وتثبيت معالج أوامر Bash والمحرّر النصي nano، وستثبت أيضًا عميل git للسحب والدفع إلى خدمات استضافة التحكّم بالإصدارات مثل GitHub و GitLab و Bitbucket. أما بالنسبة إلى ENV STATIC_URL /static فهو متغيّر البيئة الخاص بصورة Docker الحالية ويحدد الملف الثابت (static) الذي سيحتوي على جميع الأصول مثل الصور وملفات CSS و ملفات جافا سكربت.

بالنسبة لآخر سطرين فستنسخ الملف requirements.txt إلى الحاوية ومن ثم ستثبّت التبعيات.

والآن، بعد أن أنهينا ملف Dockerfile، أصبحنا مستعدين لإنشاء ملف start.sh الذي سيبني حاوية Docker، وقبل كتابة سكربت start.sh، تأكد من وجود منفذ (port) مفتوح لاستخدامه في الإعدادات، ويمكنك التأكد من ذلك عن طريق تشغيل الأمر التالي:

sudo nc localhost 56733 < /dev/null; echo $?

إذا كانت مخرجات الأمر السابق هي 1 فهذا المنفذ مفتوح ويمكن استخدامه، وخلافا ذلك، ستحتاج إلى اختيار منفذ آخر لاستخدامه في ملف إعدادات start.sh.

بمجرّد أن تجد منفذ مفتوح للاستخدام، أنشئ سكربت start.sh:

sudo nano start.sh

سيبني سكربت start.sh ملف Dockerfile وينشئ الحاوية من صورة Docker، ولهذا سنضيف إلى الملف ‎‎/var/www/TestApp/start.sh‎ الإعدادات التالية:

#!/bin/bash
app="docker.test"
docker build -t ${app} .
docker run -d -p 56733:80 \
  --name=${app} \
  -v $PWD:/app ${app}

يسمى السطر الأول بـ shebang، وهو يحدد أن هذا الملف هو ملف bash وستُنفّذ أوامره، وأما السطر الثاني فيحدّد اسم الصورة والحاوية وستُحفظ على شكل متغيّر يسمى app. أما السطر التالي فيخبر Docker ببناء الصور من ملف Dockerfile الموجود في المجلّد الحالي، وفي هذا المثال، سينشئ صورة تسمى docker.test.

الأسطر الثلاثة الأخيرة تنشئ حاوية تسمى docker.test والتي تعمل على المنفذ 56733، وفي النهاية، تربط المجلد الحالي بمجلد ‎‎‎/var/www في الحاوية.

يمكنك استخدام الراية ‎-d لتشغيل الحاوية في وضع العفريت (daemon mode) أو كعملية خلفيّة (background process)، ويمكنك تضمين الراية ‎-p لربط منفذ الخادم إلى منفذ معيّن في حاوية Docker، وفي هذه الحالة ستربط منفذ 59733 بمنّفذ 80 في حاوية Docker. بالنسبة إلى الراية ‎-v ‏(volume) ‏فيحدد مكان Docker لوصله في الحاوية.

شغّل سكربت start.sh لإنشاء صورة Docker وبناء حاوية من الصورة:

sudo bash start.sh

بمجرّد الانتهاء من الأمر السابق، استخدم الأمر التالي لعرض جميع الحاويات التي تعمل:

sudo docker ps

سيظهر لك شيء مشابه لهذا:

CONTAINER ID        IMAGE               COMMAND                 CREATED             STATUS              PORTS                           NAMES
58b05508f4dd        docker.test         "/entrypoint.sh /sta…"   12 seconds ago     Up 3 seconds    443/tcp, 0.0.0.0:56733->80/tcp   docker.test

سترى أن حاوية docker.test تعمل، والآن لنزور عنوان IP في المنفذ الذي اخترناه في متصفحك: http://ip-address:56733

ستشاهد صفحة مشابهة لهذه الصفحة:

001HelloWorld.png

في هذه الخطوة نجحنا في نشر تطبيق Flask على Docker، سنستخدم في الخطوة التالية القوالب لعرض المحتوى إلى المستخدمين.

الخطوة الثالثة - خدمة ملفات القوالب

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

إبدأ بإنشاء ملف home.html في مجلد app/templates:

sudo nano app/templates/home.html

أضف الشيفرة البرمجية إلى قالبك لإنشاء صفحة HTML5 تحتوي على عنوان وبعض النصوص.

  • الملف ‎/var/www/TestApp/app/templates/home.html:
<!doctype html>

<html lang="en-us">   
  <head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <title>Welcome home</title>
  </head>

  <body>
    <h1>Home Page</h1>
    <p>This is the home page of our application.</p>
  </body>
</html>

بعد ذلك، عدّل على ملف app/views.py لخدمة الملف المنشئ حديثًا:

sudo nano app/views.py

أضف أولا السطر التالي إلى بداية ملفك لإستدعاء أسلوب render_template من Flask والذي يرمّز صفحة HTML لتصيير (render) صفحة ويب للمستخدم.

from flask import render_template
...

أضف مسار (route) آخر في نهاية الملف لتصيير ملف القالب حيث ستحدّد هذه الشيفرة البرمجية أن المستخدمين سيخدمون بمحتوى ملف home.html عند زيارة مسار ‎/template في تطبيقك.

...

@app.route('/template')
def template():
    return render_template('home.html')

سيبدو ملف app/views.py بعد تحديثه كالتالي:

from flask import render_template
from app import app

@app.route('/')
def home():
    return "Hello world!"

@app.route('/template')
def template():
    return render_template('home.html')

من أجل تفعيل هذه التغييرات، ستحتاج إلى إيقاف وإعادة تشغيل حاويات Docker. شغّل الأمر التالي لإعادة بناء الحاوية:

sudo docker stop docker.test && sudo docker start docker.test

زر تطبيقك الآن على http://your-ip-address:56733/template لمشاهدة خدمة القالب الجديد.

002HomePage.png

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

الخطوة الرابعة - تحديث التطبيق

ستحتاج في بعض الأحيان إلى القيام ببعض التغييرات في تطبيقك مثل تثبيت متطلبات جديدة، تحديث حاوية Docker أو تغيير شيفرة HTML أو المنطق، لذلك، سنتحدّث الآن عن إعداد touch-reload للقيام بهذه التغييرات دون الحاجة إلى إعادة تشغيل حاوية Docker.

يراقب بايثون autoreloading نظام الملفات للتغييرات وتحديث التطبيق عند إيجاد تغيير، وهذه العملية غير منصوح بها عند مرحلة الإنتاج (production) لأنها ستستهلك الكثير من الموارد. سنستخدم touch-reload لمراقبة التغييرات لملف معيّن وإعادة التحميل عند تحديث أو استبدال هذا الملف.

لفعل ذلك، نبدأ بفتح ملف uwsgi.ini:

sudo nano uwsgi.ini

ونضيف هذا السطر الأخير إلى نهاية ملف uwsgi.ini كما هو موضح في الشيفرة التالية:

module = main
callable = app
master = true
touch-reload = /app/uwsgi.ini

يحدد هذا السطر الملف الذي يجب التعديل عليه لإعادة تحميل كامل التطبيق.

لمشاهدة هذا، غيّر قليلا في تطبيقك، فعلى سبيل المثال، افتح ملف app/views.py:

sudo nano app/views.py

استبدل السلسلة النصيّة التي تعود من الدالة home:

from flask import render_template
from app import app

@app.route('/')
def home():
    return "<b>There has been a change</b>"

@app.route('/template')
def template():
    return render_template('home.html')

والآن، إذا فتحت الصفحة الرئيسية لتطبيقك على http://ip-address:56733 ستلاحظة عدم ظهور التغييرات، وهذا بسبب أن شرط إعادة التحميل هو تغيير ملف uwsgi.ini، لذا استخدم touch لتفعيل هذا الشرط:

sudo touch uwsgi.ini

أعد تحميل صفحة الرئيسية في تطبيقك على متصفحك مرّة أخرى، ستظهر لك التغييرات:

003ThereHasBeenAChange.png

في هذه الخطوة، أعددت شرط touch-reload لتحديث تطبيقك بعد القيام بتغييرات.

الخلاصة

في هذا الدرس، أنشأت ونشرت تطبيق Flask على حاوية Docker، ولقد أعددّت touch-reload لإعادة تحميل التطبيق دون الحاجة إلى إعادة تشغيل الحاوية.

يمكنك الآن التوسع أكثر واكتشاف Docker بطريقة معمّقة، ويمكنك البدء بالتوثيق الرسمي

ترجمة -وبتصرف- للمقال How To Build and Deploy a Flask Application Using Docker on Ubuntu 18.04 لصاحبه Michael Okoh





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


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



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن