ورشة عمل RHEL 8 Beta: بناء تطبيقات الويب العاملة

يوفر RHEL 8 Beta للمطورين العديد من الميزات الجديدة، والتي قد يستغرق إدراجها صفحات، ومع ذلك، فإن تعلم أشياء جديدة يكون دائمًا أفضل من الناحية العملية، لذلك نقدم أدناه ورشة عمل حول إنشاء بنية أساسية للتطبيقات فعليًا استنادًا إلى Red Hat Enterprise Linux 8 Beta.

ورشة عمل RHEL 8 Beta: بناء تطبيقات الويب العاملة

لنأخذ Python، لغة برمجة شائعة بين المطورين، كأساس، مزيج من Django وPostgreSQL، وهو مزيج شائع إلى حد ما لإنشاء التطبيقات، وقم بتكوين RHEL 8 Beta للعمل معهم. ثم سنقوم بإضافة بضعة مكونات أخرى (غير مصنفة).

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

لنبدأ بنشر صورة RHEL 8 Beta VM. يمكنك تثبيت جهاز افتراضي من البداية، أو استخدام صورة ضيف KVM المتوفرة مع اشتراكك التجريبي. عند استخدام صورة ضيف، ستحتاج إلى تكوين قرص مضغوط افتراضي يحتوي على بيانات التعريف وبيانات المستخدم لتهيئة السحابة (cloud-init). لا تحتاج إلى القيام بأي شيء خاص فيما يتعلق ببنية القرص أو الحزم المتوفرة؛ أي تكوين سيفي بالغرض.

دعونا نلقي نظرة فاحصة على العملية برمتها.

تثبيت جانغو

مع الإصدار الأحدث من Django، ستحتاج إلى بيئة افتراضية (virtualenv) مع Python 3.5 أو أحدث. في الملاحظات التجريبية، يمكنك أن ترى أن Python 3.6 متاح، فلنتحقق مما إذا كان هذا هو الحال بالفعل:

[cloud-user@8beta1 ~]$ python
-bash: python: command not found
[cloud-user@8beta1 ~]$ python3
-bash: python3: command not found

تستخدم Red Hat لغة Python بشكل نشط كمجموعة أدوات نظام في RHEL، فلماذا يحدث هذا؟

الحقيقة هي أن العديد من مطوري Python ما زالوا يفكرون في الانتقال من Python 2 إلى Python 2، في حين أن Python 3 نفسها قيد التطوير النشط، وتظهر المزيد والمزيد من الإصدارات الجديدة باستمرار. لذلك، لتلبية الحاجة إلى أدوات نظام مستقرة مع إتاحة الوصول للمستخدمين إلى إصدارات جديدة مختلفة من بايثون، تم نقل نظام بايثون إلى حزمة جديدة ووفر القدرة على تثبيت كل من بايثون 2.7 و3.6. يمكن العثور على مزيد من المعلومات حول التغييرات وسبب إجرائها في المنشور الموجود في مدونة لانغدون وايت (لانجدون وايت).

لذلك، للحصول على عمل بايثون، ما عليك سوى تثبيت حزمتين، مع تضمين python3-pip كتبعية.

sudo yum install python36 python3-virtualenv

لماذا لا تستخدم استدعاءات الوحدة المباشرة كما يقترح لانغدون وتثبيت pip3؟ مع الأخذ في الاعتبار الأتمتة القادمة، من المعروف أن Ansible سيتطلب تثبيت نقطة للتشغيل، نظرًا لأن وحدة النقطة لا تدعم Virtualenvs مع نقطة مخصصة قابلة للتنفيذ.

مع وجود مترجم python3 فعال تحت تصرفك، يمكنك متابعة عملية تثبيت Django والحصول على نظام عمل بالإضافة إلى مكوناتنا الأخرى. هناك العديد من خيارات التنفيذ المتاحة على شبكة الإنترنت. هناك إصدار واحد معروض هنا، ولكن يمكن للمستخدمين استخدام العمليات الخاصة بهم.

سنقوم بتثبيت إصدارات PostgreSQL وNginx المتوفرة في RHEL 8 افتراضيًا باستخدام Yum.

sudo yum install nginx postgresql-server

سيتطلب PostgreSQL استخدام psycopg2، لكنه يجب أن يكون متاحًا فقط في بيئة Virtualenv، لذا سنقوم بتثبيته باستخدام pip3 مع Django وGunicorn. لكن نحتاج أولاً إلى إعداد virtualenv.

هناك دائمًا الكثير من الجدل حول موضوع اختيار المكان المناسب لتثبيت مشاريع Django، ولكن عندما تكون في شك، يمكنك دائمًا اللجوء إلى Linux Filesystem Hierarchy Standard. على وجه التحديد، تقول FHS أن /srv يستخدم من أجل: "تخزين البيانات الخاصة بالمضيف - البيانات التي ينتجها النظام، مثل بيانات خادم الويب والبرامج النصية، والبيانات المخزنة على خوادم FTP، ومستودعات نظام التحكم". -2.3 في عام 2004)."

هذه هي حالتنا بالضبط، لذلك نضع كل ما نحتاجه في /srv، المملوك لمستخدم التطبيق لدينا (مستخدم السحابة).

sudo mkdir /srv/djangoapp
sudo chown cloud-user:cloud-user /srv/djangoapp
cd /srv/djangoapp
virtualenv django
source django/bin/activate
pip3 install django gunicorn psycopg2
./django-admin startproject djangoapp /srv/djangoapp

يعد إعداد PostgreSQL وDjango أمرًا سهلاً: قم بإنشاء قاعدة بيانات، وإنشاء مستخدم، وتكوين الأذونات. أحد الأشياء التي يجب وضعها في الاعتبار عند تثبيت PostgreSQL في البداية هو البرنامج النصي لإعداد postgresql المثبت مع حزمة postgresql-server. يساعدك هذا البرنامج النصي على تنفيذ المهام الأساسية المرتبطة بإدارة مجموعة قاعدة البيانات، مثل تهيئة المجموعة أو عملية الترقية. لتكوين نسخة PostgreSQL جديدة على نظام RHEL، نحتاج إلى تشغيل الأمر:

sudo /usr/bin/postgresql-setup -initdb

يمكنك بعد ذلك بدء PostgreSQL باستخدام systemd، وإنشاء قاعدة بيانات، وإعداد مشروع في Django. تذكر إعادة تشغيل PostgreSQL بعد إجراء تغييرات على ملف تكوين مصادقة العميل (عادةً pg_hba.conf) لتكوين تخزين كلمة المرور لمستخدم التطبيق. إذا واجهت صعوبات أخرى، فتأكد من تغيير إعدادات IPv4 وIPv6 في الملف pg_hba.conf.

systemctl enable -now postgresql

sudo -u postgres psql
postgres=# create database djangoapp;
postgres=# create user djangouser with password 'qwer4321';
postgres=# alter role djangouser set client_encoding to 'utf8';
postgres=# alter role djangouser set default_transaction_isolation to 'read committed';
postgres=# alter role djangouser set timezone to 'utc';
postgres=# grant all on DATABASE djangoapp to djangouser;
postgres=# q

في الملف /var/lib/pgsql/data/pg_hba.conf:

# IPv4 local connections:
host    all        all 0.0.0.0/0                md5
# IPv6 local connections:
host    all        all ::1/128                 md5

في الملف /srv/djangoapp/settings.py:

# Database
DATABASES = {
   'default': {
       'ENGINE': 'django.db.backends.postgresql_psycopg2',
       'NAME': '{{ db_name }}',
       'USER': '{{ db_user }}',
       'PASSWORD': '{{ db_password }}',
       'HOST': '{{ db_host }}',
   }
}

بعد تكوين ملف settings.py في المشروع وإعداد تكوين قاعدة البيانات، يمكنك بدء تشغيل خادم التطوير للتأكد من أن كل شيء يعمل. بعد بدء تشغيل خادم التطوير، من الجيد إنشاء مستخدم مسؤول لاختبار الاتصال بقاعدة البيانات.

./manage.py runserver 0.0.0.0:8000
./manage.py createsuperuser

WSGI؟ واي؟

يعد خادم التطوير مفيدًا للاختبار، ولكن لتشغيل التطبيق، يجب عليك تكوين الخادم والوكيل المناسبين لواجهة بوابة خادم الويب (WSGI). هناك العديد من المجموعات الشائعة، على سبيل المثال، Apache HTTPD مع uWSGI أو Nginx مع Gunicorn.

تتمثل مهمة واجهة بوابة خادم الويب في إعادة توجيه الطلبات من خادم الويب إلى إطار عمل الويب Python. يعد WSGI من بقايا الماضي الرهيب عندما كانت محركات CGI موجودة، واليوم WSGI هو المعيار الفعلي، بغض النظر عن خادم الويب أو إطار عمل Python المستخدم. ولكن على الرغم من استخدامه على نطاق واسع، لا يزال هناك العديد من الفروق الدقيقة عند العمل مع هذه الأطر، والعديد من الخيارات. في هذه الحالة، سنحاول إنشاء تفاعل بين Gunicorn وNginx عبر المقبس.

نظرًا لأن كلا هذين المكونين مثبتان على نفس الخادم، فلنحاول استخدام مقبس UNIX بدلاً من مقبس الشبكة. نظرًا لأن الاتصال يتطلب مأخذ توصيل على أي حال، فلنحاول اتخاذ خطوة أخرى وتهيئة تنشيط المقبس لـ Gunicorn عبر systemd.

عملية إنشاء خدمات تنشيط المقبس بسيطة للغاية. أولاً، يتم إنشاء ملف وحدة يحتوي على توجيه ListStream يشير إلى النقطة التي سيتم إنشاء مقبس UNIX عندها، ثم ملف وحدة للخدمة حيث يشير التوجيه Requires إلى ملف وحدة المقبس. بعد ذلك، في ملف وحدة الخدمة، كل ما تبقى هو استدعاء Gunicorn من البيئة الافتراضية وإنشاء ربط WSGI لمقبس UNIX وتطبيق Django.

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

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

أنت الآن بحاجة إلى تكوين البرنامج الخفي Gunicorn.

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

[Service]
User=cloud-user
Group=cloud-user
WorkingDirectory=/srv/djangoapp

ExecStart=/srv/djangoapp/django/bin/gunicorn 
         —access-logfile - 
         —workers 3 
         —bind unix:gunicorn.sock djangoapp.wsgi

[Install]
WantedBy=multi-user.target

بالنسبة لـ Nginx، يعد إنشاء ملفات تكوين الوكيل وإعداد دليل لتخزين المحتوى الثابت أمرًا بسيطًا إذا كنت تستخدم واحدًا. في RHEL، توجد ملفات تكوين Nginx في /etc/nginx/conf.d. يمكنك نسخ المثال التالي إلى الملف /etc/nginx/conf.d/default.conf وبدء الخدمة. تأكد من تعيين server_name ليطابق اسم المضيف الخاص بك.

server {
   listen 80;
   server_name 8beta1.example.com;

   location = /favicon.ico { access_log off; log_not_found off; }
   location /static/ {
       root /srv/djangoapp;
   }

   location / {
       proxy_set_header Host $http_host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_pass http://unix:/run/gunicorn.sock;
   }
}

ابدأ تشغيل مقبس Gunicorn وNginx باستخدام systemd وستكون جاهزًا لبدء الاختبار.

خطأ في البوابة سيئة؟

إذا قمت بإدخال العنوان في متصفحك، فمن المرجح أن تتلقى خطأ 502 Bad Gateway. قد يكون السبب هو تكوين أذونات مقبس UNIX بشكل غير صحيح، أو قد يكون بسبب مشكلات أكثر تعقيدًا تتعلق بالتحكم في الوصول في SELinux.

في سجل أخطاء nginx، يمكنك رؤية سطر مثل هذا:

2018/12/18 15:38:03 [crit] 12734#0: *3 connect() to unix:/run/gunicorn.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.122.1, server: 8beta1.example.com, request: "GET / HTTP/1.1", upstream: "http://unix:/run/gunicorn.sock:/", host: "8beta1.example.com"

إذا اختبرنا Gunicorn مباشرة، فسنحصل على إجابة فارغة.

curl —unix-socket /run/gunicorn.sock 8beta1.example.com

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

sudo setenforce 0

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

من خلال تحديث الصفحة في المتصفح أو إعادة تشغيل أمر الضفيرة، يمكنك رؤية صفحة اختبار Django.

لذلك، بعد التأكد من أن كل شيء يعمل وأنه لم يعد هناك أي مشاكل في الأذونات، قمنا بتمكين SELinux مرة أخرى.

sudo setenforce 1

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

تحديد المجالات المسموح بها

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

لإنشاء نطاق مسموح به محدد لـ Gunicorn، تحتاج إلى نوع من السياسة، وتحتاج أيضًا إلى وضع علامة على الملفات المناسبة. وبالإضافة إلى ذلك، هناك حاجة إلى أدوات لتجميع سياسات جديدة.

sudo yum install selinux-policy-devel

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

gunicorn.te:

policy_module(gunicorn, 1.0)

type gunicorn_t;
type gunicorn_exec_t;
init_daemon_domain(gunicorn_t, gunicorn_exec_t)
permissive gunicorn_t;

يمكنك تجميع ملف السياسة هذا وإضافته إلى نظامك.

make -f /usr/share/selinux/devel/Makefile
sudo semodule -i gunicorn.pp

sudo semanage permissive -a gunicorn_t
sudo semodule -l | grep permissive

دعونا نتحقق لمعرفة ما إذا كان SELinux يحظر شيئًا آخر غير ما يصل إليه برنامجنا الخفي غير المعروف.

sudo ausearch -m AVC

type=AVC msg=audit(1545315977.237:1273): avc:  denied { write } for pid=19400 comm="nginx" name="gunicorn.sock" dev="tmpfs" ino=52977 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_run_t:s0 tclass=sock_file permissive=0

يمنع SELinux Nginx من كتابة البيانات إلى مقبس UNIX الذي يستخدمه Gunicorn. عادة، في مثل هذه الحالات، تبدأ السياسات في التغير، ولكن هناك تحديات أخرى تنتظرنا. يمكنك أيضًا تغيير إعدادات المجال من مجال تقييد إلى مجال إذن. الآن دعنا ننقل httpd_t إلى مجال الأذونات. سيمنح هذا Nginx الوصول اللازم ويمكننا مواصلة المزيد من أعمال التصحيح.

sudo semanage permissive -a httpd_t

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

sudo ausearch -m AVC -c gunicorn

ستشاهد الكثير من الرسائل التي تحتوي على 'comm=gunicorn'' والتي تقوم بأشياء مختلفة على الملفات الموجودة في /srv/djangoapp، لذلك من الواضح أن هذا أحد الأوامر التي تستحق وضع علامة عليها.

ولكن بالإضافة إلى ذلك، تظهر رسالة مثل هذه:

type=AVC msg=audit(1545320700.070:1542): avc:  denied { execute } for pid=20704 comm="(gunicorn)" name="python3.6" dev="vda3" ino=8515706 scontext=system_u:system_r:init_t:s0 tcontext=unconfined_u:object_r:var_t:s0 tclass=file permissive=0

إذا نظرت إلى حالة خدمة gunicorn أو قمت بتشغيل الأمر ps، فلن ترى أي عمليات قيد التشغيل. يبدو أن gunicorn يحاول الوصول إلى مترجم Python في بيئة البيئة الافتراضية الخاصة بنا، ربما لتشغيل البرامج النصية العاملة. فلنضع الآن علامة على هذين الملفين القابلين للتنفيذ ونتحقق مما إذا كان بإمكاننا فتح صفحة اختبار Django الخاصة بنا.

chcon -t gunicorn_exec_t /srv/djangoapp/django/bin/gunicorn /srv/djangoapp/django/bin/python3.6

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

ps -efZ | grep gunicorn

لا تنس إنشاء سياسة SELinux عادية لاحقًا!

إذا نظرت إلى رسائل AVC الآن، فستجد أن الرسالة الأخيرة تحتوي على السماح = 1 لكل ما يتعلق بالتطبيق، والسماح = 0 لبقية النظام. إذا فهمت نوع الوصول الذي يحتاجه التطبيق الحقيقي، فيمكنك العثور بسرعة على أفضل طريقة لحل مثل هذه المشكلات. لكن حتى ذلك الحين، من الأفضل الحفاظ على أمان النظام والحصول على تدقيق واضح وقابل للاستخدام لمشروع جانغو.

sudo ausearch -m AVC

حدث!

ظهر مشروع Django عامل بواجهة أمامية تعتمد على Nginx وGunicorn WSGI. قمنا بتكوين Python 3 وPostgreSQL 10 من مستودعات RHEL 8 Beta. يمكنك الآن المضي قدمًا وإنشاء (أو ببساطة نشر) تطبيقات Django أو استكشاف الأدوات الأخرى المتاحة في RHEL 8 Beta لأتمتة عملية التكوين، أو تحسين الأداء، أو حتى نقل هذا التكوين في حاوية.

المصدر: www.habr.com

إضافة تعليق