هذا المقال هو الثالث من سلسلة تعليمية تتضمن ثلاث مقالات عن الانتقال من الحاويات إلى كوبيرنتس Kubernetes باستخدام جانغو Django إطار العمل الخاص بتسريع تطوير تطبيقات الويب المبنية بلغة بايثون، في المقال الأول تعلمنا تعديل عينة تجريبية من تطبيق جانغو (تسمى التطبيق Polls) وفق منهجية Twelve-Factor الخاصة بتطوير تطبيقات ويب سحابية قابلة للتوسع والعمل ضمن الحاويات، وفي المقال الثاني تعرفنا على طرق توسيع هذا التطبيق (المعدّل المغلف في حاوية دوكر) توسيعًا أفقيًا وتأمينه بتشغيل وكيل عكسي أمامه هو خادم Nginx مع استخدام شهادات TLS مصدقة من Let's Encrypt، أما اليوم نشارككم كيفية نشر التطبيق المعدّل نفسه باستعمال عنقود كوبيرنتس Kubernetes Cluster.
كوبيرنتس kubernetes هو حل مفتوح المصدر لإدارة الحاويات، ميزته الأساسية أتمتة عمليات نشر وتوسيع وإدارة تطبيقات الويب المغلفة ضمن حاويات، وذلك من خلال مجموعة من الكائنات الخاصة مثل خرائط الإعدادات ConfigMaps والأسرار Secrets التي تسمح بتهيئة مركزية للبيئة من خارج الحاويات، ووحدات التحكم مثل النشر Deployments المسؤولة عن إعادة تشغيل الحاويات تلقائيًا في حال حدوث أي خلل وعن التوسعة التلقائية بتشغيل حاويات إضافية مماثلة لحاوية التطبيق عند ازدياد الطلب عليه، وأيضًا كائن الإدخال ingress أو المُدخل (تترجم أحيانًا إلى كائن الولوج واعتمدنا هنا كائن الإدخال والمُدخل حسب سياق الجملة) المسؤول عن توجيه حركة البيانات HTTP أو HTTPS المشفرة بشهادات TLS من المصادر الخارجية إلى خدمات كوبيرنتس الداخلية عبر قواعد وصول محددة مع وحدة التحكم مفتوحة المصدر ingress-nginx، أما التجديد الدوري للشهادات وتصديقها من Let's Encrypt تنجزه الوظيفة الإضافية مدير الشهادات cert-manager.
هذا المقال جزء من سلسلة قصيرة حول احتواء تطبيقات جانغو بحاويات وإدارتها وإليك فهرس السلسلة:
- بناء تطبيق جانغو بخادم Gunicorn ووضعه ضمن حاوية دوكر
- توسيع تطبيق جانغو وتأمينه عبر حاوية دوكر وخادم Nginx وخدمة Let's Encrypt
- نشر تطبيق جانغو آمن وقابل للتوسيع باستخدام كوبيرنتس
متطلبات بيئة العمل
ستحتاج المتطلبات التالية لتتمكن من التطبيق العملي لهذا المقال:
- عنقود كوبيرنتس بإصدار 1.15 أو أعلى، مع تفعيل خاصية التحكم بالوصول المستند على الدور Role-based access control (RBAC)، استخدمنا في المقال عنقود خدمة DigitalOcean، لكنك لست ملزمًا بذلك يمكنك إعداده بنفسك بالطريقة التي تناسبك.
- تثبيت أداة سطر الأوامر kubectl على جهازك المحلي وإعدادها للاتصال بالعنقود، يمكنك معرفة المزيد عن تثبيت kubectl بالاطلاع على توثيقات كوبيرنتس الرسمية. أما إن اعتمدت DigitalOcean فاستعن بالمقال إعداد الاتصال مع عنقود كوبيرنتس في DigitalOcean.
- حجز اسم نطاق لتطبيقك، سنعتمد في مقالنا على الاسم your_domain.com، مع العلم بإمكانية حصولك على اسم نطاق مجاني من Freenom.
- تثبيت وحدة التحكم ingress-nginx ومدير الشهادات cert-manager وضبط الإعدادات اللازمة للتحقق من الشهادات، يمكنك الاطلاع على كيفية إعداد Nginx Ingress ومدير الشهادات على كوبيرنتس.
- ربط اسم النطاق بعنوان IP العام لمُدخل موازن الحمل عبر إضافة سجل DNS من النوع A، يمكنك الحصول على تفاصيل إضافية حول DNS بالاطلاع على القسم الخاص بخدمة اسم النطاق DNS على أكاديمية حسوب.
- خدمة تخزين كائني متوافقة مع S3 ومع إضافة django-storages مثل DigitalOcean Space وظيفتها تأمين المساحة التخزينية اللازمة لملفات تطبيق جانغو الساكنة، مع مجموعة مفاتيح الوصول الخاصة بإدارة هذه المساحة، استرشد بكيفية إعداد مساحة على DigitalOcean Space وعدّل بما يلائم خدمة التخزين التي اخترتها.
- نظام إدارة قواعد بيانات متوافق مع جانغو مع إنشاء قاعدة بيانات ومستخدم خاص بالتطبيق، نحن اخترنا خادم PostgreSQL، اهتم بالتفاصيل التالية أثناء إنجازك لهذه النقطة لتتمكن من متابعة خطوات المقال:
- استخدم الاسم polls لقاعدة البيانات والاسم sammy لمستخدم قاعدة البيانات (هذه الأسماء ليست ملزمة ولكننا استخدمناها في المقال واستخدامك لها سيسهل عليك المتابعة)، لمزيد من التفاصيل حول الإنشاء استرشد بالخطوة /1/ من مقالنا السابق [بناء تطبيق جانغو بخادم Gunicorn ووضعه ضمن حاوية دوكر]()، مع العلم أن هذه المهمة يمكن إنجازها من أي خادم من الخوادم الثلاثة لبيئة العمل.
- اعتمدنا في المقال على نظام إدارة قواعد البيانات من نوع Managed PostgreSQL cluster الذي توفره DigitalOcean.
- لكن يمكنك تثبيت PostgreSQL وتشغيله وإعداده بنفسك مع الاستعانة بالفيديو التعليمي تثبيت وإعداد قاعدة بيانات PostgreSQL.
- حساب على Docker Hub، استرشد بتوثيقات دوكر الخاصة بالمستودعات لإنشائه.
- تثبيت دوكر على جهازك المحلي، استعن لذلك بمقال كيفية تثبيت دوكر واستخدامه على دبيان.
الخطوة 1: استنساخ التطبيق وضبط إعداداته
تتلخص هذه الخطوة باستنساخ كود التطبيق من مستودع GitHub بالإضافة إلى ضبط محددات الاتصال مع خادم قاعدة البيانات والمفاتيح الخاصة بخدمة التخزين الكائني المستخدمة.
سنحصل على كود التطبيق والملف Dockerfile من القسم polls-docker من مستودع GitHub الذي يتضمن نسخًا تجريبية من التطبيق المسمى Polls وهذه النسخ تتناسب مع أغراض جانغو التعليمية، بالإضافة لكونها مغلفة ومعدّلة بكل ما يلزم لتعمل بفعالية داخل الحاوية وتتكيف مع متغيرات بيئة التشغيل وفق ما تعلمنا في المقال الأول من هذه السلسلة.
اكتب الأمر التالي للاستنساخ:
git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git
توجه للمجلد django-polls عبر الأمر:
cd django-polls
يتضمن هذا المجلد كود التطبيق والملف Dockerfile الذي سيستخدمه دوكر لبناء صورة الحاوية، بالإضافة إلى الملف env الذي يتضمن متغيرات البيئة التي ستمرر للحاوية من بيئة التشغيل المحيطة.
استعرض الملف Dockerfile عبر تعليمة لينكس cat
:
cat Dockerfile
وستحصل على الخرج التالي:
FROM python:3.7.4-alpine3.10 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 CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi"]
وفقًا للخرج السابق، فإن Dockerfile سيستخدم صورة دوكر بإصدار بايثون 3.7.4، ويثبت اعتماديات وحزم بايثون اللازمة لكل من جانغو وخادم gunicorn وفق ملف متطلبات التطبيق django-polls/requirements.txt ومن ثم يحذف الملفات غير اللازمة بعد انتهاء التثبيت، وينسخ بعد ذلك كود التطبيق إلى الصورة ويسند قيمة المتغير PATH
، وأخيرًا سيحدد البوابة أو المنفذ 8000 لاستقبال حركة البيانات الواردة إلى الحاوية، ويشغل gunicorn
مع عمال workers
عدد /3/ عبر المنفذ 8000.
لتعرف المزيد عن كل مرحلة ضمن Dockerfile راجع مقالنا الأول السابق من السلسلة.
لنبني صورة الحاوية باستخدام docker build
:
docker build -t polls .
سميّنا الصورة باسم polls باستخدام الراية t-
ومررنا المسار الحالي .
كمسار context للتعليمة build فهو يتضمن كافة الملفات اللازمة لعملية البناء.
بعد انتهاء عملية البناء استخدم الأمر docker images
لاستعراض الصور المتاحة:
docker images
وستحصل على الخرج التالي:
REPOSITORY TAG IMAGE ID CREATED SIZE polls latest 80ec4f33aae1 2 weeks ago 197MB python 3.7.4-alpine3.10 f309434dea3a 8 months ago 98.7MB
لنضبط متغيرات البيئة تمهيدًا لعملية التشغيل docker run
، وذلك بتعديل الملف env باستعمال محرر النصوص:
nano env
سيظهر أمامك الملف env بالشكل التالي:
DJANGO_SECRET_KEY= DEBUG=True DJANGO_ALLOWED_HOSTS= DATABASE_ENGINE=postgresql_psycopg2 DATABASE_NAME=polls DATABASE_USERNAME= DATABASE_PASSWORD= DATABASE_HOST= DATABASE_PORT= STATIC_ACCESS_KEY_ID= STATIC_SECRET_KEY= STATIC_BUCKET_NAME= STATIC_ENDPOINT_URL= DJANGO_LOGLEVEL=info
كما ترى فبعض محددات الملف قيمها موجودة والبعض الآخر يحتاج إلى ضبط بما يناسب بيئتك، سنذكر المحددات التي تحتاج إلى ضبط بالترتيب:
-
المفتاح السري
DJANGO_SECRET_KEY
اختر له قيمة فريدة صعبة التخمين كما توصي توثيقات Docker نقدم لك في الخطوة /5/ من مقال كيفية إعداد تطبيق جانغو عبر DigitalOcean Managed Databases and Spaces إحدى طرق جانغو لتوليد المفاتيح. -
أما
DJANGO_ALLOWED_HOSTS
فأسند له اسم النطاق الخاص بكyour_domain.com
، أو يمكنك وضع الرمز*
لأغراض الاختبار فقط وليس ضمن بيئة العمل الفعلية فهذا الرمز يسمح لأي عنوان IP بالوصول، يحميك هذا المحدد من الهجمات المعتمدة على حقن ترويسة HTTP وفي حال أردت المزيد من المعلومات اطلع على توثيقات جانغو الخاصة بهذا المحدد ضمن Core Settings -
DATABASE_USERNAME
ضع اسم مستخدم قاعدة البيانات PostgreSQL الذي أنشأته أثناء إعدادك لمتطلبات العمل. -
DATABASE_NAME
ضع القيمةpolls
أو الاسم الذي اخترته لقاعدة البيانات أثناء إعدادك لمتطلبات العمل. -
كذلك الأمر لكلمة المرور
DATABASE_PASSWORD
. -
ضع اسم مضيف قاعدة البيانات
DATABASE_HOST
ورقم بوابة الاتصال معهاDATABASE_PORT
. -
أما المحددات
STATIC_ACCESS_KEY_ID
وSTATIC_SECRET_KEY
وSTATIC_BUCKET_NAME
وSTATIC_ENDPOINT_URL
فهي تتعلق بخدمة التخزين الكائني الخارجية، اضبطها بما يتناسب مع الخدمة التي تستخدمها.
احفظ التغيرات على الملف env بعد الانتهاء وأغلقه، ولننتقل للخطوة التالية.
الخطوة 2: إنشاء مخطط قاعدة البيانات ورفع ملفات التطبيق إلى وحدة التخزين الكائني
شغل صورة الحاوية polls:latest
عبر الأمر التالي:
docker run --env-file env polls sh -c "python manage.py makemigrations && python manage.py migrate"
وبموجبه الأمر سيجري تجاوز الأمر الافتراضي عند بدء التشغيل المحدد بتعليمة CMD
ضمن الملف Dockerfile وتنشئ أثناء التشغيل مخطط قاعدة بيانات التطبيق وهو الأمر الفرعي:
sh -c "python manage.py makemigrations && python manage.py migrate"
إن كنت تنفذ الأمر docker run
للمرة الأولى فستحصل على الخرج التالي الذي يبين نجاح إنشاء مخطط قاعدة البيانات:
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
مع العلم أن جانغو لن يجري أي عملية عندما تنفذ migrate
في المرات القادمة طالما لم يطرأ أي تغيير على مخطط قاعدة بيانات التطبيق.
نفذ بعدها أمر تشغيل جديد للحاوية مع استدعاء سطر الأوامر الخاص بالصدفة Shell كما يلي:
docker run -i -t --env-file env polls sh
تظهر بنتيجة هذا الأمر نافذة الصدفة shell ضمن الحاوية اكتب فيها تعليمة إنشاء المستخدم الخاص بتطبيق جانغو:
python manage.py createsuperuser
استكمل بياناته وهي اسم المستخدم وكلمة المرور والبريد الإلكتروني، واضغط بعدها على الاختصار Ctrl+D للخروج من الحاوية وإنهاء عملها.
أما العملية الأخيرة فهي جمع الملفات الساكنة الخاصة بالتطبيق collectstatic
ورفعها على خدمة التخزين الكائني وتتم وفق الأمر التالي علمًا أنها قد تستغرق بعض الوقت:
docker run --env-file env polls sh -c "python manage.py collectstatic --noinput"
يظهر بعدها الخرج التالي:
121 static files copied.
يمكنك الآن تشغيل التطبيق:
docker run --env-file env -p 80:8000 polls
ستحصل على الخرج:
[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
وأن البوابة 8000 للحاوية فُتحت لتستقبل حركة البيانات القادمة من البوابة 80 للخادم أوبونتو.
يمكنك الآن كتابة عنوان URL الخاص بالتطبيق polls بمتصفح الإنترنت واستعراضه، لاحظ أنك ستحصل على الخطأ 404 (لم يتم العثور على الصفحة) في حال اكتفيت بالعنوان:
http://APP_SERVER_1_IP/
بسبب عدم وجود بيانات للتطبيق تحت الجذر /
لذا اكتب العنوان التالي لتحصل على واجهة polls:
http://APP_SERVER_1_IP/polls
اكتب الرابط التالي:
http://APP_SERVER_1_ip/admin
وستتمكن من الوصول إلى واجهة الدخول للوحة التحكم الخاصة بالتطبيق:
أدخل بيانات المستخدم مدير التطبيق المنشأ سابقًا باستخدام الأمر createsuperuser
وستظهر أمامك واجهة الإدارة والتحكم الخاصة بالتطبيق.
تذكر أن تسليم المكونات الساكنة للتطبيق admin و polls يتم من خدمة التخزين الكائني ويمكنك اتباع هذه الخطوات لاختبار جودته وأيضًا التأكد من صحة إحضار الملفات.
بعد أن تنتهي من اختبار عمل التطبيق اضغط على الاختصار Ctrl+c في نافذة كتابة الأوامر السطرية التي تشغل حاوية دوكر لتنهي عمل الحاوية.
والآن بعد إتمام بناء صورة دوكر للتطبيق، وبناء مخطط قاعدة البيانات وتحميل مكونات التطبيق الساكنة إلى وحدة التخزين الكائني، وأخيرًا اختبار عمل الحاوية والتطبيق، يمكننا رفع الصورة إلى سجل صور مثل Docker Hub وهو موضوع خطوتنا التالية.
الخطوة 3: رفع صورة جانغو إلى Docker Hub
لتستخدم تطبيقك ضمن عنقود كوبيرنتس، ستحتاج أولًا رفع صورة التطبيق إلى سجل صور عام أو خاص مثل Docker Hub حتى تتمكن خدمة كوبيرنتس من سحب الصورة ونشر التطبيق.
علمًا أن لسجلات الصور نوعان عام وخاص، السجل العام مجاني إلاّ أنه يجعل صورك في متناول جميع المستخدمين ويمكّنهم من سحبها، أما الخاص فيعطي خصوصية أكبر إذا يسمح لك بمشاركة الصور مع مجموعة محددة من المستخدمين مثل زملاء عملك، وتقدم بعض الشركات -مثل سجل حاويات DigitalOcean على سبيل المثال لا الحصر- عروضًا مجانية لإنشاء سجلات خاصة محدودة، أما Docker Hub فيدعم النوعين العام والخاص.
سنقدم في مثالنا طريقة سحب الصور من سجل Docker Hub عام ويمكنك الاطلاع على توثيقات كوبيرنتس لمعرفة طريقة العمل مع السجلات الخاصة.
لنبدأ بتسجيل الدخول إلى Docker Hub وفق الحساب الذي ذكرناه في متطلبات العمل:
docker login
سيُطلب منك إدخال اسم المستخدم وكلمة المرور لحسابك وفق التالي:
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username:
تحمل صورة التطبيق في بيئتنا الوسم polls:latest
أعطها وسمًا جديدًا يتضمن اسم حسابك على Docker Hub واسم مستودعك باستخدام tag
وفق التالي:
docker tag polls:latest your_dockerhub_username/your_dockerhub_repo_name:latest
استعملنا في مثالنا الاسم sammy للحساب و sammy-django للمستودع وبذلك يصبح الأمر على الشكل التالي:
docker push sammy/sammy-django:latest
ستحصل على خرج يتضمن تفاصيل عملية الرفع، وبعد إتمام العملية ستصبح الصورة متاحة أمام كوبيرنتس على Docker Hub.
الخطوة 4: إعداد خريطة الإعدادات ConfigMap
خريطة الإعدادات ConfigMap هي كائن كوبيرنتس المسؤول عن تمرير متغيرات بيئة التشغيل العامة إلى العنقود مثل إعدادات التطبيق، ويشاركه المهمة كائن آخر يدعى السرّ Secret إلاّ أنه يُعنى بالمتغيرات الحساسة مثل البيانات الخاصة بواجهات التخاطب API أو بقاعدة البيانات ولذا فهو يخضع لقواعد خاصة لحمايته والتحكم بوصول المستخدمين إليه مثل تفعيل خاصية encryption at rest، وبناءً على هذا الاختلاف في طبيعة بيانات الكائنين فإن بيانات ConfigMap تخزن بشكل نصي صريح ومقروء للمستخدم، بينما تخزن بيانات Secret معماة بأسلوب base64.
تتشابه هذه الآلية في الواقع مع تمرير متغيرات البيئة لحاوية دوكر بالملف env
الذي يلقم هذه المعلومات لأمر تشغيل الحاوية docker-run
.
لنبدأ بإعداد ConfigMap، والخطوة الأولى هي إنشاء مجلد يدعى yaml
سنخزن ضمنه وثائق كوبرنيتس الأساسية:
mkdir yaml
cd
ومن ثم أنشئ ضمن المجلد ملف نصي باسم polls-configmap.yaml يتضمن خريطة الإعدادات وفق التالي:
nano polls-configmap.yaml
وهذا يعني أننا بصدد إنشاء كائن كوبرنتس من نوع ConfigMap يحمل الاسم polls-config.
الآن لننتقي من الملف env (الذي عملنا عليه في الخطوة /1/) البيانات العامة للتطبيق التي لا تتمتع بحساسية خاصة ونكتبها في الملف polls-configmap.yaml وذلك وفق الصيغة التالية:
apiVersion: v1 kind: ConfigMap metadata: name: polls-config data: DJANGO_ALLOWED_HOSTS: "*" STATIC_ENDPOINT_URL: "https://your_space_name.space_region.digitaloceanspaces.com" STATIC_BUCKET_NAME: "your_space_name" DJANGO_LOGLEVEL: "info" DEBUG: "True" DATABASE_ENGINE: "postgresql_psycopg2"
بالطبع مع وضع نفس القيم الموجودة في env وبخصوص المحدد DJANGO_ALLOWED_HOSTS
نعطيه القيمة *
كوننا نعمل في بيئة تجريبية.
نحفظ بعدها التغييرات ونغلق الملف تمهيدًا لإنشاء الكائن ConfigMap بالأمر الأمر التالي:
kubectl apply -f polls-configmap.yaml
وسنحصل على الخرج التالي الذي يبين نجاح العملية:
configmap/polls-config created
الخطوة 5: إعداد السر Secret
بيانات الكائن من نوع سرّ secret هي بيانات حساسة ومهمة كما ذكرنا سابقًا لذا فهي تُعمَّى بالأساس64، ولتحقيق ذلك لديك طريقتين إما ترميز البيانات خارجيًا من ثم كتابتها معماة ضمن الملف الذي سنعدّه للكائن، أو استخدام الأمر kubectl create
مع الراية from-env-file--
وهي الطريقة التي استعملناها هنا.
نعود للملف env استخدمنا متغيراته العامة في إنشاء الكائن Configmap والآن سنستخدم بقية المتغيرات التي تعد بيانات حساسة لإعداد الكائن secret
.
لننتقل للتطبيق العملي، انسخ الملف env إلى المجلد yaml
وسمي النسخة polls-secrets
وفق التالي:
cp ../env ./polls-secrets
افتح الملف polls-secrets
باستعمال محرر النصوص:
nano polls-secrets
ستحصل على بيانات الملف env المنسوخ كاملةً:
DJANGO_SECRET_KEY= DEBUG=True DJANGO_ALLOWED_HOSTS= DATABASE_ENGINE=postgresql_psycopg2 DATABASE_NAME=polls DATABASE_USERNAME= DATABASE_PASSWORD= DATABASE_HOST= DATABASE_PORT= STATIC_ACCESS_KEY_ID= STATIC_SECRET_KEY= STATIC_BUCKET_NAME= STATIC_ENDPOINT_URL= DJANGO_LOGLEVEL=info
الآن احذف منه المتغيرات التي كتبناها ضمن ConfigMap في الخطوة السابقة، وستبقى المتغيرات التالية ضمنه:
DJANGO_SECRET_KEY=your_secret_key DATABASE_NAME=polls DATABASE_USERNAME=your_django_db_user DATABASE_PASSWORD=your_django_db_user_password DATABASE_HOST=your_db_host DATABASE_PORT=your_db_port STATIC_ACCESS_KEY_ID=your_space_access_key STATIC_SECRET_KEY=your_space_access_key_secret
ضع القيم الخاصة بتطبيقك نفسها التي ضبطناها في الخطوة /1/ من المقال (مثل محددات قاعدة البيانات والمفتاح الخاص وغيرها)، واحفظ التغييرات على الملف polls-secrets
وأغلقه.
أنشئ الآن الكائن من نوع secret والذي يحمل الاسم polls-secret
من خلال الأمر التالي مع تمرير المتغيرات اللازمة للعنقود عبر الملف polls-secrets الذي أغلقته توًا:
kubectl create secret generic polls-secret --from-env-file=poll-secrets
وستحصل على الخرج الذي يبين نجاح الإنشاء:
secret/polls-secret created
استعرض بعدها وصف الكائن باستخدام kubectl describe
:
kubectl describe secret polls-secret
لاحظ القيم المرمزة في الخرج:
Name: polls-secret Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== DATABASE_PASSWORD: 8 bytes DATABASE_PORT: 5 bytes DATABASE_USERNAME: 5 bytes DJANGO_SECRET_KEY: 14 bytes STATIC_ACCESS_KEY_ID: 20 bytes STATIC_SECRET_KEY: 43 bytes DATABASE_HOST: 47 bytes DATABASE_NAME: 5 bytes
مررنا بذلك إعدادات التطبيق جانغو إلى داخل عنقود كوبيرنتس وتم تخزينها باستخدام كائنات من نوع Secret و ConfigMap، وأصبحت بيئتنا جاهزة لنشر التطبيق ضمن العنقود.
الخطوة 6: نشر التطبيق باستخدام وحدة تحكم النشر
وحدة النشر Deployment هي إحدى وحدات تحكم عنقود كوبيرنتس، تتولى إدارة التطبيقات عديمة الحالة stateless applications التي تعمل ضمن العنقود. إذ أنها تدير أعباء العمل صعودًا ونزولًا فتُدخل العناصر بالخدمة أو تخرجها تبعًا لضغط العمل، بالإضافة إلى إعطائها أوامر إعادة التشغيل للحاويات التي تفشل أثناء الإقلاع.
تتحكم وحدة النشر Deployment بواحد أو أكثر من كائنات Pod، أما Pod فهي أصغر عناصر عنقود كوبيرنتس وكل واحدة منها يمكن أن تتضمن حاوية أو أكثر، لمعرفة المزيد يمكنك الاطلاع على تعلم أساسيات Kubernetes على أكاديمية حسوب.
افتح ملفًا جديدًا يحمل الاسم polls-deployment.yaml عبر أي محرر تفضله وليكن عبر nano مثلًا:
nano polls-deployment.yaml
والصق ضمنه التعليمات التالية:
apiVersion: apps/v1 kind: Deployment metadata: name: polls-app labels: app: polls spec: replicas: 2 selector: matchLabels: app: polls template: metadata: labels: app: polls spec: containers: - image: your_dockerhub_username/app_repo_name:latest name: polls envFrom: - secretRef: name: polls-secret - configMapRef: name: polls-config ports: - containerPort: 8000 name: gunicorn
أكتب اسم الصورة التي بنيناها في الخطوة /2/ ولا تنسَ الإشارة للاسم الذي رفعنا به الصورة على Docker Hub.
لنشرح التعليمات السابقة بالترتيب.
أطلقنا على وحدة التحكم الاسم polls-app وميّزناها بعنوان label ذا القيمة المزدوجة app: polls
، وضبطنا عدد نسخ Pods المطلوب تشغيلها بنسختين وفق replicas: 2
أما إعدادات هذه Pods فتُحددها المواصفات المكتوبة تحت template
.
أما configMapRef
و secretRef
المستخدمين بعد envFrom
فيحددان على الترتيب قيم المتغيرات التي عرفناها من خلال كائن خريطة الإعدادات واسمه polls-config
والكائن سرّ واسمه polls-secret
، وفي نهاية الملف عرفنا بوابة الحاوية containerPort
وهي تحمل الرقم 8000 والاسم gunicorn
.
يمكنك تعلم المزيد عن وحدة تحكم النشر من خلال توثيقات كوبيرنتس.
أغلق الملف بعد حفظ التغييرات، لنباشر بإنشاء وحدة تحكم النشر عبر الأمر التالي:
kubectl apply -f polls-deployment.yaml
تحصل على الخرج الذي يؤكد نجاح العملية:
deployment.apps/polls-app created
يمكنك أيضًا التأكد من الإنشاء عبر الأمر:
kubectl get deploy polls-app
الذي يعطي النتيجة:
NAME READY UP-TO-DATE AVAILABLE AGE polls-app 2/2 2 2 6m38s
في حال واجهت أي خطأ في عمل وحدة التحكم وأردت التأكد من حالتها استخدم الأمر:
kubectl describe deploy
أما لفحص الـ Pods وحالتها فاستخدم:
kubectl get pod
وستحصل على الخرج التالي:
NAME READY STATUS RESTARTS AGE polls-app-847f8ccbf4-2stf7 1/1 Running 0 6m42s polls-app-847f8ccbf4-tqpwm 1/1 Running 0 6m57s
الخطوة 7: السماح للمصادر الخارجية بالوصول إلى الخدمة
سننشئ في هذه الخطوة خدمة كوبيرنتس خاصة بتطبيقنا، والخدمات في كوبيرنتس عبارة عن مفهوم مجرد يعرّف مجموعة من كائنات Pod لتظهر أمام المصادر الخارجية كخدمة شبكة واحدة أي كطرف واحد endpoint بغض النظر عن توقف أي Pod أو تشغيلها، والمصادر الخارجية في مثالنا هي حركة البيانات باتجاه التطبيق، وبالتالي من يطلب التطبيق جانغو سيطلب خدمة كوبيرنتس واحدة مستقرة ولن يتأثر بتوقف أو إضافة أي Pod داخلية.
تتعدد أنواع خدمات كوبيرنتس، مثل خدمة ClusterIP التي تعتمد على عنوان IP الداخلي للعنقود، وخدمة NodePort التي تعرض الخدمة على بوابة محددة من كل عقدة، وخدمة LoadBalancer التي توفر موازنة حمل سحابية لحركة مرور البيانات القادمة من المصادر الخارجية إلى Pods داخل العنقود وذلك عبر خدمات NodePorts الخاصة بكل عقدة، والتي يتم إنشاؤها تلقائيًا في هذه الحالة.
يمكنك تعلم المزيد عن هذه الخدمات من خلال توثيقات كوبيرنتس.
لنبدأ بإنشاء الخدمة الخاصة بتطبيق جانغو وسنعتمد النوع NodePort.
أنشئ ملفًا باسم polls-svc.yaml باستعمال محرر النصوص الذي تفضله وليكن باستعمال نانو nano:
nano polls-svc.yaml
والصق ضمنه التعليمات التالية:
apiVersion: v1 kind: Service metadata: name: polls labels: app: polls spec: type: NodePort selector: app: polls ports: - port: 8000 targetPort: 8000 ```
لاحظ التعليمات السابقة، نوع الخدمة هو NodePort، اسمها polls
وهي مميزة بالعنوان app: polls
، وحددنا من خلال مُحدِّد selector الربط مع Pods الواجهات الخلفية بالعنوان app: polls
و البوابة 8000.
أغلق الملف بعد حفظ التغييرات، ومن ثم أنشئ الخدمة بالأمر kubectl apply
:
kubectl apply -f polls-svc.yaml
ونحصل على ما يؤكد الإنشاء وفق الخرج التالي:
service/polls created
استعرض الخدمة وتأكد من إنشائها عبر kubectl get svc
:
kubectl get svc polls
وسيظهر لك الخرج التالي، الذي يتضمن عنوان IP الداخلي للعنقود وبوابة العقدة NodePort وهي في حالتنا 32654
:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE polls NodePort 10.245.197.189 <none> 8000:32654/TCP 59s
حصلنا على رقم البوابة، ونحتاج إلى عنوان IP الخارجي للخدمة للاتصال مع التطبيق، وسنحصل عليه بالأمر التالي:
kubectl get node -o wide
الذي يعطينا الخرج:
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME pool-7no0qd9e0-364fd Ready <none> 27h v1.18.8 10.118.0.5 203.0.113.1 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9 pool-7no0qd9e0-364fi Ready <none> 27h v1.18.8 10.118.0.4 203.0.113.2 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9 pool-7no0qd9e0-364fv Ready <none> 27h v1.18.8 10.118.0.3 203.0.113.3 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9
أصبحت معلوماتنا كاملة لنستعرض التطبيق بكتابة العنوان التالي في المتصفح:
http://203.0.113.1:32654/polls
من المفترض حصولك على واجهة التطبيق نفسها التي استعرضتها محليًا في الخطوة /1/:
تصفح أيضًا واجهة إدارة التطبيق admin/
للتأكد أكثر:
http://203.0.113.1:32654/admin
إذًا تطبيق جانغو الآن يستخدم حاويتين متماثلتين ويمكن الوصول له من خارج العنقود عبر خدمة كوبيرنتس مستقرة من نوع NodePort، والخطوة الأخيرة المتبقية لنا هي تأمين هذا الوصول ليتم عبر HTTPS وذلك بالاستفادة من وحدة التحكم ingress-nginx
(وهي من متطلبات بيئة العمل المفترض تحضيرها) وبإنشاء كائن إدخال يوجه الحركة الخارجية إلى الخدمة polls
.
الخطوة 8: إعداد HTTPS باستخدام Nginx Ingress ومدير الشهادات
توجه موارد كوبيرنتس من نوع المدخلات Ingresses حركة البيانات HTTP و HTTPS بمرونة إلى داخل العنقود، وذلك من خلال كائنات الإدخال التي تحدد قواعد التوجيه من المصادر الخارجية إلى خدمات كوبيرنتس الموجودة داخل عنقودك، ووحدات تحكم الإدخال التي تنفذ هذه القواعد عبر موازنة الحمل وتوجيه الطلبات إلى خدمات الواجهات الخلفية الأنسب.
لابد أنك جهزت المتطلبات الواردة في بداية المقال وأنت تحضر بيئة العمل، ومنها تثبيت وحدة التحكم Ingress-nginx والوظيفة الإضافية cert-manager الخاصة بأتمتة شهادات TLS، مع ما يلزم لتصديق النطاق من Let’s Encrypt، وفي أثناء العملية أنشأت كائن إدخال لاختبار صلاحية الشهادة والتشفير TLS مع اثنين من خدمات الواجهات الخلفية الوهمية المنشأة لهذا الغرض، لذا سنبدأ بحذف echo-ingress الذي أنشأته خلال التحضير وفق التالي:
kubectl delete ingress echo-ingress
يمكنك أيضًا حذف الخدمات الوهمية المنشأة أثناء التحضير عبر التعليمتين
kubectl delete svc
kubectl delete deploy
لكنه ليس أمرًا ملزمًا في حالتنا.
لديك أيضًا (من متطلبات بيئة العمل) سجل DNS من النوع A
يربط اسم لنطاقك بعنوان الـ IP العام لمُدخل موازن الحمل، فيمكنك إذًا إنشاء مُدخل لنطاقك your_domain.com
والخدمة polls
.
أنشئ ملف polls-ingress.yaml باستخدام محرر النصوص:
nano polls-ingress.yaml
والصق ضمنه التعليمات التالية:
[polls-ingress.yaml] apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: polls-ingress annotations: kubernetes.io/ingress.class: "nginx" cert-manager.io/cluster-issuer: "letsencrypt-staging" spec: tls: - hosts: - your_domain.com secretName: polls-tls rules: - host: your_domain.com http: paths: - backend: serviceName: polls servicePort: 8000
بموجب التعليمات السابقة، وصّفنا كائن إدخال يدعى polls-ingress
ليستخدم وحدة التحكم ingress-nginx ووظيفة مدير الشهادات cert-manager مع ضبط ClusterIssuer الخاص بها على خيار staging، كما فعّلنا الشهادات TLS لترتبط باسم النطاق الخاص بنا your_domain.com
أما ملف الشهادة والمفتاح الخاص تم تخزينهم في كائن من نوع سرّ يدعى polls-tls
، وأخيرًا وجهّنا حركة البيانات التي تطلب النطاق your_domain.com
لتمر عبر البوابة 8000
من الخدمة polls
.
احفظ الملف الآن وأغلقه، لننشئ الكائن بالاعتماد عليه، باستعمال الأمر التالي:
kubectl apply -f polls-ingress.yaml
سنحصل بعدها على الخرج:
ingress.networking.k8s.io/polls-ingress created
يمكنك استعراض وصف الكائن وتتبع حالته باستخدام kubectl describe
:
kubectl describe ingress polls-ingress
التي تعطي الخرج:
Name: polls-ingress Namespace: default Address: workaround.your_domain.com Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>) TLS: polls-tls terminates your_domain.com Rules: Host Path Backends ---- ---- -------- your_domain.com polls:8000 (10.244.0.207:8000,10.244.0.53:8000) Annotations: cert-manager.io/cluster-issuer: letsencrypt-staging kubernetes.io/ingress.class: nginx Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 51s nginx-ingress-controller Ingress default/polls-ingress Normal CreateCertificate 51s cert-manager Successfully created Certificate "polls-tls" Normal UPDATE 25s nginx-ingress-controller Ingress default/polls-ingress
وبنفس الطريقة تستطيع استعراض وصف polls-tls
للتأكد من صحته:
kubectl describe certificate polls-tls
وستظهر لك حالة الشهادة في الخرج:
. . . Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Issuing 3m33s cert-manager Issuing certificate as Secret does not exist Normal Generated 3m32s cert-manager Stored new private key in temporary Secret resource "polls-tls-v9lv9" Normal Requested 3m32s cert-manager Created new CertificateRequest resource "polls-tls-drx9c" Normal Issuing 2m58s cert-manager The certificate has been successfully issued
بذلك نكون قد وجّهنا الطلبات الواردة إلى التطبيق لتصبح HTTPS مشفرة، ولكن Clusterissuer في وضع staging ما يعني أن شهادتنا التجريبية المزيفة من Let's Encrypt لن تحظى بثقة معظم متصفحات الويب، أكتب الطلب التالي لنطاقك في نافذة سطر الأوامر:
wget -O - http://your_domain.com/polls
ولاحظ التحذير الظاهر في الخرج بسبب هذه الشهادة:
. . . ERROR: cannot verify your_domain.com's certificate, issued by ‘CN=Fake LE Intermediate X1’: Unable to locally verify the issuer's authority. To connect to your_domain.com insecurely, use `--no-check-certificate'.
سنتبع المقترح الوارد في الخرج أعلاه ونستخدم الراية no-check-certificate--
مع الطلب wget
لتجاوز التحذير الخاص بالشهادة وفق التالي:
wget --no-check-certificate -q -O - http://your_domain.com/polls
ستحصل عندها على الخرج المبين أدناه والذي يظهر صفحة HTML المقابلة للواجهة polls/
وأيضًا مسار التخزين الكائني الذي أُحضرت منه الصفحة:
<link rel="stylesheet" type="text/css" href="https://your_space.nyc3.digitaloceanspaces.com/django-polls/static/polls/style.css"> <p>No polls are available.</p>
وهذا دليل على نجاح طلب النطاق، ما يعني أن بإمكاننا الآن تبديل Clusterissuer إلى وضع المنتج production عوضًا عن staging عبر فتح الملف polls-ingress.yaml
:
nano polls-ingress.yaml
وتعديل قيمة cluster-issuer
كما ذكرنا:
[polls-ingress.yaml] apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: polls-ingress annotations: kubernetes.io/ingress.class: "nginx" cert-manager.io/cluster-issuer: "letsencrypt-prod" spec: tls: - hosts: - your_domain.com secretName: polls-tls rules: - host: your_domain.com http: paths: - backend: serviceName: polls servicePort: 8000
احفظ التغييرات على الملف وأغلقه، ولكن انتبه فهي لن تصبح نافذة حتى تُحدّث الكائن polls-ingress
باستخدام التعليمة kubectl apply
:
kubectl apply -f polls-ingress.yaml
سيشير الخرج إلى نجاح العملية:
ingress.networking.k8s.io/polls-ingress configured
شاهد التغير الذي طرأ على حالة الشهادات باستخدام:
kubectl describe certificate polls-tls
أو
kubectl describe ingress polls-ingress
وستحصل على الخرج التالي الذي يبين نجاح التحقق من الشهادات الجديدة وكونها مخزنة في السرّ polls-tls
:
. . . Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 23m nginx-ingress-controller Ingress default/polls-ingress Normal CreateCertificate 23m cert-manager Successfully created Certificate "polls-tls" Normal UPDATE 76s (x2 over 22m) nginx-ingress-controller Ingress default/polls-ingress Normal UpdateCertificate 76s cert-manager Successfully updated Certificate "polls-tls"
استعرض الآن تطبيقك
your_domain.com/polls
ولاحظ تفعيل HTTPS وعدم وجود أي تحذير بخصوص صلاحية الشهادة ويمكنك استعراض تفاصيل الشهادة وسلطة التصديق بالضغط على رمز القفل قرب الشريط المخصص لكتابة عنوان URL في متصفحك:
ننصحك بإجراء اختياري أخير هو تعديل نوع الخدمة polls من NodePort إلى ClusterIP إذ إن هذا النوع يقبل فقط الحركة القادمة من عنوان IP الداخلي للعنقود ما يمنح تطبيقك مزيدًا من الأمان:
افتح الملف polls-svc.yaml باستخدام المحرر:
nano polls-svc.yaml
بدل قيمة المحدد type
من NodePort
إلى ClusterIP
:
apiVersion: v1 kind: Service metadata: name: polls labels: app: polls spec: type: ClusterIP selector: app: polls ports: - port: 8000 targetPort: 8000
احفظ التغييرات على الملف وأغلقه، وحدّث بعدها الخدمة عبر الأمر:
kubectl apply -f polls-svc.yaml --force
سيؤكد لك الخرج التالي نجاح تعديل الخدمة:
service/polls configured
تحقق من حالة الخدمة المحدثة باستخدام:
kubectl get svc polls
ولاحظ معلومات الخرج:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE polls ClusterIP 10.245.203.186 <none> 8000/TCP 22s
وبذلك قيدّت الوصول لتطبيقك ليكون حصرًا من خلال اسم النطاق والمُدخل المنشأ في هذه الخطوة.
خاتمة
تعلمنا في هذا المقال طريقة نشر تطبيق جانغو قابل للتوسيع ومؤمن ببروتوكول HTTPS باستخدام عنقود كوبيرنتس، مع تسليم ملفات التطبيق الساكنة من وحدة تخزين كائني خارجية، وتوفير المرونة التامة لزيادة أو إنقاص عدد Pods التي تُخدم التطبيق بسرعة وبسهولة عبر المحدد replicas
من ملف الإعدادات الخاص بوحدة تحكم النشر Deployment والتي أسميناها polls-app
، ونذكرك بإمكانية تسريع التسليم باستخدام شبكة توصيل المحتوى CDN كإجراء اختياري.
ترجمة -وبتصرف- للمقال How To Deploy a Scalable and Secure Django Application with Kubernetes لصاحبه Hanif Jetha.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.