البحث في الموقع
المحتوى عن 'خادوم'.
-
تمهيد تتوسع قواعد البيانات بسرعة مع مرور الزمن، وتكاد في بعض الأحيان أن تملأ المساحة التخزينية المتاحة في نظام الملفات كلها. وقد تتعرض أيضًا إلى مشاكل في الإدخال والإخراج نتيجةً لمحاولة عدِّة خدمات الكتابة على (أو القراءة من) نفس القسم معًا. هذا الدرس سيفيدك لو كنتَ تريد إضافة المزيد من المساحة التخزينية، أو استخدام خصائص جهاز التخزين لزيادة الأداء (ربما عبر استخدام RAID)، أو تتطلّع إلى استعمال ميزات أخرى للتخزين. سيعلِّمُك هذا الدرس طريقة تغيير مجلد تخزين بيانات MySQL. التعليمات المذكورة هنا تناسب الخواديم التي تُشغِّل نسخةً وحيدةً من MySQL، أمّا لو كانت عندك أكثر من نسخة، فسيساعدك درس «How To Move a MySQL Data Directory to a New Location on Ubuntu 16.04» في ذلك، لأنَّه يحتوي معلومات عن كيفية تغيير مكان التخزين عبر تعديل الضبط. المتطلبات المسبقة خادوم أوبنتو 16.04 (أو 14.04) مضبوطٌ كما في درس «الإعداد الابتدائي لخادوم أوبنتو 14.04»، بما في ذلك إعداد حساب مستخدم عادي لكنه يملك امتيازات الجذر (root) عبر الأداة sudo. خادوم MySQL. لو لم يكن عندك خادوم MySQL مضبوطٌ مسبقًا، فسيساعدك الدرس «تثبيت وإعداد نظامي إدارة قواعد البياناتMySQL وPostgreSQL على أوبنتو». نسخة احتياطية من قواعد بياناتك. ما لم تكن تتعامل مع نسخةٍ حديثة التثبيت من MySQL، فاحرص على أخذ نسخة احتياطية من بياناتك. سيساعدك درس «كيف تقوم بالنسخ الاحتياطي لقواعد بيانات MySQL على Ubuntu» على فعل ذلك. سننقل البيانات من جهاز تخزينٍ موصولٍ (mounted) في نقطة الوصل /mnt/volume-nyc1-01.. سيعلّمك هذا الدرس طريقة نقل مجلد تخزين بيانات MySQL إلى مكانٍ جديد بغض النظر عن وسيط التخزين الذي تستخدمه (قرص صلب، أو مصفوفة RAID، أو تخزين شبكي). الخطوة الأولى: نقل مجلد بيانات MySQL لكي نضمن سلامة البيانات، علينا أولًا إيقاف خادوم MySQL: sudo systemctl stop mysql الأمر systemctl لا يُظهِر نتيجة تنفيذ أوامر إدارة الخدمات، لذا إذا أردتَ التحقق أنَّ الخادوم قد أُغلِق بنجاح، فنفِّذ الأمر الآتي: sudo systemctl status mysql انظر إلى آخر سطر من ناتج الأمر السابق الذي يجب أن يخبرك أنَّ الخادوم قد توقف عن العمل: . . . Jul 18 11:24:20 ubuntu-512mb-nyc1-01 systemd[1]: Stopped MySQL Community Server. نستطيع الآن –بعد إغلاق الخادوم– نقل مجلد قواعد البيانات إلى مكانٍ آخر: sudo mv /var/lib/mysql /mnt/volume-nyc1-01/mysql ثم سنُنشِئ وصلةً رمزيةً (symbolic link): sudo ln -s /mnt/volume-nyc1-01/mysql /var/lib/mysql يبدو أنَّنا نستطيع تشغيل خادوم MySQL بعد إنشاء الوصلة الرمزية، لكن هنالك أمرٌ إضافيٌ يجب ضبطه. الخطوة الثانية: ضبط قواعد الوصول في AppArmor بعد أن نقلتَ مجلد MySQL إلى مكانٍ آخر في نظام الملفات، فعليك أن تُعدِّل في ضبط AppArmor، وذلك بتعديل ملف alias التابع لبرمجية AppArmor: sudo nano /etc/apparmor.d/tunables/alias أضف الآن التعليمة الآتية في نهاية الملف: . . . alias /var/lib/mysql/ -> /mnt/volume-nyc1-01/mysql/, لكي تأخذ التعديلات مفعولها، فيجب إعادة تشغيل AppArmor: sudo systemctl restart apparmor ملاحظة: إذا تخطيت خطوة ضبط AppArmor وحاولت تشغيل mysql مباشرةً، فستظهر لك رسالة الخطأ الآتية: Job for mysql.service failed because the control process exited with error code. See "systemctl status mysql.service" and "journalctl -xe" for details. يمكن تلخيص الناتج الظاهر من الأمرَين systemctl و journalctl بما يلي: Jul 18 11:03:24 ubuntu-512mb-nyc1-01 systemd[1]: mysql.service: Main process exited, code=exited, status=1/FAILURE ولمّا كانت رسائل الخطأ لا تُظهِر ربطًا مباشرًا بين AppArmor ومجلد التخزين، فقد يصعب عليك أن تعرف لماذا يحدث هذا الخطأ. لكن إن نظرنا إلى ملف syslog فسنرى ما هي المشكلة: sudo tail /var/log/syslog الناتج: Nov 24 00:03:40 digitalocean kernel: [ 437.735748] audit: type=1400 audit(1479945820.037:20): apparmor="DENIED" operation="mknod" profile="/usr/sbin/mysqld" name="/mnt/volume-nyc1-01/mysql/mysql.lower-test" pid=4228 comm="mysqld" requested_mask="c" denied_mask="c" fsuid=112 ouid=112 يمكننا الآن أن نُشغِّل خدمة MySQL: sudo systemctl start mysql sudo systemctl status mysql بعد أن تُعيد تشغيل MySQL، فتحقق أنَّ بياناتك سليمة وأنَّ خادوم MySQL يعمل عملًا سليمًا دون مشاكل. الخلاصة نقلنا في هذا الدرس مجلد تخزين بيانات MySQL واستعملها وصلةً رمزيةً لكي نخبر MySQL ما هو مكان التخزين الجديد؛ ثم حدّثنا ضبط برمجية AppArmor (الموجودة في توزيعة أوبنتو وغيرها) لكي يتوافق مع ما عدّلناه. وصحيحٌ أننا استعملنا جهاز تخزين مستقل، إلا أنَّك تستطيع اتباع تعليمات هذا الدرس لإعادة تعريف مكان تخزين البيانات بغض النظر عن تقنية التخزين المستعملة. ترجمة -وبتصرّف- للمقال How to Change a MySQL Data Directory to a New Location Using a Symlink لصاحبته Melissa Anderson
-
تمهيد كلما كانت صفحات الموقع أسرع تحميلًا كلما ازداد احتمال بقاء الزائر متصفحًا للموقع، وعندما تمتلئ صفحات مواقع الويب بالصور والمحتوى التفاعلي الظاهر عبر سكربتات تُحمَّل في الخلفية، فلن تكون عملية فتح «صفحة» ويب أمرًا هينًا، إذ تتضمن طلب ملفاتٍ عدِّة من خادوم الويب ملفًا ملفًا، وعملية تقليل تلك الطلبيات هي إحدى سُبُل تسريع موقعك. يمكن فعل ذلك بطرائق عدِّة، لكن إحدى أهم الخطوات التي يجب إجراؤها هي ضبط التخزين المؤقت في المتصفح (browser caching)؛ وهذا يعني إخبار المتصفح بإمكانية استخدام نسخ محلية من الملفات التي حُمِّلَت في إحدى المرات بدلًا من طلبها من الخادوم مرارًا وتكرارًا؛ ولفعل ذلك يجب إضافة ترويسات لرد HTTP (HTTP response headers) تخبر المتصفح بما عليك فعله. هذا هو دور وحدة header (header module) في خادوم Nginx، التي يمكن استعمالها لإضافة الترويسات إلى رد HTTP، لكن دورها الأساسي يمكن في ضبط الترويسات المسؤولة عن التخزين المؤقت. سننظر في هذا الدرس إلى كيفية استخدام وحدة header لتطبيق الفكرة السابقة. المتطلبات المسبقة ستحتاج قبل إكمال قراءة هذا الدرس إلى ما يلي: خادوم أوبنتو 16.04 (أو 14.04) مضبوطٌ كما في درس «الإعداد الابتدائي لخادوم أوبنتو 14.04»، بما في ذلك إعداد حساب مستخدم عادي لكنه يملك امتيازات الجذر (root) عبر الأداة sudo. خدمة Nginx مثبّتة على خادومك باتباعك لهذا الدرس «تنصيب، إعداد واستخدام nginx كخادوم ويب». سنحتاج بالإضافة إلى وحدة header إلى وحدة map التابعة لخادوم Nginx؛ لمزيدٍ من المعلومات حول وحدة map، فاقرأ درس كيفية استخدام وحدة map على خادوم أوبنتو 16.04. الخطوة الأولى: إنشاء ملفات للتجربة سنُنشِئ في هذه الخطوة عدِّة ملفات في مجلد Nginx، إذ سنستخدم هذه الملفات لاحقًا للتحقق من سلوك خادوم Nginx الافتراضي لاختبار أنَّ التخزين المؤقت في المتصفح يعمل عملًا صحيحًا. لتقرير ما هو نوع الملفات المُخدَّمة عبر الشبكة، فلن يُحلِّل Nginx محتويات الملف لأن ذلك يستهلك وقتًا كثيرًا؛ وإنما سينظر إلى لاحقة (أو امتداد) الملف لتحديد ما هو نوع MIME التابع له، والذي سيُصرِّح عن الهدف أو الغاية من الملف. ونتيجةً لهذا السلوك، فلن يكون لمحتوى الملفات الاختبارية أيّة أهمية؛ إذ سنستطيع خداع خادوم Nginx بتسمية الملفات تسميةً صحيحةً، فقد يظن خادوم Nginx أنَّ ملفًا فارغًا يُمثِّل صورةً وآخر يُمثِّل سكربت JavaScript. لنُنشِئ ملفًا باسم test.html في مجلد Nginx الافتراضي عبر الأداة truncate (يمكنك استخدام أيّة أداة أو أمر آخر وليكن touch مثلًا). لاحقة الملف تُشير إلى أنَّه مستند HTML: sudo truncate -s 1k /var/www/html/test.html لنُنشِئ المزيد من الملفات الاختبارية بنفس الطريقة السابقة: صورة jpg وملف css وسكربت js: sudo truncate -s 1k /var/www/html/test.jpg sudo truncate -s 1k /var/www/html/test.css sudo truncate -s 1k /var/www/html/test.js الخطوة التالية هي التحقق من سلوك خادوم Nginx فيما يتعلق بإرسال ترويسات للتحكم بالتخزين المؤقت للمتصفح وذلك بتخديم الملفات التي أنشأناها أخيرًا مع الضبط الافتراضي له. الخطوة الثانية: التحقق من السلوك الافتراضي لخادوم Nginx يكون لجميع الملفات نفس سلوك التخزين المؤقت افتراضيًا. ولرؤية ذلك سنستخدم ملف HTML الذي أنشأناه في الخطوة الأولى، إلا أنَّك تستطيع إجراء هذا الاختبار على أيّ ملف من الملفات التي أنشأناها سابقًا. لنتحقق من أنَّ الملف test.html يُخدَّم ومعه المعلومات التي لها علاقة بالمدة الواجب تخزين المتصفح للملف فيها مؤقتًا. سيؤدي الأمر الآتي إلى طلب الملف من خادوم Nginx المحلي ويُظهِر ترويسات رد HTTP: curl -I http://localhost/test.html يجب أن ترى عدِّة ترويسات HTTP: HTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:12:26 GMT Content-Type: text/html Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400" Accept-Ranges: bytes يمكنك أن تلاحظ السطر قبل الأخير الذي يبدأ بالكلمة ETag الذي يتضمن مُعرِّفًا فريدًا للنسخة الحالية من الملف المطلوب. فإذا حاولت تنفيذ أمر curl السابق أكثر من مرة فستجد نفس قيمة ETag. أما عند استخدام متصفح ويب، فستُخزَّن قيمة ETag ثم تُرسَل إلى الخادوم عبر ترويسة If-None-Match عندما يريد المتصفح أن يطلب نفس الملف مرةً أخرى (عند تحديث الصفحة على سبيل المثال). يمكنك محاكاة ذلك في سطر الأوامر بالأمر الآتي. احرص على تغيير قيمة الترويسة If-None-Match في الأمر لتُطابِق قيمة ETag في ناتج الأمر السابق: curl -I -H 'If-None-Match: "57d40685-400"' http://localhost/test.html ستجد أنَّ الرد أصبح مختلفًا الآن: HTTP/1.1 304 Not Modified Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:20:31 GMT Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400" ردّ خادوم Nginx بحالة «304 Not Modified»، ولن يُرسِل الملف عبر الشبكة مجددًا، وإنما يخبر المتصفح أنَّه يستطيع إعادة استخدام الملف المُخزَّن محليًا والذي نزَّله سابقًا. ما سبق مفيدٌ لأنه يُقلِّل من التراسل الشبكي، لكنه ليس مثاليًا لتحقيق أداءٍ عالٍ نتيجةٍ للتخزين المؤقت. المشكلة مع ترويسة ETag أنَّ المتصفح سيرسل الطلبية إلى الخادوم دائمًا ويسأله إن كان بإمكانه استخدام الملف المُخزَّن مؤقتًا، وحتى لو ردّ الخادوم على المتصفح بالإيجاب بحالة 304 بدلًا من إعادة إرسال الملف مجددًا، إلا أنَّ إرسال الطلبية واستلام الرد سيستهلك وقتًا. سنستخدم في الخطوة التالية وحدة headers لإضافة معلومات للتحكم بالتخزين المؤقت. وهذا سيجعل المتصفح يُخزِّن الملفات محليًا دون أخذ إذن الخادوم أولًا. الخطوة الثالثة: ضبط ترويستَي Cache-Control و Expires إضافةً إلى ترويسة التحقق من تغيّر الملف (ETag)، هنالك ترويستان للتحكم بالتخزين المؤقت هما Cache-Control و Expires. ترويسة Cache-Control هي نسخةٌ أحدث، وفيها خياراتٌ أكثر مقارنةً بترويسة Expires وهي أفيد إن شئت التحكم بسلوك التخزين المؤقت تحكمًا دقيقًا. إذا ضُبِطَت تلك الترويسات، فهي تخبر المتصفح أنَّ الملف المطلوب يمكن أن يُخزَّن محليًا لفترةٍ زمنيةٍ معيّنة (تستطيع أن تجعل صلاحيته «للأبد»!) دون طلبه مرةً أخرى. أما إن لم تُضبَط تلك الترويسات، فسيطلب المتصفحُ الملفَ دومًا من الخادوم، متوقعًا أن يحصل على الحالة «200 OK» أو «304 Not Modified». يمكننا استخدام وحدة header لإرسال ترويسات HTTP آنفة الذكر. وحدة header هي وحدةٌ من أساس خادوم Nginx لذا لن تحتاج إلى تثبيت أيّ شيءٍ لاستخدامها. لإضافة وحدة header، افتح ملف ضبط Nginx في محرر nano أو أيّ محررٍ نصيٍ تشاء: sudo nano /etc/nginx/sites-available/default اعثر على القسم المُعَنوَنَ server، والذي يبدو كما يلي: . . . # Default server configuration # server { listen 80 default_server; listen [::]:80 default_server; . . . أضف القسمَين الآتيين: أولهما قبل قسم server لتعريف المدة الزمنية لتخزين مختلف أنواع الملفات مؤقتًا، وثانيهما داخل كتلة server الذي يضبط ترويسات التخزين المؤقت ضبطًا سليمًا: . . . # Default server configuration # # Expires map map $sent_http_content_type $expires { default off; text/html epoch; text/css max; application/javascript max; ~image/ max; } server { listen 80 default_server; listen [::]:80 default_server; expires $expires; . . . القسم الموجود قبل قسم server هو قسم map الذي يربط بين أنواع الملفات ومدة التخزين المؤقت لهذا النوع من الملفات. استعملنا عدِّة خيارات ضبط فيه: القيمة الافتراضية هي off، والتي لن تُضيف أيّة ترويسات للتحكم بالتخزين المؤقت، وهذا خيارٌ جيدٌ للمحتوى الذي لا يتطلب تخزينًا مؤقتًا. لنوع text/html سنضبط القيمة إلى epoch وهي قيمةٌ خاصةٌ التي تعني عدم التخزين المؤقت نهائيًا، مما يجبر المتصفح على السؤال إن كانت الصفحة مُحدَّثة. لنوع text/css و application/javascript -والتي هي ملفات الأنماط CSS وسكربتات JavaScript- سنضبط القيمة إلى max وهذا يعني أنَّ المتصفح سيُخزِّن هذه الملفات مؤقتًا أطول مدة يستطيعها، مما يُقلِّل عدد الطلبيات لوجود عدد كبير من هذه الملفات التي ترتبط بصفحة HTML. آخر خيار لنوع ~image/ وهو تعبيرٌ نمطيٌ (regular expression) الذي يُطابِق جميع الملفات ذات النوع الذي يحتوي على العبارة image/ فيه (مثل image/jpg و image/png). وكما في ملفات CSS و JS، تتواجد عادةً صورٌ كثير في مواقع الويب، ومن الجيد تخزينها محليًا، لذا ضبطنا القيمة إلى max أيضًا. التعليمة expires داخل قسم server (التي هي جزءٌ من وحدة headers) تضبط ترويسات التحكم بالتخزين المؤقت؛ حيث تستخدم القيمة المأخوذة من المتغير $expires الذي يربط بين أنواع الملفات ومدة صلاحيتها. وبهذه الطريقة ستختلف الترويسات المُرسَلة اعتمادًا على نوع الملف. احفظ الملف واخرج من المُحرِّر النصي، وأعد تشغيل خادوم Nginx لتفعيل الضبط الجديد: sudo systemctl restart nginx سنتحقق في الخطوة التالية من أنَّ الضبط الجديد يعمل عملًا سليمًا. الخطوة الرابعة: اختبار التخزين المؤقت في المتصفح لنُنفِّذ نفس الأمر الذي طلبنا فيه ملف HTML في القسم السابق: curl -I http://localhost/test.html سيكون الرد مختلفًا هذه المرة، إذ يجب أن ترى ترويستين جديدتين: HTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:48:53 GMT Content-Type: text/html Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400" Expires: Thu, 01 Jan 1970 00:00:01 GMT Cache-Control: no-cache Accept-Ranges: bytes ترويسة Expires مرتبطة بتاريخٍ في الماضي وترويسة Cache-Control مضبوطةٌ إلى no-cache، مما يجعل المتصفح يسأل الخادوم إن توفرت نسخةٌ جديدةٌ من الملف (باستخدام ترويسة ETag كما سبق). ستجد ردًا مختلفًا عندما تُجرِّب على صورة: curl -I http://localhost/test.jpg ناتج الأمر السابق: HTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:50:41 GMT Content-Type: image/jpeg Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:36 GMT Connection: keep-alive ETag: "57d40688-400" Expires: Thu, 31 Dec 2037 23:55:55 GMT Cache-Control: max-age=315360000 Accept-Ranges: bytes ارتبطت الترويسة Expires في هذه المرة بتاريخٍ في المستقبل البعيد، واحتوت ترويسة Cache-Control على max-age التي تخبر المتصفح كم ثانية يجب أن يُبقي على الملف. وهذا يُخبِر المتصفح أن يُخزِّن الصورة المُنزَّلة مؤقتًا لأطول مدة ممكنة، وبالتالي إذا ظهرت الصورة مرةً أخرى فستُستعمَل النسخة المحلية ولن تُرسَل الطلبية إلى الخادوم بتاتًا. يجب أن تكون النتيجة مشابهةً لملفَي test.js و test.css لأنهما ملفا JavaScript و CSS ويُضبَط لهما ترويسات التخزين المؤقت أيضًا، مَثَلُهُما كَمَثل الصور. إن ظهر عندك مثلما عرضنا في أمثلتنا، فاعلم أنَّ ترويسات التحكم بالتخزين المؤقت قد ضُبِطَت ضبطًا صحيحًا وسيستفيد موقعك من ناحية الأداء، وسيقل الحِمل على خادومك نتيجةً لتخزين المتصفح للملفات محليًا وعدم طلبها من الخادوم. عليك الآن أن تُخصِّص إعدادات التخزين المؤقت اعتمادًا على محتوى موقعك، لكن قد تجد أنَّ الضبط الذي وفرناه في هذا الدرس مناسبًا لتبدأ منه. الخلاصة يمكن أن تُستعمَل وحدة headers لإضافة أي نوع من الترويسات إلى رد HTTP، لكن من أفضل تطبيقات هذه الوحدة هو ضبط ترويسات التحكم بالتخزين المؤقت. إذ سيساعد ذلك في تحسين أداء مواقع الويب المُستضافة على خادومك، خصوصًا في الشبكات ذات زمن التأخير المرتفع (نسبيًا) مثل شبكات الهواتف المحمولة. يمكن أن يؤدي ذلك إلى تحسين ظهور موقعك في محركات البحث التي تأخذ عامل سرعة الموقع بالحسبان. حيث يُعتَبَر ضبط ترويسات التخزين المؤقت من أبزر نصائح أدوات اختبار سرعة الصفحات من Google (أقصد Google PageSpeed). لمزيدٍ من المعلومات التفصيلية عن وحدة headers، فارجع إلى توثيق Nginx الرسمي. ترجمة -وبتصرّف- للمقال How to Implement Browser Caching with Nginx’s header Module on Ubuntu 16.04 لصاحبه Mateusz Papiernik.
-
يُعدّ Nginx خادوم ويب مفتوح المصدر يتسّم بالسرعة والاعتمادية، كسب شعبيّته نتيجة حجم الذاكرة القليل الذي يستهلكه، وقابليته الكبيرة للتوسعة، وسهولة إعداده، ودعمه لمعظم البروتوكولات المختلفة. ويعتبر بروتوكول HTTP/2 الجديد نسبيًا أحد البروتوكولات التي يدعمها خادوم Nginx، الأمر الذي تم منذ أقل من عام مضى، وتعتبر الميّزة الرئيسية في HTTP/2 هي سرعة نقله العالية للمحتويات الغنيّة بالمعلومات في مواقع الويب. سيساعد المقال على تثبيت وإعداد خادوم Nginx سريع وآمن مع دعم لبروتوكول HTTP/2. المتطلبات الأولية قبل أن نبدأ، سنحتاج للأمور التالية: نظام تشغيل Ubuntu 14.04، مستخدم عادي بدون صلاحيات مدير نظام، لكنّه يملك صلاحية تنفيذ أمر sudo. يمكن مراجعة الإعداد الابتدائي لخادوم أوبونتو 14.04 لمزيد من المعلومات، نطاق موقع، ويمكن شراء واحد من موقع Namecheap أو الحصول على واحد مجانًا من موقع Freenom، تأكّد من أنّ النطاق يشير إلى عنوان نظام التشغيل الذي ستستخدمه عبر الإنترنت، وستساعدك مقالة إعداد اسم مضيف مع DigitalOcean إن احتجت لمساعدة، شهادة SSL رقميّة، ويمكن إنشاء واحدة بشكل يدوي، أو الحصول على واحدة مجانًا من Let’s Encrypt، أو شراء واحدة من مزوّد آخر. هذا كل شيء، فإن كانت لديك جميع المتطلبات المذكورة، فأنت جاهز للبدء. الفرق ما بين HTTP 1.1 و HTTP/2 إن HTTP/2 هو الإصدار الجديد من بروتوكول نقل النصوص الفائقة HTTP، الذي يستخدم على الشبكة العنكبوتية لإيصال محتويات صفحات الويب من الخوادم إلى المتصفّحات، وهو أول تحديث ضخم طرأ على HTTPمنذ حوالي عقدين، حيث تم إطلاق الإصدار HTTP 1.1 عام 1999 حينما كانت الصفحات عبارة عن ملف HTML مستقل مع أنماط CSS ضمنيّة (أي مكتوبة ضمن مستند HTML وليست مستوردة من ملف خارجي). لقد تغيّرت شبكة الإنترنت بشكل متسارع في السنوات الستة عشر الأخيرة، وأصبحنا الآن نواجه صعوبة مع المحدودية التي يفرضها HTTP 1.1، حيث يَحُدّ البروتوكول من سرعة النقل الممكنة لمعظم مواقع الويب الحديثة لأنه يقوم بتحميل أجزاء الصفحة وفق مبدأ الطابور queue (بمعنى أنه يجب أن ينتهي تحميل كل جزء قبل أن يبدأ تحميل الجزء التالي له)، ويحتاج موقع ويب حديث وسطيًّا حوالي 100 طلب ليتم تحميل الصفحة(كل طلب يمثّل صورة، ملف js، ملف css، إلخ). يقوم بروتوكول HTTP/2 بحل هذه المشكلة لأنّه يقوم بتقديم تغييرات جذرية تتمثل في: تحميل جميع الطلبات على التوازي، وليس على التسلسل كما في مبدأ الطابور، ضغط ترويسة HTTP، نقل الصفحات عبر الشبكة بصيغة ثنائية binary، وليس كنصوص text، وهذا أكثر فعالية، قدرة الخادوم الآن على "دفع" البيانات قبل أن يطلبها المستخدم، مما سيحسّن من السرعة للمستخدمين الذين يعانون من بطء في الإرسال. وعلى الرغم من أن بروتوكول HTTP/2 لا يتطلب التشفير، إلا أن مطوّري أشهر متصفّحين، Google Chrome و Mozilla Firefox، صرّحوا بأنه -لدواع أمنيّة- سيتم دعم بروتوكول HTTP/2 في اتصالات HTTPS الآمنة فقط، ولهذا السبب إن قررت تثبيت خواديم تدعم HTTP/2 فيجب عليك الانتقال إلى استخدام HTTPS. الخطوة الأولى: تثبيت الإصدار الأخير من Nginx تم طرح دعم HTTP/2 في إصدار Nginx 1.9.5، ولسوء الحظ فإنّ المستودع الافتراضي في نظام Ubuntu متأخر عن إدراج آخر إصدار ويقوم حاليًّا بتوفير الإصدار 1.4.6. لكن لحسن الحظ فإن مطوّري Nginx لديهم مستودع apt خاص على نظام Ubuntu، حيث بالإمكان الحصول دومًا على آخر إصدار متوفّر. لإضافة المستودع الخاص إلى قائمة مستودعات apt لدينا، نحتاج أولًا للحصول على مفتاح التوقيع الرقمي الخاص بالمستودع، وهذا إجراء أمني يضمن بأن الحزم البرمجية التي سنحصل عليها من هذا المستودع صادرة عن المطوّرين الحقيقين وموقّعة منهم. سنقوم بتنفيذ الأمر التالي في سطر الأوامر: $ wget -qO - http://nginx.org/keys/nginx_signing.key | sudo apt-key add - بعد ذلك، سنقوم بإضافة المستودع إلى قائمة مصادر الحزم البرمجية، بتنفيذ الأمر التالي: $ sudo echo -e "deb http://nginx.org/packages/mainline/ubuntu/ `lsb_release -cs` nginx\ndeb-src http://nginx.org/packages/mainline/ubuntu/ `lsb_release -cs` nginx" | sudo tee /etc/apt/sources.list.d/nginx.list الآن وبعد أن أصبح نظام الحزم (الذي يدعى apt في نظامي Ubuntu و Debian) يعلم عن توفّر المستودع الجديد، سنقوم بتحديث قائمة الحزم البرمجية المتوفّرة وأخيرًا سنقوم بتثبيت الإصدار الأخير من Nginx: $ sudo apt-get update $ sudo apt-get install nginx ويمكن التحقق من رقم الإصدار بتنفيذ الأمر: $ sudo nginx -v حيث ينبغي أن يكون الخرج مشابهًا لـ nginx version: nginx/1.9.x. سنقوم في الخطوات التالية بتغيير الإعدادات الافتراضية في Nginx ليقوم بتخديم موقع الويب الخاص بنا. الخطوة الثانية: تغيير منفذ التنصّت الافتراضي سنقوم أولًا بتغيير رقم المنفذ من 80 إلى 443، وذلك بفتح ملف الإعدادات: $ sudo nano /etc/nginx/conf.d/default.conf ملاحظة: في حال ظهر خطأ يخبرك بأنه لم يتم التعرّف على الأمر nano، فتأكّد من تثبيت محرر النصوص nano وحاول مجدّدًا. ينبغي أن نخبر Nginx برقم المنفذ الذي يجب عليه تلقّي الاتصالات عبره، وهو المنفذ 80 افتراضيًا، المنفذ القياسي في بروتوكول HTTP. سنبحث في ملف الإعدادات عن السطر التالي: listen 80; سنقوم بتغيير المنفذ إلى 443 لأن HTTP/2 لن يعمل مع معظم المستخدمين إذا بقي يقوم بإرسال البيانات عبر المنفذ 80، كما أشرنا سابقًا إلى أنه مدعوم فقط عبر المنفذ 443 على متصفحات Chrome و Firefox.سنستبدل السطر السابق بـ: listen 443 ssl http2; لاحظ أننا أضفنا كلمة ssl و http2 في نهاية السطر، وتخبر هذه المتغيّرات Nginx بأن يستخدم بروتوكول HTTP/2 مع المتصفحات التي تدعم البروتوكول الجديد. الخطوة الثالثة: تغيير اسم الخادوم يأتي اسم الخادوم server_name بعد السطر السابق الذي يحوي على listen، وسنقوم بإعلام Nginx أيّ نطاق سيرتبط مع ملف الإعدادات. سنستبدل الاسم الافتراضي localhost الذي يعني بأن ملف الإعدادات مسؤول عن جميع الطلبات الواردة، وسنضع مكانه اسم النطاق الحقيقي، كـ example.com على سبيل المثال: server_name example.com; سنقوم الآن بحفظ التغييرات بالضغط على CTRL+O ومن ثم نضغط CTRL+X للخروج من محرر nano. الخطوة الرابعة: إضافة المسار إلى شهادات SSL الأمنية سنقوم بإعداد Nginx الآن ليستخدم الشهادات الأمنية اللازمة لـ HTTPS، وإن لم تكن تعلم ما هي الشهادة الأمنية أو لم تكن تملك واحدة فيرجى مراجعة المقالات المذكورة في قسم المتطلبّات الأولية في بداية المقال. لنقم أولًا بإنشاء مجلّد لحفظ الشهادات الأمنية فيه داخل مجلد إعدادات Nginx: $ sudo mkdir /etc/nginx/ssl سنقوم الآن بنسخ ملف الشهادة وملف المفتاح الخاص private key إلى داخل المجلد الجديد، وبعد ذلك سنقوم بتغيير أسماء الملفات لتظهر بوضوح أي نطاق ترتبط معه (تساعد هذه الخطوة في المستقبل على توفير الوقت والجهد إن امتلكت أكثر من نطاق واحد). لا تنسى استبدال example.com باسم النطاق الخاص بك. $ sudo cp /path/to/your/certificate.crt /etc/nginx/ssl/example.com.crt $ sudo cp /path/to/your/private.key /etc/nginx/ssl/example.com.key سنقوم بإضافة سطر جديد الآن إلى ملف الإعدادات ضمن كتلة server لتعريف مسارات الشهادة الرقمية مع مفتاحها الخاص ssl_certificate /etc/nginx/ssl/example.com.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; الخطوة الخامسة: تحويل جميع طلبات HTTP إلى HTTPS ينبغي الآن أن نخبر Nginx بأننا نرغب بتوفير المحتوى عبر HTTPS فقط إن استلم طلب HTTP عوضًا عن ذلك. سنقوم في نهاية ملف الإعدادات بإنشاء كتلة server جديدة لتحويل جميع طلبات HTTP إلى HTTPS، ومرة أخرى لا تنس استبدال النطاق باسم النطاق الخاص بك، ستكون الكتلة الجديدة على الشكل التالي: server { listen 80; listen [::]:80; server_name example.com; return 301 https://$server_name$request_uri; } لاحظ في الكتلة السابقة كيف أنّ Nginx عند استلامه لطلبات HTTP تخص النطاق example.com عبر المنفذ 80، فإنّه سيقوم بتحويل الطلب إلى HTTPS على نفس المسار المطلوب، مستخدمّا التحويل من النمط301 (moved permanently). سيصبح شكل ملف الإعدادات (إن تجاهلنا الأسطر الخاصة بالتعليقات) على النحو التالي: server { listen 443 ssl http2 default_server; listen [::]:443 ssl http2 default_server; root /var/www/html; index index.html index.htm index.nginx-debian.html; server_name example.com; location / { try_files $uri $uri/ =404; } ssl_certificate /etc/nginx/ssl/example.com.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; ssl_dhparam /etc/nginx/ssl/dhparam.pem; } server { listen 80; listen [::]:80; server_name example.com; return 301 https://$server_name$request_uri; } سنقوم الآن بحفظ ملف الإعدادات بالضغط على CTRL+O ومن ثم الخروج من محرر nano بالضغط على CTRL+X. الخطوة السادسة: تحميل ملف الإعدادات الجديد في ذاكرة Nginx لتطبيق التغييرات التي قمنا بها، سنحتاج لإعادة تشغيل خادوم Nginx: $ sudo service nginx restart وللتحقق من أن كل شيء سار على ما يرام، سنقوم بفتح المتصفح واستعراض النطاق الذي أعددناه مع Nginx، فإن كانت الإعدادات صحيحة، يجب أن يتم تحويل الطلب إلى HTTPS بشكل تلقائي، وللتأكد مما إذا تم استخدام البروتوكول الجديد فسنقوم بفتح أدوات المطوّر (في متصفح كروم يمكن فتحها من خلال قائمة View > Developer > Developer Tools)، ومن ثم نعيد تحميل الصفحة من جديد (View > Reload This Page)، ثم من خلال لسان Network، سنضغط بالزر الأيمن للفأرة على سطر رأس الجدول ونختار Protocol. يجب الآن أن يظهر عمود جديد عنوانه Protocol ستتمكن من خلاله من رؤية أن البروتوكول المستخدم هو h2 (الذي يرمز إلى بروتوكول HTTP/2) في حال تم الإعداد بشكل صحيح. إلى هنا سيكون Nginx قد أصبح قادرًا على تخديم المحتوى باستخدام بروتوكول HTTP/2، ولكن تبقى هناك بعض الأمور التي ينبغي إعدادها قبل استخدام الخادوم في بيئة التشغيل. الخطوة السابعة: تحسين Nginx لتقديم أداء أفضل سنقوم الآن بفتح ملف الإعدادات مجدّدًا لضبط إعدادات Nginx لتقديم أفضل أداء وحماية. تفعيل Connection Credentials Caching بالمقارنة مع HTTP، فإن HTTPS يتطلب وقتًا أطول نسبيًا لإنشاء اتصال ما بين الخادوم والمستخدم، ولتقليل الفرق في سرعة تحميل الصفحة، سنقوم بتفعيل ميّزة Connection Credentials Caching وهذا يعني بأنه عوضًا عن إنشاء جلسة عمل جديدة عند طلب كل صفحة، سيقوم الخادوم باستخدام نسخة خبء cache لمعلومات المصادقة. لتفعيل الميّزة، سنقوم بإضافة السطرين التاليين إلى نهاية كتلة http في ملف الإعدادات: ssl_session_cache shared:SSL:5m; ssl_session_timeout 1h; يقوم المتغيّر ssl_session_cache بتحديد حجم الخبء الذي سيحتوي على معلومات الجلسة، حيث يمكن لـ 1MB أن يقوم بتخزين معلومات 4000 جلسة عمل، وبالتالي ستكون القيمة الافتراضية 5MB أكثر من كافية لمعظم الحالات، ولكن إن كنت تتوقع أن يكون هناك حركة مرور كثيفة على الموقع، فيمكنك زيادة القيمة وفقًا لذلك. يحدّد المتغير ssl_session_timeout من الوقت الذي تكون ضمنه جلسة العمل فعّالة داخل الخبء، ولا ينبغي أن تكون هذه القيمة كبيرة (أكثر من ساعة)، وإلى جانب ذلك فإن تخفيضها كثيرًا سيخفض من الفائدة المرجوّة أيضًا. تحسين باقات التشفير Cipher Suites باقات التشفير هي مجموعة من خوارزميات التشفير التي تصف كيف ينبغي أن يتم تشفير البيانات المنقولة. يملك بروتوكول HTTP/2 قائمة كبيرة من خوارزميات التشفير القديمة وغير الآمنة التي يجب تجنّب استخدامها. سنقوم باستخدام مجموعة تشفير معتمدة من قبل جهات عملاقة مثل CloudFlare. لا تسمح هذه المجموعات باستخدام خوارزمية MD5 في التشفير (التي تم إثبات أنها غير آمنة في عام 1996، وبالرغم من ذلك، فما تزال منتشرة حتى يومنا هذا). سنقوم بإضافة السطرين التاليين بعد ssl_session_timeout: ssl_prefer_server_ciphers on; ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; تفعيل ميزة (Strict Transport Security (HSTS على الرغم من أنّا قمنا بتحويل جميع طلبات HTTP إلى HTTPS في إعدادات Nginx، فينبغي علينا أيضًا أن نقوم بتفعيل ميّزة Strict Transport Security لتجنّب الحاجة لإخبار Nginx بعملية التحويل بأنفسنا أساسًا. عندما يصادف المتصفح ترويسة HSTS، فلن يحاول الاتصال بالخادوم عبر HTTP التقليدي مجدّدًا قبل مضي فترة من الوقت، ومهما حصل، سيعمل على تبادل البيانات عبر اتصالات HTTPS مشفّرة. تساعد هذه الترويسة على حمايتنا من هجمات تخفيض البروتوكول. كل ما نحتاجه هو إضافة السطر التالي في ملف الإعدادات: add_header Strict-Transport-Security "max-age=15768000" always; يحدّد المتغير max-age القيمة التي يجب على المتصفح خلالها ألا يحاول التواصل مع الخادوم إلا عبر بروتوكول HTTPS، وهذه القيمة ممثّلة بالثواني وبالتالي فإن القيمة 15768000 تساوي 6 أشهر. وبشكل افتراضي فإن هذه الترويسة لن يتم إضافتها إلى الطلبات الموجّهة للنطاقات الفرعية، فإن كنت تملك نطاقات فرعية وترغب أن يتم تطبيق HSTS عليها جميعًا فيجب إضافة المتغير includeSubDomains حينها إلى نهاية السطر على الشكل التالي: add_header Strict-Transport-Security "max-age=15768000; includeSubDomains: always;"; لا تنس حفظ الملف الآن بالضغط على CTRL+O ومن ثم إغلاقه بالضغط على CTRL+X إن كنت تستخدم محرر nano. الخطوة الثامنة: رفع حماية تبادل المفاتيح الأمنية إن الخطوة الأولى في إنشاء اتصال آمن هو تبادل المفاتيح الخاصة بين الخادوم والعميل، وتكمن المشكلة في هذه العملية في أنّه حتى تلك اللحظة فالاتصال غير مشفّر بينهما وبالتالي فالبيانات المتبادلة يمكن التنصّت عليها من طرف ثالث. لهذا السبب، سنحتاج لاستخدام خوارزمية Diffie-Hellman-Merkle في تبادل المفاتيح. يستخدم Nginx بشكل افتراضي مفتاح DHE) Ephemeral Diffie-Hellman) بطول 1024 بت، والذي يمكن فك تشفيره بسهولة نسبيًا، وللحصول على أمان أعلى فينبغي علينا إنشاء مفتاح DHE خاص أكثر أمنًا. للقيام بذلك سنقوم بتنفيذ الأمر التالي في سطر الأوامر: $ sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048 تنبيه: يجب أن يتم إنشاء مُعاملات DH في المجلد الذي قمنا بتخزين الشهادات الأمنية فيه، وفي المقال قمنا بتخزين الشهادات في المسار /etc/nginx/ssl/. إن السبب وراء هذا هو أن Nginx يقوم دومًا بالبحث عن مفتاح DHE الخاص بالمستخدم في مجلد الشهادات الأمنية ويستخدمه إن وُجد. تحدّد القيمة 2048 في الأمر السابق طول المفتاح الذي نرغب بإنشائه، حيث سيكون مفتاح بطول 2048 بت أكثر أمنًا وينصح به من قبل مؤسسة موزيلّا، ولكن إن كنت تسعى خلف أقصى درجة أمان، فيمكنك استخدام مفتاح بطول 4096 بت. ستأخذ عملية إنشاء المفتاح حوالي 5 دقائق، ويمكنك أثناء ذلك الاسترخاء وارتشاف بعض القهوة. لتطبيق التغييرات التي قمنا بها على ملف الإعدادات، لا تنسى أن تقوم بإيقاف وإعادة تشغيل خادوم Nginx: $ sudo service nginx restart الخلاصة أصبح اتصال SSL الخاص بك أكثر أمنًا وقابلًا للاستخدام لنقل المعلومات الحساسة، ولكن إن أردت اختبار قوته الأمنية فقم بزيارة Qualys SSL Lab وبتشغيل اختبار على خادومك، حيث ينبغي أن تحصل على نتيجة A+إن كان كلّ شيء معدّ بشكل صحيح. هذا كلّ شيء، أصبح خادوم Nginx الخاص بك جاهزًا للاستخدام، وكما ترى فإن بروتوكول HTTP/2 هو حل رائع لتحسين سرعات نقل البيانات ومن السهل استخدامه كما أظهرنا في المقال. ترجمة -وبتصرّف- للمقال How To Set Up Nginx with HTTP/2 Support on Ubuntu 14.04 لصاحبه Sergey Zhukaev.
-
من المعروف أن حزمة LEMP (والتي هي اختصار لـ Linux, Nginx, MySQL, PHP) توفر سرعة وموثوقية عالية لتشغيل مواقع PHP، إلّا أنها تملك مزايا أخرى غير مشهورة كالأمن والعزل. في هذا المثال، سنعرض لك مزايا أمن وعزل المواقع على LEMP مع مستخدمين مُختلفين، وهذا عن طريق إنشاء أحواض pools خاصّة بـ php-fpm لكل جزء من خادوم nginx (سواء كان موقعًا أو مستضيفًا افتراضيًا virtual host). المتطلبات الأساسية تمت تجربة هذا الدرس على أوبنتو 14.04 وعلى الرغم من ذلك فإن طريقة التثبيت والإعداد ستكون مشابهة لها على بقية الأنظمة والإصدارات، لكن قد تختلف الأوامر وأماكن ملفات الإعداد بين الأنظمة. كما أننا نفترض أنك قد ثبتت nginx و php-fpm، وبخلاف ذلك أنصحك بإتباع الخطوة الأولى والثالثة من هذا المقال: كيف تثبت حزم MySQL ،nginx ،Linux :LEMP وPHP على أوبنتو 14.04. يجب تنفيذ جميع الأوامر في هذا الدرس التعليمي بمستخدم غير الجذر (non-root)، وإن إحتجنا لصلاحياته فسنستخدم sudo، وإذا لم تُعدّ بعد هذا المستخدم فأنصحك بإتباع هذا الدرس: الإعداد الابتدائي لخادوم أوبنتو 14.04. ستحتاج أيضا إلى اسم نطاق مؤهل بالكامل (fully qualified domain name (fqdn الذي يربط على خادوم أو خادوم للتجربة بالإضافة إلى localhost الافتراضي، وإذا لم يكن لديك واحد، يمكنك استخدام site1.example.org، وذلك عن طريق تعديل ملف etc/hosts/ باستخدام محررك المفضل كهذا sudo vim /etc/hosts وأضف هذا السطر (استبدل site1.example.org بـ fqdn الخاص بك إذا كنت تستخدمه): ... 127.0.0.1 site1.example.org ... أسباب لزيادة تأمين LEMP عند إعداد LEMP مشترك سيكون هنالك حوض pool php-fpm واحد فقط والذي سيشغل جميع سكربتات PHP لجميع المواقع باستخدام نفس المستخدم، وهذا يطرح مشكلتين كبيرتين: إذا تعرض تطبيق ويب على أحد أجزاء خادوم nginx -على سبيل المثال عنوان فرعي أو موقع ما- إلى خطر أو هجوم فستتعرض جميع مواقع الموجودة على هذا الخادوم أيضا، فالمهاجم سيتمكن من قراءة ملفات الإعداد (configuration files) بما في ذلك تفاصيل قواعد بيانات لمواقع أخرى أو حتى تغيير ملفاتها. إذا أردت إعطاء مستخدم صلاحيات للدخول إلى الخادوم الخاص بك، فسوف تعطيه صلاحيات الوصول إلى جميع المواقع، فعلى سبيل المثال، إذا كان مطورك يحتاج إلى العمل على بيئة الإدراج (staging environment)، فحتى مع صلاحيات صارمة جدا على الملفات، فستبقى له إمكانية وصوله إلى جميع المواقع بما في ذلك موقعك الرئيسي على نفس الخادوم. المشاكل أعلاه تم حلها في php-fpm بإنشاء أحواض مختلفة والتي تشتغل تحت مستخدم مختلف لكل موقع. الخطوة الأولى - إعداد php-fpm إذا غطيت المتطلبات الأساسية فسيكون لديك في الوقت الحالي موقع ويب يعمل على خادوم. سننشئ الآن موقعًا ثانيًا (site1.example.org) مع حوض php-fpm ومستخدم لينكس مخصصين له. لنبدأ بإنشاء المستخدم الضروري، ولأفضل عملية فصل يجب أن يحصل المستخدم الجديد على مجموعته الخاصة لذلك سننشئ أولا مجموعة المستخدم site1: sudo groupadd site1 وبعد ذلك سننشئ مستخدم site1 ينتمي إلى هذه المجموعة: sudo useradd -g site1 site1 حتى الآن لا يملك هذا المستخدم الجديد كلمة مرور ولا يمكنك تسجيل دخوله في خادوم، إذا أردت توفير صلاحيات وصول إلى هذه الملفات من الموقع لشخص معين، فيجب عليك في هذه الحالة إنشاء كلمة مرور لهذا المستخدم عن طريق الأمر: sudo passwd site1 ومع تركيبة اسم المستخدم/كلمة المرور سيتمكن المستخدم من تسجيل دخوله عن بعد باستخدام ssh أو sftp. بعد ذلك، أنشئ حوض php-fpm جديد لـ site1، فالحوض مهم للغاية وهو عبارة عن عملية (process) لينكس عادية والتي تعمل تحت مستخدم/مجموعة محددة وتستمع لـ Linux socket وقد تستمع أيضا لتركيبة IP:Port لكن سيتطلب هذا إلى المزيد من موارد الخادوم وهذه الطريقة ليست جيدة. بشكل افتراضي في نظام أبنتو 14.04، كل حوض php-fpm يجب أن يتم إعداده في ملف داخل مجلد etc/php5/fpm/pool.d/. كل ملف مع امتداد conf. في هذا المجلد سيتم تحميله تلقائيا في الإعداد العام لـ php-fpm. لذلك سننشئ ملف etc/php5/fpm/pool.d/site1.conf/ لموقعنا، يمكنك فعل ذلك مع محررك المفضل كالتالي: sudo vim /etc/php5/fpm/pool.d/site1.conf يجب أن يحتوي هذا الملف على: [site1] user = site1 group = site1 listen = /var/run/php5-fpm-site1.sock listen.owner = www-data listen.group = www-data php_admin_value[disable_functions] = exec,passthru,shell_exec,system php_admin_flag[allow_url_fopen] = off pm = dynamic pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3 chdir = / في الإعدادات أعلاه، لاحظ هذه الخيارات: إن [site1] هو اسم الحوض، فلكل حوض اسم خاص به. يشير كل من user و group إلى المستخدم المجموعة التي سيعمل عليها الحوض الجديد. ستشير listen إلى مكان خاص لكل حوض. إن كل من listen.owner و listen.group يعرّفان ملكية المستمع (listener) -على سبيل المثال socket الخاصة بحوض php-fpm الجديد- ويجب على Nginx أن يكون قادرا على قراءة هذا socket، وهذا هو سبب أن socket يُنشأ مع اسم المستخدم والمجموعة تحت nginx الذي يشغل www-data. يسمح لك php_admin_value بوضع قيم إعداد php مخصصة والتي سنستخدمها لتعطيل دوال التي تُشغّل أوامر لينكس مثل exec, passthru, shell_exec, system. إن php_admin_flag مشابه لـ php_admin_value لكنه مجرد مبدل لقيم المنطقية مثل on و off. سنعطل دالة PHP التي تدعى allow_url_fopen والتي تسمح لسكربت PHP بفتح ملفات عن بعد والتي يمكن أن تُستخدم بواسطة المهاجم. ملاحظة: إن قيم php_admin_value و php_admin_flag يمكن تطبيقها بشكل عام، وعلى الرغم من ذلك قد يحتاجهما الموقع وهذا هو سبب عدم إعدادهما بشكل افتراضي. إن من مميزات أحواض php-fpm أنها تسمح لك بتخصيص إعدادات أمن لكل موقع، وعلاوة على ذلك، فيمكنك استخدام هذه الخيارات لأي إعدادات php أخرى، خارج المجال الأمني، لتخصيص بيئة الموقع. لن نتحدث في درسنا حول الأمن عن خيارات pm، لكن يجب أن تعرف أنها تسمح لك بإعداد أداء الحوض. وبالنسبة إلى خيار chdir فيجب أن يكون / والذي يعبر عن جذر نظام الملفات، وهذا السطر لا يجب تغييره ما لم تكن تستخدم chroot. لم يتم تضمين خيار chroot في الإعدادات أعلاه لأنه سيسمح لك بتشغيل الحوض في بيئة مسجونة، مثل قفل المجلد، وهذا الأمر سيكون رائعًا لأغراض أمنية لأنه ستتمكن من قفل الحوض دخل مجلد الجذر للموقع، ولكن على الرغم من ذلك فإن هذا الخيار الأمني قد يتسبب بالعديد من المشاكل لأي تطبيق PHP يعتمد على تطبيقات النظام (system binaries) وتطبيقات مثل Imagemagick والتي لن تكون متوفرة. بمجرد انتهائك من الإعدادات أعلاه أعد تشغيل php-fpm لتفعيل الخيارات الجديدة وذلك عن طريق الأمر: sudo service php5-fpm restart تأكد من أن الحوض يعمل بشكل صحيح وذلك بواسطة البحث عن عملياته كالتالي: ps aux |grep site1 إذا اتبعت التعليمات بدقة فستحصل على مخرجات مشابهة لهذه: site1 14042 0.0 0.8 133620 4208 ? S 14:45 0:00 php-fpm: pool site1 site1 14043 0.0 1.1 133760 5892 ? S 14:45 0:00 php-fpm: pool site1 بالإضافة إلى ذلك، سنعطل التخزين المؤقت الذي يوفره opcache، فهذا الأخير قد يُحسّن الأداء لكنه قد يتسبب في مشاكل أمنية. لتعطيله، عدل ملف etc/php5/fpm/conf.d/05-opcache.ini/ باستخدام صلاحيات أعلى (super user) وأضف السطر التالي: opcache.enable=0 بعد ذلك أعد تشغيل php-fpm حتى تعمل الخيارات الجديدة: sudo service php5-fpm restart الخطوة الثانية - إعداد nginx بعد أن انتهينا من إعداد حوض php-fpm لموقعنا، سنقوم الآن بإعداد جزء الخادوم في nginx. ولذلك أنشئ ملفًا جديدًا وذلك باستخدام محررك المفضل كالتالي: sudo vim /etc/nginx/sites-available/site1 يجب أن يحتوي هذا الملف على: server { listen 80; root /usr/share/nginx/sites/site1; index index.php index.html index.htm; server_name site1.example.org; location / { try_files $uri $uri/ =404; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/var/run/php5-fpm-site1.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } الشيفرة أعلاه تظهر إعدادات مشتركة لجزء الخادوم في nginx، لاحظ هذه الأجزاء: جذر الويب (Web root) هو usr/share/nginx/sites/site1/. اسم الخادوم يستخدم site1.example.org التي ذكرناها في جزء المتطلبات الأساسية من هذا الدرس. يحدد fastcgi_pass المتعامل (handler) لملفات php، يجب استخدام unix socket مختلفة لكل موقع مثل var/run/php5-fpm-site1.sock/. بعد ذلك أنشئ مجلد جذر الويب: sudo mkdir /usr/share/nginx/sites sudo mkdir /usr/share/nginx/sites/site1 لتفعيل الموقع أعلاه تحتاج إلى إنشاء رابط رمزي (symlink) له في مجلد /etc/nginx/sites-enabled/. يمكنك فعل ذلك باستعمال الأمر التالي: sudo ln -s /etc/nginx/sites-available/site1 /etc/nginx/sites-enabled/site1 في النهاية، أعد تشغيل nginx لتعمل التغييرات الجديدة كالتالي: sudo service nginx restart الخطوة الثالثة - التجارب للتجارب، سنستخدم دالة phpinfo والتي توفر لنا معلومات تفصيلية حول بيئة php. أنشئ ملفًا جديدًا باسم info.php والذي يحتوي على سطر واحد فقط: <?php phpinfo(); ?> ستحتاج هذا الملف في الموقع الافتراضي لـ nginx وفي جذر الويب /usr/share/nginx/html/، ولهذا الغرض يمكنك استخدام محرر النّصوص كالتالي: sudo vim /usr/share/nginx/html/info.php ثم انسخ الملف إلى جذر الويب للموقع الآخر (site1.example.org) كالتالي: sudo cp /usr/share/nginx/html/info.php /usr/share/nginx/sites/site1/ أنت الآن مستعد لتشغيل أبسط اختبار للتأكد من مستخدم الخادوم، يمكنك إجراء الاختبار عن طريق متصفح أو من خلال طرفية خادوم و lynx (متصفح يعمل عبر الطرفية)، وإذا لم تثبت lynx سابقا في خادوم فيمكنك تثبيته بكل سهولة عن طريق الأمر: sudo apt-get install lynx تأكد أولا من وجود ملف info.php في الموقع الافتراضي، ينبغي أن تتمكن من الوصول إليه عبر localhost كالتالي: lynx --dump http://localhost/info.php |grep 'SERVER\["USER"\]' في الأمر السابق نرشح المخرجات باستخدام grep لمتغير ["SERVER["USER فقط والذي يشير إلى مستخدم الخادوم، بالنسبة إلى الموقع الافتراضي يفترض أن تكون المخرجات تعرض مستخدم www-data الافتراضي كالتالي: _SERVER["USER"] www-data ونفس الشيء سنفعله للتأكد من مستخدم خادوم site1.example.org: lynx --dump http://site1.example.org/info.php |grep 'SERVER\["USER"\]' يجب أن ترى هذه المرة site1 في المخرجات: _SERVER["USER"] site1 إذا قمت بعمل أية تخصيصات في إعدادات php في أحواض php-fpm الأساسية، فيمكنك أيضا التأكد من قيمهما بنفس الطريقة السابقة وذلك عن طريق ترشيح المخرجات التي تهمك. حتى الآن، عرفنا أن موقعينا يعملان تحت مستخدمين مختلفين، لكن لنرى الآن كيف يمكننا تأمين الاتصال، لتفسير المشكلة الأمنية التي نريد حلها في هذا المقال، سننشئ ملفًا يحتوي على معلومات حساسة، في العادة يحتوي هذا الملف معلومات الاتصال بقاعدة البيانات ويحتوي على تفاصيل اسم المستخدم وكلمة المرور لقاعدة بيانات المستخدم، فإذا وُجد أن أحدهم يستطيع الوصول إلى هذه المعلومات فسيتمكن من فعل أي شيء لموقعنا. أنشئ باستخدام محررك المفضل ملفًا جديدًا في موقعك الرئيسي يدعىusr/share/nginx/html/config.php/ ويحتوي على التالي: <?php $pass = 'secret'; ?> في الملف أعلاه عرّفنا متغيرًا يدعى pass والذي يحتوي على قيمة secret، وطبعا نريد تقييد الوصول إلى هذا الملف لذلك سنغير الصلاحيات إلى 400، والتي تعطي صلاحيات القراءة فقط لمالك الملف. لتغيير الصلاحيات إلى 400 نفذ الأمر التالي: sudo chmod 400 /usr/share/nginx/html/config.php بالإضافة إلى ذلك موقعنا الرئيسي يعمل تحت مستخدم www-data والذي يجب أن يكون قادرًا على قراءة هذا الملف، وبالتالي، غيّر ملكية الملف إلى ذاك المستخدم كالتالي: sudo chown www-data:www-data /usr/share/nginx/html/config.php في مثالنا سنستخدم ملفًا آخر يدعى usr/share/nginx/html/readfile.php/ لقراءة المعلومات السرية ومن ثم طباعتها، ويجب أن يحتوي هذا الملف على الشيفرة البرمجية التالية: <?php include('/usr/share/nginx/html/config.php'); print($pass); ?> بعد ذلك غيّر ملكية هذا الملف لمستخدم www-data كالتالي: sudo chown www-data:www-data /usr/share/nginx/html/readfile.php للتأكد من جميع الصلاحيات والملكيات في جذر ويب، نفّذ الأمر التالي: ls -l /usr/share/nginx/html/ يجب أن تكون المخرجات شبيهة بهذه: -r-------- 1 www-data www-data 27 Jun 19 05:35 config.php -rw-r--r-- 1 www-data www-data 68 Jun 21 16:31 readfile.php الآن جرب الوصول إلى الملف السّابق على موقعك الافتراضي باستخدام الأمر: lynx --dump http://localhost/readfile.php ستلاحظ أن secret مطبوعة على الشاشة والتي تعني أن الملف مع المعلومات الحساسة يمكن الوصول إليه من داخل نفس الموقع، وهذا السلوك متوقع. الآن جرب نسخ ملف usr/share/nginx/html/readfile.php/ إلى موقعك الثاني site1.example.org كالتالي: sudo cp /usr/share/nginx/html/readfile.php /usr/share/nginx/sites/site1/ للحفاظ على علاقات الموقع/المستخدم، تأكد من أن الملفات داخل كل موقع مملوكة من طرف المستخدم المعني وذلك عن طريق تغيير ملكية الملف المنسوخ إلى site1 باستخدام الأمر التالي: sudo chown site1:site1 /usr/share/nginx/sites/site1/readfile.php للتحقق من وضعك الصلاحيات والملكيات الصحيحة للملفات، اعرض قائمة محتويات جذر ويب site1 باستخدام الأمر: ls -l /usr/share/nginx/sites/site1/ يجب أن ترى كالتالي: -rw-r--r-- 1 site1 site1 80 Jun 21 16:44 readfile.php بعد ذلك حاول الوصول إلى نفس الملف من site1.example.com باستخدام الأمر: lynx --dump http://site1.example.org/readfile.php سترى أنه تم إرجاع مساحة فارغة، وبالإضافة إلى ذلك، إذا بحثت عن الأخطاء في سجل الأخطاء لـ nginx باستخدام الأمر grep التالي: sudo grep error /var/log/nginx/error.log فسترى شيئا مشابها لهذا: 2015/06/30 15:15:13 [error] 894#0: *242 FastCGI sent in stderr: "PHP message: PHP Warning: include(/usr/share/nginx/html/config.php): failed to open stream: Permission denied in /usr/share/nginx/sites/site1/readfile.php on line 2 ملاحظة: سترى أيضا خطأ مشابهًا في مخرجات lynx إذا فعّلت خيار display_errors في إعدادات php-fpm في ملف etc/php5/fpm/php.ini/ (بوضع On في ذلك الخيار). يظهر التحذير أن السكربت من موقع site1.example.org لا يمكنه قراءة الملف الحساس (config.php) من الموقع الرئيسي وبالتالي، المواقع التي تعمل تحت عدة مستخدمين لا يمكنها تعريض أمن بقية المواقع. إذا ذهبت إلى نهاية جزء الإعدادات من هذا المقال، سترى أننا عطلنا التخزين المؤقت التي يوفرها opcache بشكل افتراضي، وإذا رغبت بمعرفة السبب حاول تفعيله مجددا وذلك عن طريق وضع opcache.enable=1 في ملفetc/php5/fpm/conf.d/05-opcache.ini/ عن طريق مستخدم sudo ومن ثم إعادة تشغيل php5-fpm باستخدام الأمر sudo service php5-fpm restart. ستجد أنه إذا قمت بإعادة خطوات الاختبار بنفس الترتيب فإنك ستتمكن من قراءة الملف الحساس على الرغم من صلاحياته وملكياته، وهذه المشكلة في opcache قد تم الإبلاغ عنها منذ فترة طويلة لكن لم يتم إصلاحها حتى وقت كتابة هذا المقال. الخاتمة من الناحية الأمنية، يجب استخدام أحواض php-fpm مع مستخدم مختلف لكل موقع على نفس خادوم ويب nginx، فهذا الأمر حتى لو كان يضْعف الأداء قليلا، فإن ميزة عملية الفصل يمكنها منع خروق أمنية خطيرة. الفكرة التي شرحناها في هذا المقال ليست فريدة، وهي موجودة في تقنيات فصل PHP أخرى مثل SuPHP. وعلى الرغم من ذلك، أداء بقية البدائل أسوء من php-fpm. ترجمة -وبتصرف- للمقال: How To Host Multiple Websites Securely With Nginx And Php-fpm On Ubuntu 14.04 لصاحبه Anatoliy Dimitrov.
-
ستوفِّر قدرًا كبيرًا من الوقت عندما تطوِّر مواقع ووردبريس على خادوم محلي؛ فالتطوير على خادوم محلي له عدِّة مزايا أهمها أنه أكثر أمانًا وأسرع من لو أنَّك سترفع ملفاتك إلى الخادوم البعيد بشكل مستمر. المشكلة الوحيدة هي أنَّ نقل موقع ووردبريس إلى خادوم إنتاجي سيُسبِّب لك الصداع، فلا يحب أحدٌ أن يعبث مع جداول قاعدة البيانات. لحسن الحظ، عملية النقل أسهل مما تتصور ولن تأخذ من وقتك إلا القليل إن اتبعت الخطوات التي سنووردها في هذا الدرس. سأريك في هذا الدرس كيف تستعمل إضافة "Duplicator" لنسخ موقع ووردبريس يعمل على خادوم محلي إلى استضافة خارجية بسرعة وسهولة. هنالك طرقٌ مختلفة لنقل مواقع ووردبريس، لكن أسهلها هو استخدام إضافة Duplicator؛ وهذه الإضافة مشهورة جدًا في مستودع إضافات ووردبريس، بتقييم 4.9 من 5 وبأكثر من 700 000 تنزيل. سأشرح -وسأريك- في الخطوات الآتية كيفية نقل ووردبريس من الخادوم المحلي عبر Duplicator، لكن قد تختلف بعض الخطوات اعتمادًا على الخادوم الإنتاجي الذي ستنقل إليه الموقع. ستحتاج إلى عميل FTP مع بيانات الدخول لخادومك الإنتاجي لكي تتبع الخطوات التي سنشرحها في هذا الدرس. من الجدير بالذكر أنَّك لا تحتاج إلى تثبيت ووردبريس على الخادوم الإنتاجي قبل اتباع هذه الخطوات، إذ سينسخ Duplicator كل ملفات ووردبريس اللازمة تلقائيًا. الخطوة الأولى: تحزيم موقعك باستخدام Duplicator نزِّل وثبِّت Duplicator على موقعك المحلي. هذه الإضافة مجانية ومتاحة في مستودع إضافات ووردبريس، لذا تستطيع أن تبحث عنها في صفحة الإضافات. بعد تثبيت وتفعيل الإضافة، اضغط على "Duplicator" في شريط أدوات لوحة التحكم، وستظهر شاشة "Duplicator Packages". ولأننا ثبتنا Duplicator منذ قليل فلن تكون هنالك أيّة حزم. حسنًا، ما هي الحزمة؟ تحتوي الحزمة على أرشيف لموقعك (الصيغة الافتراضية هي ZIP) وملف تثبيت مهمته أتمتة عملية ضبط الموقع المؤرشف على الخادوم الجديد. اضغط على Create New لإنشاء حزمة. ستسألك الصفحة التالية عن اسم الحزمة وتسمح لك بإضافة ملاحظات. الاسم الذي تمنحه لموقعك ليس ذا أهمية كبيرة، لكن عليك أن تعطيه اسمًا قابلًا للتذكر إن كنت تنوي أن تُنشِئ عدِّة حزم. أضف ملاحظاتك إن شئت. هنالك إعدادات اختيارية متعلقة بالأرشيف والمُثبِّت، لكنك تستطيع تجاهلها الآن. تسمح لك إعدادات الأرشيف بترشيح (filter) قاعدة البيانات الخاصة بك، بينما تسمح لك خيارات المُثبِّت بملء خيارات التثبيت التي كانت ستظهر لك عند تثبيت الحزمة المؤرشفة، مما يوفر عليك بعض الوقت في المستقبل. اضغط على Next للمتابعة. سيتفحَّص Duplicator نظامك ليتأكد أنَّ عملية بناء الحزمة ستتم بسلاسة دون مشاكل. يمكن أن تُساعد هذه الخطوة في تحديد أيّة مشاكل محتملة الوقوع؛ واجتياز هذا الخطوة يعني أنَّه ليس عليك فعل أيّ شيء لبناء الحزم، بينما يعني ظهور "تحذيرات" (Warn) أنَّك ستواجه مشاكل أثناء عملية بناء وتثبيت الحزمة. لم يكشف الفحص الذي أجراه Duplicator على الموقع الذي نحاول نقله في مثالنا عن أيّة مشاكل، سوى أنَّ المساحة التخزينية لملفات الصور كبيرة، لكنني سأتجاهل ذلك لأنه من غير المحتمل أن تُسبِّب الصور أيّة مشاكل في الخطوات المقبلة. اضغط الآن على Build لإنشاء الحزمة، وعندها ستبدأ الإضافة بأخذ نسخة احتياطية من موقعك. وكما ذكرتُ سابقًا، ستُنتِج إضافة Duplicator ملفين: أرشيف لموقعك (كملف ZIP مضغوط) وملف تثبيت (بصيغة PHP). حمّل كلا الملفين إلى سطح المكتب عندك. الخطوة الثانية: نسخ المثبت والأرشيف إلى الخادوم الإنتاجي علينا الآن نسخ ملفَي الأرشيف والتثبيت اللذان أنشأتهما إضافة Duplicator إلى الخادوم الإنتاجي لكي نستطيع البدء بعملية التثبيت. سأستخدم هنا برنامج Filezilla لنقلهما عبر FTP. عليك تسجيل الدخول إلى خادومك عبر FTP، ثم التنقل إلى مجلد public_html (أو أيًّا كان اسم المجلد الجذر لموقعك) ونسخ ملفَي الأرشيف والمُثبِّت من سطح مكتبك إلى ذاك المجلد؛ ربما ستأخذ هذه العملية بعضًا من الوقت، خصوصًا إذا كان ملف الأرشيف كبيرًا جدًا. الخطوة الثالثة: تثبيت الموقع على خادومك الإنتاجي الخطوة التالية هي تثبيت الموقع على الخادوم الإنتاجي، وعليك الوصول إلى ملف التثبيت الذي نسخته إلى خادومك بإضافة /installer.php إلى اسم النطاق الخاص بك. مثلًا http://example.com/installer.php. ستظهر صفحة المُثبِّت أمامك طالبةً منك إدخال تفاصيل الدخول لقاعدة بيانات MySQL. إذا كنتَ ستستبدل موقع ووردبريس موجود مسبقًا (أي أنَّك تريد تحديث نسختك الحالية من الموقع) فعليك إدخال تفاصيل قاعدة البيانات الموجودة مسبقًا. أما إذا كنتَ تُنشِئ موقعًا جديدًا، فاضغط على Create New وأدخِل تفاصيل قاعدة البيانات الفارغة. لاحظ أنَّ بعض شركات الاستضافة لا تسمح باستخدام خيار إنشاء قاعدة البيانات، وهذا يعني أنَّ عليك إنشاء قاعدة البيانات يدويًا. هذه هي خطوات إنشاء قاعدة بيانات في لوحة تحكم cPanel: افتح MySQL Database أنشِئ قاعدة بيانات بالاسم الذي يحلو لك أنشِئ مستخدمًا جديدًا أضف المستخدم الجديد إلى قاعدة البيانات التي أنشَأتها امنح مستخدمك جميع امتيازات الوصول إلى قاعدة البيانات ثم اضغط على Make Changes املأ تفاصيل الدخول إلى قاعدة البيانات في صفحة التثبيت (لإضافة Duplicator) ثم اضغط على زر Test Connection لتتأكد أنَّ المُثبِّت يملك وصولًا إلى قاعدة البيانات الخاصة بك تأكد أنَّك ستحصل على رسالة Success لاختبارَي Server Connected (أي تم العثور على الخادوم) و Database Found (أي تم العثور على قاعدة البيانات) قبل أن تنتقل إلى الخطوة الآتية. وبهذا ستضمن عدم مواجهة مشاكل أثناء عملية التثبيت. اضغط على Close، ثم ضع إشارة "صح" على بند I have read all warnings and notices في أسفل الصفحة ثم اضغط على Run Deployment. سيبدأ المُثبِّت الآن بتثبيت موقعك على الخادوم الإنتاجي، لكن قد تستغرق هذه الخطوة بعض الوقت إن كان موقعك كبيرًا. سيسألك Duplicator أثناء التثبيت أن تتأكد من تفاصيل موقعك القديمة… اضغط على Run Update بعد أن تتأكد منها. الخطوات الأخيرة سيطلب Duplicator منك إكمال أربع خطوات قصيرة لكنها مهمة: Install Report (تقرير التثبيت): هذا تقريرٌ عن تفاصيل الأخطاء التي قد واجهت عملية التثبيت (أرجو أن لا تكون هنالك أيّة أخطاء!) وعدد الجداول والسجلات التي أنُشِئت (أو حُدِّثَت) في قاعدة البيانات كي تتأكد أنَّ إضافة Duplicator قد نسخت كل معلومات قاعدة البيانات التي تريدها. Save Permalinks (حفظ إعدادات الروابط الدائمة): اضغط على Save Permalinks لكي تؤخذ إلى موقعك الجديد حيث تستطيع ضبط إعدادات الروابط الدائمة. Test Site (اختبار الموقع): اضغط على Test Site وستُظهر لك واجهة الموقع على الخادوم الإنتاجي لكي تتحقق من أنَّ كل شيءٍ يعمل على ما يرام. File Cleanup (حذف ملفات التثبيت): هذه الخطوة مهمة جدًا وغرضها هو حذف ملف التثبيت وأيّة ملفات أخرى مرتبطة فيه التي أنشِئت أثناء عملية التثبيت وذلك لضروراتٍ أمنية. اضغط على File Cleanup لحذف تلك الملفات تلقائيًا. اختبر موقعك الجديد على الخادوم الإنتاجي هذا كل ما في الأمر، يجب أن تحصل الآن على نسخة مماثلة لموقعك الذي كنت تطوره على الخادوم المحلي. هل استخدمتَ Duplicator لنقل المواقع من قبل؟ إن لم تكن تستخدمه، فما هي أفضل طريقة لنقل المواقع برأيك؟ شاركنا تجربتك في التعليقات. ترجمة -وبتصرّف- للمقال The Quick and Easy Guide to Migrating a Local WordPress Installation to a Live Site لصاحبته Raelene Morey.
-
- duplicator
- إضافة
- (و 9 أكثر)
-
يُعد إنجن إكس NginX خادوم ويب Web Server ذو أداء عالي وهو مسؤول عن معالجة العبء الواقع على بعض أكبر المواقع الإلكترونية Web Sites على الإنترنت Internet. وهو جيد خصوصًا في معالجة الكثير من الاتصالات المتزامنة concurrent ويتفوق في تخديم المحتوى الثابت static content. مع أنّ الكثير من المستخدمين يدركون قدرات Nginx، إلا أنه عادةً ما يحتار المستخدمين الجدد ببعض المصطلحات التي يجدونها في ملف إعدادات configuration خادوم Nginx. سنركّز في هذا الدليل على شرح البنية الأساسية لملف إعدادات Nginx بالإضافة لاستعراض بعض المبادئ guidelines في كيفية تصميم ملف الإعدادات. فهم سياقات الإعدادات في Nginxسيُغطي هذا الدليل البنية الأساسية الموجودة في ملف الإعدادات الرئيسي Nginx. قد يختلف مكان هذا الملف وفقًا للطريقة التي اتبعتها عند تثبيت البرمجية Software على الخادوم. إلا أنَّك سوف تجده في العديد من التوزيعات distributions على المسار التالي: /etc/nginx/nginx.confإنّ لم يكن موجودًا فقد تجده على المسار: /usr/local/nginx/conf/nginx.confأو المسار: /usr/local/etc/nginx/nginx.confإنّ أحد أول الأمور التي ينبغي عليك ملاحظتها عندما تنظر إلى ملف الإعدادات الرئيسي هي أنَّه منظَّم في بنيةٍ من ثلاثة أقسام تعرّف بداية ونهاية كلٍ منها بالقوسين } و {. تسمى الأقسام المعرّفة باستخدام القوسين سياقات contexts وفقًا للغة Nginx، وذلك لأنها تحوي على إعداداتٍ مُفَصَّلة وموزعة بين تلك الأقسام وفقًا للمجال الذي يُعنى به كل قسم. يقدم هذا التقسيم بشكل أساسي بنيةً منظّمة وفق بعض الشروط المنطقية التي تقرّر فيما إن كان ينبغي تطبيق الإعدادات التي يحويها القسم أو لا. وباعتبار أنه يمكن للسياقات أن تتداخل فيما بينها فإنNginx يقدم مستوًا من وراثة التعليمات directive inheritance. كقاعدةٍ عامة، إن كانت التعليمة صالحةً في عدّة مجالات scopes متدخلة فإنّ التصريح عنها في سياق أعلى boarder context سيُمرّر لأي سياقٍ أدنى (سياق ابن child context) كقيم افتراضية. يمكن للسياقات الأدنى أن يعيد تعريف override تلك القيم لاحقًا. جدير بالذكر أنّ عملية إعادة التعريف إلى أي مصفوفة-نوع array-type سينتج عنها استبدال القيم السابقة وليس إضافة append القيم الجديدة لها. يمكن أن تستخدم التعليمات Directives ضمن السياقات المصمّمة لها فقط، إذ أنّ Nginx سيُصدر خطأ عند قراءته لملف إعدادات يحوي على تعليمات مصرّح عنها في السياق الخاطئ. يحوي توثيق Nginx على معلومات حول كلّ تعليمة والسياقات التي تكون صالحة ضمنها لذلك يُعد مرجع رائع إن لم تكن متأكدًا من معلوماتك. سنناقش فيما يلي أكثر السياقات شيوعًا والتي قد تصادفها عند استخدامك Nginx. السياقات الأساسيةأول مجموعة سياقات سنشرحها هي السياقات الأساسية core contexts التي يستخدمها Nginx لإنشاء شجرة هرمية hierarchical tree وتوزيع الإعدادات - بحسب مهمّتها - في كتل Blocks منفصلة. تشكّل تلك السياقات البنية الأساسية لإعدادات Nginx. السياق الأساسيإن أكثر سياق عام هو السياق الأساسي Main Context، والذي يدعى أيضًا بالسياق العام Global Context. إذ أنه السياق الوحيد غير المحتوى ضمن كتل السياق القياسية والتي تبدو كما يلي: # السياق الأساسي يوجد هنا، السياقات الأخرى تكون خارجا . . . context { . . . }يمكن القول عن أيّة تعليمة موجودة خارج كل تلك الكتل بأنها تقع في السياق الأساسي. ضع في الحسبان أنه إن كانت إعدادات Nginx مضبوطةً على شكل وحدات modular فستحوي بعض الملفات على تعليماتٍ instructions تبدو وكأنها تقع خارج أقواس السياق إلا أنها ستُضمّن داخله عندما يتم دمج الإعدادات معًا. يمثّل السياق الأساسي أوسع بيئة حاضنة لإعدادات Nginx، وهي مستخدمة لضبط التفاصيل التي تؤثر على كامل التطبيق application في مستوى أساسي. وفي الوقت الذي تؤثر فيه التعليمات الموجودة في هذا القسم على السياقات الأدنى فإن العديد من تلك التعليمات لا توّرث، إذ أنه من غير الممكن إعادة تعريفها في مستوياتٍ أدنى. إن بعض التفاصيل الشائعة والتي تُهيّئ في السياق الرئيسي هي المستخدم user والمجموعة group التي ستُشغّل العمليات العاملة worker processes باسمهما، بالإضافة لعدد العمليات العاملة والملف الذي سيحتفظ بمعرّف العملية الرئيسية PID. يمكن أيضًا أن يُضبط ملف الأخطاء الافتراضي لكامل التطبيق ضمن هذا المستوى (كما يمكن أن يُعاد تعريف الملف ضمن سياقات مخصّصة أكثر). سياق الأحداثإنَّ سياق الأحداث Events Context يقع ضمن السياق الأساسي. وهو يستخدم لضبط الخيارات العامة التي تؤثّر على كيفية معالجة Nginx للاتصالات في مستوى عام. يمكن أن يُعرّف سياق أحداث واحد فقط ضمن إعدادات Nginx. سوف يبدو هذا السياق ضمن ملف الإعدادات كما يلي، خارج أقواس أي سياقٍ آخر: # السياق الأساسي events { # سياق الأحداث . . . }يستخدم Nginx وحدة معالجة اتصال مقادة بالأحداث event-based connection processing model لذلك فإن التعليمات المعرّفة داخل هذا السياق تحدّد كيف ينبغي على العمليات العاملة worker processes معالجة الاتصالات connections. تُستخدم التعليمات الموجودة هنا بشكل أساسي إما في اختيار الطريقة المستخدمة في معالجة الاتصال أو لتغيير كيفية تحقيق implement تلك الطرق methods. عادةً ما تُختار طريقة معالجة الاتصال تلقائيًا بالاعتماد على أكثر الخيارات - التي توفّرها منصّة العمل platform - فاعليةً. بالنسبة لأنظمة لينكس Linux، فعادةً ما تكون طريقة epoll هي الخيار الأفضل. هنالك عناصر أخرى يمكن ضبطها، كعدد الاتصالات التي يمكن لكل عملية عاملة أن تعالجها، وفيما إن كان على العملية أن تأخذ اتصالًا تلو الآخر أو أن تأخذ كل الاتصالات المنتظرة بعد أن يتم إعلامها بوجودها. وأيضًا فيما إن كان على العمليات العاملة أن تتناوب في الرد على الأحداث. سياق HTTPعند إعداد Nginx كخادوم ويب أو خادوم وكيل معكوس reverse proxy، فسيحتفظ سياق HTTP بغالبية الإعدادات. سيحتوي السياق على كل التعليمات والسياقات الأخرى الضرورية لتعريف كيفية معالجة البرنامج لاتصالات HTTP أو HTTPS. يُعد سياق HTTP أخًا لسياق الأحداث - أي أنهما يقعان في نفس المستوى - إذ أن كلاهما يُعد ابنًا للسياق الرئيسي، لذلك ينبغي أن يُدرجا جنبًا إلى جنب بدلًا من أن يتداخلا: # السياق الأساسي events { # سياق الأحداث . . . } http { # سياق HTPP . . . }فيما تختص السياقات الدنيا lower contexts في كيفية معالجات الطلبات، فإن التعليمات في هذا المستوى تتحكم بالقيم الافتراضية لكل الخواديم الافتراضية المعرّفة داخله. هنالك عدد كبير من التعليمات القابلة للضبط configurable ضمن هذا السياق والسياقات المحتواة ضمنه، يعتمد ذلك على طريقة التوريث inheritance التي ترغب أن يتم العمل وفقها. فيما يلي مجموعة من التعليمات التي يمكن أن تصادفها خلال ضبط إعدادات Nginx: تعليمات تتحكم بالمواقع الافتراضية لسجلات الوصول والأخطاء مثل (access_log و error_log).تعليمات تضبط عمليات القراءة والكتابة I/O غير المتزامنة asynchronous من وإلى الملفات مثل (aio و sendfile و directio)،تعليمات تضبط حالات الخادوم عندما تحدث الأخطاء مثل (error_page).تعليمات تضبط عملية الضغط compression مثل (gzip و gzip_disable).عمليات تحسّن fine-tune إعدادات اتصال TCP الحي TCP keep alive مثل (keepalive_disable و keepalive_requests و keepalive_timeout).القواعد rules التي يتبعها Nginx عندما يحاول تحسين optimize الحزم packets واستدعاءات النظام system calls مثل (sendfile و tcp_nodelay و tcp_nopush).تعليمات تضبط جذر المستندات document root وفهرس الملفات index files على مستوى التطبيق application-level مثل (root و index).تعليمات تهيّئ العديد من جداول المقابلة hash tables المستخدمة لتخزين أتماط مختلفة من البيانات مثل (*_hash_bucket_size و *_hash_max_size المستخدمتين مع server_names بالإضافة لـ types و variables).سياق الخادوميُعرّف سياق الخادوم Server Context ضمن سياق HTTP، ويُعد ذلك أوّل مثال لنا عن السياقات المتداخلة. كما أنّه أوّل سياق مرّ معنا يمكن تعريفه عدّة مرات. قد يبدو التنسيق العام لسياق الخادوم مشابهًا لما يأتي، تذكّر أنّ هذا السياق سيقع ضمن سياق HTTP: # السياق الأساسي http: { # سياق HTTP server { # سياق خادوم أول } server { # سياق خادوم ثاني } }يعود سبب السماح بوجود أكثر من تعريف لسياق الخادوم إلى أن كل غرض instance يعرّف خادومًا افتراضيًا لمعالجة طلبات المستخدم client. بإمكانك تعريف كتل الخادوم server blocks بقدر حاجتك، إذ أنّه بإمكان كل منها أن يعالج مجموعة فرعية محدّدة من الاتصالات. وبسبب امكانية وترجيح وجود عدة كتل خواديم، فإن هذا أوّل نوع من السياقات يحتّم على Nginx استخدام خوارزمية algorithm انتقاء لاتخاذ القرارات. إنّ كلّ طلب مستخدم سيُعالَج وفقًا للإعدادات المعرّفة في سياق خادوم وحيد، لذلك فإن على Nginx أن يقرّر أي سياق خادوم هو الأنسب وفقًا لتفاصيل الطلب. إنّ التعليمات التي تقرّر فيما إذا كانت كتلة الخادوم ستُستخدم أم لا هي: تعليمة التنصت listen: تُصمَّم كتلة الخادوم للرد على الطلبات الواردة من مصدر معرّف بثنائية عنوان الإنترنت IP / المنفذ Port. فإن ورد طلب request من مستخدم يطابق ذلك المصدر فمن المحتمل أن يتم اختيار تلك الكتلة لمعالجة الاتصال.تعليمة اسم الخادم server_name: هذه التعليمة هي المكوّن الآخر المستخدم في اختيار كتلة الخادوم التي ستقوم بالمعالجة. إن كان هنالك العديد من كتل الخادوم مزوّدةً بتعليمات تنصت مضبوطة بنفس القيم ويمكنها أن تعالج الطلب، فسيحلّل إنجن إكس ترويسة المُضيف Host header الخاصة بالطلب ويطابقها مع التعليمة.يمكن للتعليمات الموجودة ضمن هذا السياق أن تعيد تعريف العديد من التعليمات المعرّفة في سياق HTTP بما فيها تعليمات التسجيل logging، وجذر المستندات، والضغط، وإلى ما هنالك. وبالإضافة إلى التعليمات المأخوذة من سياق HTTP، فبإمكاننا أيضًا إعداد ملفات لمحاولة الرد على الطلبات عبر تعليمة (try_files)، وإصدار أمر لإعادة التوجيه وإعادة الكتابة (return و rewrite)، وضبط المتحولات عبر تعليمة (set). سياق الموقعالسياق التالي الذي ستتعرف عليه هو سياق الموقع location context وهو سياقٌ ستتعامل معه بشكل متكرّر. تتشارك سياقات الموقع العديد من الصفات مع سياقات الخادوم. فمثلًا، يمكن تعريف أكثر من سياق موقع، ويُستخدم كل منها لمعالجة نوع محدّد من طلبات المستخدم، ويتم اختيار كل موقع بناءًا على مطابقة تعريف الموقع مع طلب المستخدم باستخدام خوارزمية انتقاء. في الوقت الذي تُعرّف فيه التعليمات - التي تحدّد فيما إن كان سيتم اختيار كتلة خادوم - ضمن السياق الخادوم ذاته، فإن المكوّن component الذي يقرّر مدى قدرة الموقع على معالجة الطلب يقع ضمن تعريف الموقع نفسه. تبدو الصيغة العامة كما يلي: location match_modifier location_match { . . . }تقع كتل الموقع ضمن سياقات الخواديم، وعلى عكس كتل الخادوم، يمكن لكتل الموقع أن تتداخل فيما بينها. قد يفيد ذلك في إنشاء سياق موقع أعم ليلتقط مجموعة فرعية محدّدة من الطلبات المتناقلة traffic، لتكمل السياقات الإضافية الداخلية معالجتها بإسهاب أكبر وفقًا لمعايير أدق. # السياق الأساسي server { # سياق الخادوم location /match/criteria { # سياق موقع أول } location /other/criteria { # سياق موقع ثاني location nested_match { # موقع أول } location other_nested { # موقع ثاني } } }بينما يتم اختيار سياقات الخواديم وفقًا لثنائية عنوان IP / المنفذ المطلوبة ولاسم المضيف الموجود ضمن ترويسة المضيف، فإن كتل الموقع تقسّم معالجة الطلب ضمن كتلة الخادوم بالنظر إلى معرّف الموارد الموّحد URI الخاص بالطلب. إن معرّف URI الخاص بالطلب هو جزءٌ من الطلب يأتي بعد اسم النطاق domain name وثنائية عنوان IP / المنفذ. لذلك فإن طلب المستخدم http://www.example.com/blog على المنفذ 80، يستخدم العنوان http www.example.com والمنفذ 80 لتحديد كتلة الخادوم التي سيتم اختيارها. بعد اختيار الخادوم، سيُطابق الجزء /blog (المُمثّل لمعرّف URI الخاص بالطلب) مع المواقع المعرّفة لتحديد السياق الإضافي الذي ينبغي استخدامه ليرد على الطلب. إنّ العديد من التعليمات التي يمكن أن تراها في سياق الموقع متوفّرةٌ أيضًا في مستويات الآباء (المستويات الأعلى) parent levels. فيما يلي مجموعة جديدة من التعليمات المستخدمة ضمن هذا المستوى تسمح لك بالوصول إلى المواقع الواقعة خارج جذر المستندات (تعليمة alias)، وتحديد الموقع كموقع يمكن الوصول له من الداخل فقط (تعليمة internal)، وكوكيل لخواديم أو مواقع أخرى (باستخدام برتوكولات http، fastcgi، scgi، uwsgi) سياقات أخرىمع أنّ الأمثلة السابقة تمثّل السياقات الأساسية التي ستصادفها في Nginx، إلا أنه يوجد سياقات أخرى أيضًا. هنالك عدّة أسباب دعتنا لفصل السياقات التالية، فإما أنها تعتمد على الكثير من الوحدات الاختيارية، أو أنها تستخدم ضمن ظروف خاصة فقط، أو أنها تستخدم لوظائف لن يستخدمها معظم الأشخاص. لكننا لن نناقش كل السياقات المتوفّرة، ولن نخوض بشرح السياقات الآتية بالتفصيل: split_clients أُعد هذا السياق لتقسيم المستخدمين اللذين يستقبلهم الخادوم إلى فئات categories عبر عنونتهم بمتحولات تعتمد على النسبة المئوية. يمكن أن يُستخدم ذلك لاحقًا للقيام باختبار A/B عبر توفير محتويات content مختلفة لمُضيفين hosts مختلفين.perl / perl_set يُهيّئ هذين السياقين معالجات بيرل Perl للموقع الذي تتواجد ضمنه. تستخدم هذه السياقات للمعالجة باستخدام بيرل فقط.map يُستخدم هذا السياق لضبط قيمة متحول بالاعتماد على قيمة متحول آخر. إذ أنه يوفّر جدول مقابلة mapping لقيم المتحوّل الأول حتى يحدّد القيمة التي ينبغي ضبط المتحول الثاني بها.geo يُستخدم هذا السياق - مثل السياق السابق - في تحديد جدول مقابلة. إلا أنّ هذا الجدول يستخدم خصيصًا لتصنيف عناوين IP. يضبط هذا السياق قيمة متحول وفقًا لعنوان IP المتّصل.types يُستخدم هذا السياق أيضًا لجداول المقابلة. إذ أنه يُستخدم لمقابلة أنماط وسائط الإنترنت MIME بامتدادات (لواحق) extensions الملفات التي ينبغي ربطهم بها. يوفّر Nginx ذلك عادةً عبر ملف يُضمّن محتواه داخل ملف الإعدادات nginx.conf.charset_map يمثّل هذا السياق مثالًا آخر عن سياقات المقابلة، إذ أنه يُستخدم لإنشاء جدول مقابلة للتحويل conversion table من مجموعة محارف إلى مجموعة أُخرى. تضمّن كلا المجموعتين في ترويسة هذا السياق، ويضمّن جدول المقابلة في بنيته body.إنّ السياق التالي ليس شائعًا بقدر السياقات التي شرحناها حتى الآن، إلا أنّ التعرّف عليه يبقى مفيدًا. سياق مجموعة الخواديم العليايُستخدم سياق مجموعة الخواديم العليا upstream context لتعريف وإعداد الخواديم العليا upstream. يعرّف هذا السياق بشكل أساسي حوضًا pool معرّفًا باسم يحتوي على مجموعة الخواديم التي يستطيع Nginx أن يعمل كوكيل proxy عنها للطلبات. ستستخدم هذا السياق على الأرجح عندما تُعد وكلاءًا من مختلف الأنواع. يجب وضع سياق مجموعة الخواديم العليا ضمن سياق HTTP، وخارج كلِّ سياقات الخواديم. تبدو الصيغة العامة له كما يلي: # السياق الأساسي http { # سياق HTTP upstream upstream_name { # سياق مجموعة الخواديم العليا server proxy_server1; server proxy_server2; . . . } server { # سياق خادوم } }يمكن فيما بعد أن تتم الإشارة إلى سياق مجموعة الخواديم العليا باسمه ضمن الخادوم أو كتل الموقع لتمرير طلبات ذات نوع محدّد لحوض الخواديم الذي تمّ تعريفه. ستستخدم عندها مجموعة الخواديم خوارزميةً لتحديد الخادوم الذي ينبغي أن يسلم الطلب له، تستخدم خوارزمية راوند روبن round-robin بشكل افتراضي. يسمح هذا السياق لـNginx أن يعمل على موازنة الحمل load balancing عندما يعمل كوكيل للطلبات. سياق البريدبالرغم من أن Nginx يُستخدم عادةً كخادوم ويب أو خادوم وكيل معكوس، إلا أنه يعمل كخادوم وكيل للبريد mail proxy server بأداءٍ عالي جدًا. يسمّى السياق المستخدم لهذا النوع من التعليمات بالبريد mail. يعرّف سياق البريد The mail context ضمن السياق العام، أو السياق الأساسي (خارج سياق HTTP). الوظيفة الأساسية لسياق البريد هي توفير مجال لإعداد وكيل للبريد على الخادوم. يمكن لـNginx أن يعيد توجيه redirect طلبات المصادقة authentication لخادوم مصادقة خارجي authentication server. ويمكنه بعد ذلك أن يوفّر الوصول إلى خواديم البريد العاملة ببرتوكولات POP3 و IMAP لجلب بيانات البريد الفعلية. يمكن لسياق البريد أن يُعد أيضًا للاتصال بخادوم الترحيل العامل ببرتوكول SMTP (SMTP Relayhost) إن دعت الحاجة. عمومًا، قد يبدو شكل سياق البريد مشابهًا لما يلي: # السياق الأساسي events { # سياق الأحداث } mail { # سياق البريد }السياق الشرطييمكن أن يُنشئ السياق الشرطي if لتوفير معالجة شرطية للتعليمات المعرّفة بداخله. وكما هو حال عبارات الشرط if في لغات البرمجة التقليدية، فإن تعليمة if في Nginx ستنفذ التعليمات المتحواة ضمنها إن كان الشرط محقّقًا وأعاد القيمة الصحيحة True. يُقدَّم السياق if في Nginx من قِبل وحدة إعادة الكتابة rewrite module وهي الغاية الأساسية من استخدام هذا السياق. باعتبار أنّ Nginx سيختبر حالات الطلب باستخدام العديد من التعليمات الأخرى المعمولة لهذا الغرض، فلا ينبغي استخدام تعليمة if في أغلب حالات التنفيذ الشرطي. تُعد هذه الملاحظة هامةً لدرجة أنّ مجتمع Nginx أنشأ صفحةً وسمّاها if الشريرة. إنّ المشكلة الأساسية هي أنّ ترتيب المعالجة لدى Nginx قد يؤدي في كثير من الأحيان إلى نتائج غير متوقعة تظهر وكأنها تقلب معنى كتلة if رأسًا على عقب. تُعد تعليمتي إعادة التوجيه return وإعادة الكتابة rewrite التعليمتين الوحيدتين اللتين يعتبر استخدامهما آمنًا داخل هذه السياقات، إذ أنها أُنشئت لأجلهما. ضع في الحسبان أيضًا أنّه عند استخدام سياق if فإنّه يقدّم تعليمة try_files داخل نفس السياق بلا فائدة. غالبًا ما يتم استخدام if لتحديد فيما إنّ كان هنالك حاجة لتعليمة return أو تعلمية rewrite. وستقع غالبًا داخل كتل الموقع، لذلك فستبدو الصيغة العامة مشابهةً لما يلي: # السياق الأساسي http { # سياق HTTP server { # سياق خادوم location location_match { # سياق موقع if (test_condition) { # سياق شرطي if } } } }سياق تحديد الاستثناءاتيُستخدم سياق تحديد الاستثناءات Limit_except Context لتحديد استخدام طرق HTTP Methods محدّدة داخل سياق الموقع. فمثلًا، إن كان ينبغي لمجموعة محدّدة فقط من المستخدمين أن يصلوا إلى محتوى طريقة POST Method، أما البقيّة فينبغي أن يتمكّنوا من قراءة المحتوى، فيمكنك استخدام كتلة limit_except لتعريف تلك المتطلّبات. سيبدو المثال السابق مشابهًا لما يلي: . . . # سياق موقع أو خادوم location /restricted-write { # سياق موقع limit_except GET HEAD { # سياق تحديد الاستثناءات allow 192.168.1.1/24; deny all; } }ستطبق التعليمات الموجودة داخل السياق - والتي يقصد منها تقييد الوصول - عند مصادفة أيٍ من طرق HTTP باستثناء تلك المدرجة ضمن ترويسة السياق. إنّ نتيجة المثال السابق هي أنّه بإمكان أي مستخدم استخدام أفعال GET و HEAD، إلا أنّه يسمح فقط للمستخدمين المتصلين من الشبكة الفرعية subnet ذات العنوان 192.168.1.1/24 باستخدام طرق أخرى. قواعد عامة حول السياقات ينبغي اتباعهاالآن وبعد أن أصبح لديك فكرة عن السياقات الشائعة التي يمكن أن تصادفها أثناء استعراضك لإعدادات إنجن إكس، فبإمكاننا شرح بعض القواعد المُثلى التي يمكن اتباعها عند التعامل مع سياقات Nginx. تطبيق التعليمات في أعلى سياق متوفرإنّ العديد من التعليمات تكون صالحةً داخل أكثر من سياق واحد. فمثلًا، هنالك بضعة تعليمات يمكن وضعها داخل كل من سياقات HTTP والخادوم والموقع. يمنحنا ذلك مرونةً عند ضبط تك التعليمات. ولكن كقاعدة عامة، عادةً ما يكون من الأفضل تعريف التعليمات في أعلى سياق تقبل التعريف ضمنه، وإعادة تعريفها ضمن السياقات الأدنى عند الحاجة. يمكن ذلك بسبب وحدة الوراثة inheritance model التي تحقّقها إنجن إكس. هنالك العديد من الأسباب لاستخدام هذه الاستراتيجية. أولًا، إنّ التعريف ضمن أعلى مستوى يجنّبك تكرار نفس التعريف ضمن السياقات الأخوة. فمثلًا، في المثال التالي يعرّف كل موقع نفس جذر المستندات. http { server { location / { root /var/www/html; . . . } location /another { root /var/www/html; . . . } } }يمكنك نقل الجذر خارج كتلة الخادوم، أو حتى لكتلة HTTP كما يلي: http { root /var/www/html; server { location / { . . . } location /another { . . . } } }أغلب الأحيان، سيكون مستوى الخادوم ملائمًا أكثر، إلا أنّ التصريح declaring في مستوىً أعلى له محاسنه. إنّ ذلك لا يسمح بضبط التعليمة في أماكن أقل فحسب، وإنما سيسمح لك بتمرير القيمة الافتراضية لكل الأبناء، ما يمنع ظهور الحالات التي تصادف فيها خطأً لأنك نسيت تعليمةً ضمن مستوىً أدنى. يمكن أن يكون ذلك مشكلةً كبيرةً في الإعدادات الطويلة. إن التصريح في مستوىً عالي يوفّر لك قيمةً افتراضيةً صحيحة. استخدام عدة سياقات أشقاء عوضا عن المنطق الشرطي If في المعالجةعندما تريد معالجة الطلبات كل بشكل مختلف، اعتمادًا على بعض المعلومات التي يمكنك ايجادها ضمن طلب المستخدم، فعادةً ما يلجئ المستخدمون إلى سياق if ليحاولوا معالجتها شرطيًا. هنالك بعض المشاكل المترافقة مع ذلك كنا قد ذكرناها سابقًا بإيجاز. أول مشكلة هي أنّ تعليمة if عادةً ما تعيد نتائج لا تتوافق مع توقعات مدير النظام administrator. مع أنّ المعالجة ستُؤدي دائمًا إلى نفس النتائج عند توفير نفس المُدخلات، إلا أنّ الطريقة التي يتبعها Nginx في تفسير البيئة يمكن أن تختلف كثيرًا عما هو متوقع ما ام تخضع لاختبارات مكثّفة. السبب الثاني هو أنّ هنالك تعليمات معدّة لهذا الغرض بالتحديد، وهي مستخدمةٌ للعديد من تلك الأغراض. يستخدم Nginx خوارزمية انتقاء - ذات توثيق جيد - لاختيار كتل الخادوم وكتل الموقع. لذلك، يفضّل نقل الإعدادات المختلفة إلى الكتل المخصّصة لها إن كان ذلك ممكنًا، ما يسمح لتلك الخوارزمية بمعالجة منطق عملية الاختيار. فمثلًا، عوضًا عن الاعتماد على إعادة الكتابة rewrites للحصول على طلب المستخدم بالشكل format الذي ترغب بالعمل به، فعليك أن تجرّب إعداد كتلتين للطلب، أحدهما تمثّل الطريقة المرغوب بها، والأخرى تلتقط الطلبات العشوائية وتعيد توجيهها - وقد تعيد كتابتها - للكتلة الصحيحة. عادةً ما تكون النتائج أسهل للقراءة كما أنها تمتلك محاسن كونها ذات أداء عالي. لا تخضع الطلبات الصحيحة لأي معالجة إضافية وفي العديد من الحالات يمكن تجاوز الطلبات غير الصحيحة عبر تعليمة إعادة التوجيه عوضًا عن تعليمة إعادة الكتابة والتي ينبغي أنّ يكون عبء تنفيذها أقل. الخلاصةإلى هنا، ينبغي أن تكون قد حصلت على فهم جيّد لأكثر سياقات Nginx شيوعًا وللتعليمات التي تُنشئ الكتل التي تعرّف تلك السياقات. تحقّق دائمًا من توثيق Nginx للحصول على معلومات حول أي السياقات هو الأنسب لوضع تعليمة ما بداخله ولتقييم الموقع الأكثر فاعلية. إنّ الحرص أثناء إنشاء الإعدادات لن يسهل من عملية الصيانة فحسب، بل أنه سيرفع الأداء أيضًا في أغلب الآحيان. ترجمة -بتصرف- للمقال Understanding the Nginx Configuration File Structure and Configuration Contexts لكاتبه Justin Ellingwood.
-
تأتي MySQL مع أداة تشخيص (diagnostic) تدعى mysqlslap، تمّ توفير هذه الأداة منذ الإصدار 5.1.4 من MySQL، وهي عبارة عن أداة قياس للأداء (benchmarking) يمكنها أن تساعد مدراء قواعد البيانات والمطورّين على أن يختبروا أداء خواديم قواعد البيانات الخاصّة بهم بسهولة. يمكن لـmysqlslap أن تحاكي عددًا كبيرًا من أجهزة العملاء التي تتصل بخادوم قاعدة البيانات بنفس الوقت. مُعامِلات اختبار الحمل (load testing parameters) قابلة للضبط بشكلٍ كلّي ويمكن استخدام نتائج الاختبارات المختلفة بهدف تحسين تصميم قواعد البيانات أو استهلاك موارد العتاد. في هذا الدرس، سنشرح كيفيّة تثبيت وإعداد mysqlslap بهدف إجراء اختبار الحِمْل (load test) على قاعدة بيانات MySQL باستخدام بعض عمليات الاستعلام الأساسية ولنرى كيف يمكن لاختبارات الأداء أن تساعدنا على تحسين هذه الاستعلامات لاحقًا. بعد القليل من التوضيحات المبدئية، سنقوم بعمل سيناريو تجريبي مشابه للتجربة الحقيقية حيث سنقوم بإنشاء نسخة من قاعدة بيانات موجودة حاليًا لنقوم بالاختبارات عليها، وسنجمع الاستعلامات من ملفّات السجل ونبدأ بالاختبار بواسطة سكربت. ما هو حجم الخادوم الذي يجب أن أستخدمه؟إذا كنتَ مهتمًا باختبار أداء خادوم قاعدة بياناتٍ معيّن، فيجب عليك أن تقوم بتشغيل اختبارات الأداء على خادوم بنفس المواصفات وبنسخة مطابقة تمامًا لقاعدة البيانات التي قمت بتثبيتها على خادومك الرئيسي. إذا كنتَ تريد المضيّ قدمًا بهذا الدرس بهدف التعلّم وتنفيذ كلّ أمر موجودٍ فيه، فإننا ننصحك بخادوم يمتلك 2 جيجابت من الذاكرة العشوائية (RAM) على الأقل. وبما أنّ الأوامر الموجودة في هذا الدرس تهدف إلى إرهاق الخادوم بالطلبات الكثيرة بهدف قياس أدائه، فإنّك قد تلاحظ أنّ الخواديم الأصغر حجمًا قد ينقطع الاتصال بها بسبب ذلك الحِمل. تمّ إنتاج الخرج الناتج في هذا الدرس بواسطة عدّة طرق بهدف تحسين الاستفادة من الأمثلة الموجودة هنا. الخطوة الأولى: تثبيت خادوم MySQL على نظام اختباريسنبدأ عبر تثبيت نسخة جديدة من خادوم MySQL المطوّر بواسطة المجتمع أو MySQL Community Server على نظام تشغيل اختباري. يجب ألّا تقوم بتشغيل أيّ أوامر أو استعلامات تجدها في هذا الدرس على خادوم قاعدة بيانات ضمن بيئة إنتاجية بتاتًا. تهدف هذه الاختبارات إلى الضغط على الخادوم التجريبي وقد تسبب تعليقا أو تعطلا لخادوم يعمل ضمن بيئة عمل إنتاجية. تمّ تنفيذ هذا الدرس ضمن بيئة العمل التالية: CentOS 7.أوامر تمّ تنفيذها بواسطة مستخدم جذر.2 جيجابت من الذاكرة العشوائية (RAM); لا تنسى أنّ الاختبارات التي سيتم ذكرها هنا تمّ إنتاجها بهدف التعليم ولا تعني بأيّ شكل من الأشكال أنّها نتيجة اختبارات رسمية صادرة عنّا.أولًا، سنقوم بإنشاء مسار ليحوي جميع الملفّات المتعلّقة بهذا الدرس. سيساعدنا هذا على إبقاء المكان نظيفًا. قم بالذهاب إلى هذا المسار: sudo mkdir /mysqlslap_tutorial cd /mysqlslap_tutorialبعدها، سنقوم بتحميل مستودع yum الخاصّ بنسخة المجتمع من خادوم MySQL. المستودع الذي سنحمّله هو مخصص بشكل اساسي لـRed Hat Enterprise Linux 7 إلّا أنّه يعمل بالطبع مع CentOS 7 كذلك: sudo wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpmالآن يمكننا تنفيذ rpm -Uvh لتثبيت هذا المستودع: sudo rpm -Uvh mysql-community-release-el7-5.noarch.rpmتحقق أنّه قد تمّ تثبيت المستودعات عبر تفحّص محتويات المجلّد etc/yum.repos.d/: sudo ls -l /etc/yum.repos.dيجب أن يبدو الخرج كالتالي: -rw-r--r--. 1 root root 1612 Jul 4 21:00 CentOS-Base.repo -rw-r--r--. 1 root root 640 Jul 4 21:00 CentOS-Debuginfo.repo -rw-r--r--. 1 root root 1331 Jul 4 21:00 CentOS-Sources.repo -rw-r--r--. 1 root root 156 Jul 4 21:00 CentOS-Vault.repo -rw-r--r--. 1 root root 1209 Jan 29 2014 mysql-community.repo -rw-r--r--. 1 root root 1060 Jan 29 2014 mysql-community-source.repoوفي حالتنا، فإنّ MySQL 5.6 Community Server هو ما نريده: mysql-connectors-community/x86_64 MySQL Connectors Community 10 mysql-tools-community/x86_64 MySQL Tools Community 6 mysql56-community/x86_64 MySQL 5.6 Community Server 64والآن قم بتثبيت خادوم MySQL: sudo yum install mysql-community-serverبمجرّد اكتمال العمليّة، قم بالتحقق من أنّه تمّ تثبيت المكوّنات فعلًا: sudo yum list installed | grep mysqlيجب أن تبدو القائمة كالتالي: mysql-community-client.x86_64 5.6.20-4.el7 @mysql56-community mysql-community-common.x86_64 5.6.20-4.el7 @mysql56-community mysql-community-libs.x86_64 5.6.20-4.el7 @mysql56-community mysql-community-release.noarch el7-5 installed mysql-community-server.x86_64 5.6.20-4.el7 @mysql56-communityبعدها، يجب أن نتأكّد أن عفريت MySQL أو MySQL Daemon يعمل بالفعل وأنّه سيبدأ تلقائيًا عند تشغيل الخادوم. يمكنك القيام بذلك عبر الأمر التالي: sudo systemctl status mysqld.serviceيجب أن ترى خرجًا كهذا: mysqld.service - MySQL Community Server Loaded: loaded (/usr/lib/systemd/system/mysqld.service; disabled) Active: inactive (dead)ابدأ الخدمة عبر: sudo systemctl start mysqld.serviceولجعلها تبدأ عند بدء تشغيل الخادوم: sudo systemctl enable mysqld.serviceوأخيرًا يجب علينا تأمين خادوم MySQL: sudo mysql_secure_installationسيظهر لك هذا مجموعة من الأسئلة. سنريك بالأسفل جميع الأسئلة مع أجوبتها التي يجب أن تختارها. في البداية لن يكون هناك كلمة مرور للمستخدم root الخاصّ بـMySQL، لذا، قم فقط بالضغط على زرّ Enter. أثناء الأسئلة، يجب أن تقوم بكتابة كلمة مرور جديدة وآمنة للمستخدم الجذر. يجب أن تقوم بكتابة y لإزالة حساب المستخدم المجهول من خادوم قاعدة البيانات، ولتعطيل السماح بالولوج البعيد للمستخدم الجذر وإعادة تحميل جدول الصلاحيات وغيرها: ... Enter current password for root (enter for none): OK, successfully used password, moving on... ... Set root password? [Y/n] y New password: Re-enter new password: Password updated successfully! Reloading privilege tables.. ... Success! ... Remove anonymous users? [Y/n] y ... Success! ... Disallow root login remotely? [Y/n] y ... Success! Remove test database and access to it? [Y/n] y - Dropping test database... ... Success! ... Reload privilege tables now? [Y/n] y ... Success! Cleaning up...يمكننا الآن أن نتّصل بقاعدة البيانات لنتأكّد من أنّ كلّ شيءٍ يعمل بشكلٍ صحيح: sudo mysql -h localhost -u root -pقم بإدخال كلمة مرور MySQL التي كتبتها للتوّ الآن. يجب أن ترى الخرج التالي: Enter password: Welcome to the MySQL monitor.... mysql>في طرفيّة <mysql قم بإدخال الأمر الذي سيظهر لك جميع قواعد البيانات: show databases;يجب أن ترى خرجًا كالتالي: +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | +--------------------+ 3 rows in set (0.00 sec)وأخيرًا، دعنا ننشئ مستخدمًا يدعى "sysadmin"، سيتم استخدام هذا الحساب للولوج إلى MySQL بدلًا من المستخدم الجذر. كن متأكّدًا من استبدال mypassword بكلمة المرور التي تريدها لهذا المستخدم. سنقوم أيضًا بمنح جميع الصلاحيات اللازمة لهذا المستخدم. في طرفيّة MySQL، قم بإدخال التالي: create user sysadmin identified by 'mypassword';الخرج: Query OK, 0 rows affected (0.00 sec)قم بإعطاء جميع الصلاحيات للمستخدم: grant all on *.* to sysadmin;الخرج: Query OK, 0 rows affected (0.01 sec)والآن فلنعد إلى طرفيّة نظام التشغيل العادية: quit;الخرج: Byeالخطوة الثانية: تثبيت قاعدة بيانات تجريبيةالآن، سنحتاج إلى تثبيت قاعدة بيانات تجريبية بهدف الاختبار. اسم قاعدة البيانات هذه هو "employees" وهي وهي متوفّرة بشكلٍ مجاني من على موقع MySQL، كما أنّه يمكن تحميل قاعدة البيانات من Launchpad. اخترنا قاعدة "employees" لأنّها تحتوي على مجموعة كبيرة من البيانات. رغم ذلك، فإنّ بنية قاعدة البيانات بسيطة بدرجة كافية، إنّها تحتوي على 6 جداول فقط، ولكن وفي داخلها، فهي تحتوي على سجلات 3 مليون موظّف (يمتلك جدول الرواتب لوحده حوالي 3 ملايين صفّ)، وسيساعدنا هذا على محاكاة حِمل بيئة عمل إنتاجية بشكل أفضل. أوّلًا، دعنا نتأكّد أننا في المسار mysqlslap_tutorial/ الخاصّ بنا: cd /mysqlslap_tutorialقم بتحميل آخر إصدار من قاعدة البيانات التجريبية: sudo wget https://launchpad.net/test-db/employees-db-1/1.0.6/+download/employees_db-full-1.0.6.tar.bz2قم بتثبيت bzip2 لنتمكّن من استخراج الملفّ: sudo yum install bzip2والآن قم بفكّ الضغط عن أرشيف قاعدة البيانات، قد يأخذ هذا وقتًا: sudo bzip2 -dfv employees_db-full-1.0.6.tar.bz2 sudo tar -xf employees_db-full-1.0.6.tarسيتم فكّ ضغط المحتويات إلى مسار منفصل جديد يدعى "employees_db"، نحتاج أن نقوم بالذهاب إلى هذا المجلّد لنقوم بتشغيل الأمر الذي يقوم بتثبيت قاعدة البيانات هذه. تتضمّن المحتويات ملفّ README، سجل للتغييرات، حِزَم بيانات وملفّات استعلامات SQL مختلفة تشكّل جميعها بنية قاعدة البيانات: cd employees_db ls -lوهذا هو ما يجب أن تراه: -rw-r--r--. 1 501 games 752 Mar 30 2009 Changelog -rw-r--r--. 1 501 games 6460 Oct 9 2008 employees_partitioned2.sql -rw-r--r--. 1 501 games 7624 Feb 6 2009 employees_partitioned3.sql -rw-r--r--. 1 501 games 5660 Feb 6 2009 employees_partitioned.sql -rw-r--r--. 1 501 games 3861 Nov 28 2008 employees.sql -rw-r--r--. 1 501 games 241 Jul 30 2008 load_departments.dump -rw-r--r--. 1 501 games 13828291 Mar 30 2009 load_dept_emp.dump -rw-r--r--. 1 501 games 1043 Jul 30 2008 load_dept_manager.dump -rw-r--r--. 1 501 games 17422825 Jul 30 2008 load_employees.dump -rw-r--r--. 1 501 games 115848997 Jul 30 2008 load_salaries.dump -rw-r--r--. 1 501 games 21265449 Jul 30 2008 load_titles.dump -rw-r--r--. 1 501 games 3889 Mar 30 2009 objects.sql -rw-r--r--. 1 501 games 2211 Jul 30 2008 README -rw-r--r--. 1 501 games 4455 Mar 30 2009 test_employees_md5.sql -rw-r--r--. 1 501 games 4450 Mar 30 2009 test_employees_sha.sqlقم بتشغيل هذا الأمر للاتصال بـMySQL وتشغيل سكربت employees.sql، والذي سيقوم بإنشاء قاعدة البيانات وتحميل البيانات إليها: sudo mysql -h localhost -u sysadmin -p -t < employees.sqlالآن، يجب عليك إدخال كلمة المرور التي اخترتها للمستخدم "sysadmin" من قبل. يجب أن يكون خرج العملية شيئًا كهذا، قد تأخذ وقتًا (حوال الدقيقة) ليكتمل: +-----------------------------+ | INFO | +-----------------------------+ | CREATING DATABASE STRUCTURE | +-----------------------------+ +------------------------+ | INFO | +------------------------+ | storage engine: InnoDB | +------------------------+ +---------------------+ | INFO | +---------------------+ | LOADING departments | +---------------------+ +-------------------+ | INFO | +-------------------+ | LOADING employees | +-------------------+ +------------------+ | INFO | +------------------+ | LOADING dept_emp | +------------------+ +----------------------+ | INFO | +----------------------+ | LOADING dept_manager | +----------------------+ +----------------+ | INFO | +----------------+ | LOADING titles | +----------------+ +------------------+ | INFO | +------------------+ | LOADING salaries | +------------------+الآن، يمكنك الولوج إلى MySQL وتشغيل بعض الاستعلامات البدائية للتحقق من أنّ البيانات قد تمّ استيرادها بنجاح: sudo mysql -h localhost -u sysadmin -pقم بإدخال كلمة المرور الخاصّة بالمستخدم sysadmin. تحقق من قائمة قواعد البيانات لرؤية قاعدة البيانات employees الجديدة: show databases;الخرج: +--------------------+ | Database | +--------------------+ | information_schema | | employees | | mysql | | performance_schema | +--------------------+ 4 rows in set (0.01 sec)استخدم قاعدة البيانات "employees" عبر الأمر: use employees;وللتحقق من الجداول الموجودة: show tables;الخرج: +---------------------+ | Tables_in_employees | +---------------------+ | departments | | dept_emp | | dept_manager | | employees | | salaries | | titles | +---------------------+ 6 rows in set (0.01 sec)إذا كنت تريد ذلك، فيمكنك التحقق من تفاصيل كلّ جدول من هذه الجداول. سنتحقق الآن من تفاصيل جدول titles فقط: describe titles;الخرج: +-----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+-------------+------+-----+---------+-------+ | emp_no | int(11) | NO | PRI | NULL | | | title | varchar(50) | NO | PRI | NULL | | | from_date | date | NO | PRI | NULL | | | to_date | date | YES | | NULL | | +-----------+-------------+------+-----+---------+-------+ 4 rows in set (0.01 sec)وللتحقق من رقم المُدخَلات: mysql> select count(*) from titles;+----------+ | count(*) | +----------+ | 443308 | +----------+ 1 row in set (0.14 sec)يمكنك التحقق من أيّ بيانات أخرى تريدها. بعد أن تنتهي، ارجع إلى طرفيّة نظام التشغيل الرئيسية عبر كتابة: quit;انتهى درسنا حول تثبيت mysqlslap وإعداده، اقرأ الدرس الآخر لمتابعة طريقة استخدامه للقيام بالاختبارات المطلوبة. ترجمة -وبتصرف- للمقال: How To Measure MySQL Query Performance with mysqlslap لصاحبه: Sadequl Hussain.
-
لقد أصبح Nginx أحد أكثر التطبيقات البرمجية المتوفرة لخواديم الويب Web Servers مرونةً وقوة. ولكن إذا ما نظرنا إليه من الناحية التصميمية فإنه يعد أولًا وقبل كل شيء خادومًا وسيطا Proxy Server. إن التركيز على أداء Nginx كوسيط يعني أنه ذو أداء عالي عندما يعمل على معالجة الطلبات بالتعاون مع خواديم أخرى. يمكن لـ Nginx أن يلعب دور الوسيط باستخدام بروتوكولات Protocols http ,FastCGI ,uwsgi ,SCGI ,memcached. سنناقش في هذه المقالة استخدام FastCGI للقيام بالوساطة proxying، فهو يعد واحدًا من أكثر البرتوكولات التي تُستخدم لذلك الغرض. لماذا نستخدم FastCGI للقيام بالوساطة؟يستخدم FastCGI للقيام بالوساطة ضمن Nginx كي يترجم طلبات المستخدمين Clients لخادوم التطبيقات Application Server الذي لا يقوم بمعالجة طلبات المستخدمين مباشرةً أو الذي لا ينبغي له ذلك. إنّ FastCGI هو برتوكول مبني على برتوكول Common Gateway Interface الأقدم والذي يشار إليه اختصارًا CGI، وقد أُريد من بروتوكول FastCGI أن يحسّن أداء البروتوكول السابق عبر عدم معالجة كل طلب في عملية Process خاصة به. يُستخدم البرتوكول ليمثّل واجهة interface ذات فعالية أكبر مع الخواديم التي تعالج طلبات المحتوى الديناميكي. إن معالجة PHP هي أحد حالات الاستخدام use-case الرئيسية لبرتوكول FastCGI ضمن Nginx. وعلى عكس أباتشي Apache، الذي يستطيع معالجة PHP مباشرةً باستخدام وحدة mod_php، فإنه ينبغي على Nginx الاعتماد على معالج PHP منفصل لمعالجة طلبات PHP. غالبًا ما تُنجز تلك المعالجة باستخدام php_fpm وهو معالج PHP قد اختبر بشكل كبير للعمل مع Nginx. يمكن أن يُستخدم Nginx مع FastCGI للعمل مع تطبيقات تستخدم لغاتٍ أخرى طالما أنه يتوفر مكونات مهيّأة للاستجابة لطلبات FastCGI. أسياسيات قيام FastCGI بالوساطةعادةً ما تتضمن طلبات الوساطة proxying requests عنوان الخادوم الوسيط- Nginx في حالتنا- الذي يوجّه الطلبات من قبل المستخدمين إلى الخادوم الوسيط بينه (الخلفي) Backend Server. يستخدم Nginx التعليمة fastcgi_pass للتعريف عن الخادوم الفعلي الذي ستوجّه إليه الطلبات باستخدام برتوكول FastCGI. فمثلًا، لتوجيه أي طلب موافق للغة PHP إلى الخادوم الخلفي المخصص لمعالجة PHP باستخدام برتوكول FastCGI، فقد يبدو المقطع البرمجي للموقع Location Block مشابهًا لما يلي: # server context location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; } . . . في الواقع، لن يعمل المقطع السابق إذا ما تم تنفيذه على حاله لأنه يقدّم معلوماتٍ قليلةً جدًا. في كل مرّة يتم فيها إنشاء اتصال وسيط Proxy Connection فإنه يجب ترجمة الطلب الأصلي لضمان أن يتمكن الخادوم الخلفي من فهم الطلب المُدار Proxied request. وباعتبار أننا نغيّر البرتوكولات بتمرير الطلب عبر FastCGI فإن ذلك يتطلب بعض العمل الإضافي، سنصطلح في هذه المقالة على عملية تمرير الطلبات عبر FastCGI بتمرير FastCGI أي FastCGI pass. إنّ القيام بالوساطة من بروتوكول Http وإليه http-to-http Proxying ينطوي بشكل رئيسي على زيادة حجم الترويسة Header ليضمن أن يمتلك الخادوم الخلفي المعلومات التي يحتاجها للرد على الخادوم الوسيط والذي ينوب بدوره عن المستخدم Client، أما FastCGI فهو برتوكول منفصل لا يستطيع قراءة ترويسات Http. مع أخذ ذلك بالحسبان فإن أية معلومات ذات صلة يجب أن تمرّر للخادوم الخلفي عبر وسيلة أخرى. إن الطريقة الأساسية لتمرير المعلومات الإضافية عند استخدام برتوكول FastCGI هي باستخدام الإعدادات Parameters. حيث يجب أن يُهيّئ الخادوم الخلفي لقراءة تلك الإعدادات ومعالجتها والتصرّف وفقًا لقيمها. يستطيع Nginx أن يحدّد إعدادات FastCGI باستخدام التعليمة fastcgi_param. إن الحد الأدنى للإعدادات الواجب ضبطها حتى يعمل FastCGI كوكيل PHP مشابهة لما يلي: # server context location ~ \.php$ { fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass 127.0.0.1:9000; } . . . لقد قمنا في الإعدادات السابقة بضبط إعدادات FastCGI وهما REQUEST_METHOD و SCRIPT_FILENAME. إن كل من الإعدادات السابقة ضرورية للخادوم الخلفي حتى يتمكن من فهم طبيعة الطلب. إذ أن الإعداد الأول يخبره بنوع العملية التي يجب أن يقوم بإنجازها، أما الإعداد الثاني فيخبر مجموعة الخواديم Upstream - التي تعالج طلبات الخادوم الوسيط- بالملف الذي يجب أن ينفّذه. استخدمنا في المثال بعض المتغيرات variables في Nginx لضبط قيم تلك الإعدادات. سيحوي المتغير request_method$ على نوع الوظيفة http method التي طلبها المستخدم. من ناحية أخرى فإن قيمة الإعداد SCRIPT_FILENAME هي عبارة عن دمج قيمتي المتغيرين document_root$ و fastcgi_script_name$. أما المتغير document_root$ فسيحوي على مسار المجلد الرئيسي والذي ضبط عبر التعليمة root. سيضبط المتغير fastcgi_script_name$ بمعرف الموارد الموحد URI المطلوب. إن انتهى معرف URI للطلب request URI بالرمز (/)، فستلحق قيمة التعليمة fastcgi_index في النهاية. إن هذا النوع من المرجعية الذاتية self-referential في تعريف الموقع ممكن لأننا نشغّل معالج FastCGI على نفس الجهاز الذي يشغّل Nginx. فلنلقي نظرة على مثال آخر: # server context root /var/www/html; location /scripts { fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_index index.php; fastcgi_pass unix:/var/run/php5-fpm.sock; } . . .إن اختير الموقع location في المثال السابق ليعالج طلبًا في المسار: /scripts/test/ فإن قيمة المسار الوسيط SCRIPT_FILENAME ستنتج عن دمج قيم تعليمة root ومعرّف URI للطلب بالإضافة للتعليمة fastcgi_index. وبالتالي ستكون قيمة الإعداد في مثالنا هذا هي: var/www/html/scripts/test/index.php/ هنالك تعديل هام آخر قمنا به في الإعدادات السابقة، إذ أننا ضبطنا خادوم FastCGI الخلفي FastCGI backend ليستخدم مقبس يونيكس Unix Socket عوضًا عن مقبس شبكة Network socket. يمكن لـ Nginx أن يستخدم أيًا من الواجهتين Interface ليتصل مع مجموعة خواديم FastCGI Upstream. إن كان معالج FastCGI يعمل على نفس الخادوم فعادةً ما يُستحسن استخدام مقبس يونيكس من أجل الأمان. شرح إعدادات FastCGIهنالك قاعدة أساسية لكتابة نص برمجي (كود) Code قابل للصيانة وهي محاولة اتباع مبدأ DRY أي لا تكرر نفسك "Don’t Repeat Yourself". يساعد ذلك في تقليل الأخطاء بالإضافة لجعل الكود قابلًا لإعادة الاستخدام كما أنها تتيح تنظيمًا أفضل. إنّ أحد أكثر التوصيات الأساسية لإدارة Nginx هي أن تُضبط كل الأوامر في أوسع مجال ممكن لها، تطبّق تلك الأهداف الأساسية على إعداداتNginx أيضًا. عند التعامل مع إعدادات الوسيط لـ FastCGI فسيتم تشارك الغالبية العظمى للإعدادات في معظم حالات الاستخدام. بسبب ذلك وبسبب الطريقة التي تعمل بها وحدة الوراثة inheritance التابعة لـ Nginx فإنه دائمًا ما يستحسن تعريف الوسطاء في مجال عام. التصريح عن تفاصيل إعدادات FastCGI في السياقات الأم Parent Contextsإن أحد الطرق لتقليل التكرار هو التصريح عن تفاصيل الإعدادات في السياق الأم، أي سياق أعلى. يمكن لكل الإعدادات الموجودة خارج التعليمة fastcgi_pass أن تُضبط في مستويات أعلى. إذ أنها ستتدفق للأسفل نحو الموقع الذي تم منه تمرير الإعداد. وذلك يعني أنّه يمكن لعدّة أماكن أن تستخدم نفس الإعدادات. فمثلًا، يمكننا أن نعدّل على القسم العلوي من المقطع البرمجي السابق لجعله مفيدًا في عدة مواقع: # server context root /var/www/html; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_index index.php; location /scripts { fastcgi_pass unix:/var/run/php5-fpm.sock; } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; } . . .إنّ كلًا من تصاريح fastcgi_param والتعليمة fastcgi_index في المثال السابق متوفرين لدى مقطعي الموقع location block الواردين لاحقًا. تلك هي إحدى الطرق لإزالة التكرار. بكل الأحوال، إن للإعدادات السابقة مساوئ كبيرة، إن صُرّح عن أية fastcgi_param ضمن سياق أدنى فلن تتم وراثة أيٍّ من قيم fastcgi_param ضمن السياقات العليا (السياقات الأم). أي إما أن تستخدم القيم الموروثة فقط أو لا تستخدم أيًا منها. تُعد التعليمة fastcgi_param مصفوفةً array من التعليمات في لغةNginx. من منظور المستخدمين فإن أي مصفوفة تعليمات هي بشكل أساسي تعليمة يمكن استخدامها أكثر من مرة ضمن سياق واحد. وأي تصريح لاحق سيضيف معلومات جديدة إلى ما يعرفه Nginx من التصريح السابق. لقد صمّمت التعليمة fastcgi_param كمصفوفة من التعليمات حتى تسمح للمستخدمين بضبط عدّة إعدادات. تورّث أي مصفوفة تعليمات إلى سياق أدنى، أو سياق ابن child context، بطريقة مختلفة عن بعض التعليمات الأخرى. ستورّث معلومات مصفوفة التعليمات إلى سياقات الأبناء فقط إن لم تكن موجودة في أي مكان ضمن سياق الابن. وذلك يعني أنك إن استخدمت تعليمة fastcgi_param ضمن موقعك فإنها ستمحي القيم الموروثة من السياق الأعلى تمامًا. فمثلًا، يمكننا التعديل قليلًا على الإعدادات السابقة كما يلي: # server context root /var/www/html; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_index index.php; location /scripts { fastcgi_pass unix:/var/run/php5-fpm.sock; } location ~ \.php$ { fastcgi_param QUERY_STRING $query_string; fastcgi_pass 127.0.0.1:9000; } . . .قد تعتقد للوهلة الأولى أنه ستتم وراثة الإعدادات REQUEST_METHOD و SCRIPT_FILENAME في مقطع لموقع ثانٍ، بالإضافة لتوفير الإعداد QUERY_STRING ضمن ذلك السياق المحدّد. لكن ما يحدث في الحقيقة هو أنّ كل قيم fastcgi_param التابعة للأب ستُمحى ضمن السياق الثاني، وسيضبط الإعداد QUERY_STRING فقط. بينما ستبقى الإعدادات REQUEST_METHOD و SCRIPT_FILE دون ضبط. ملاحظة حول القيم المتعدّدة للوسطاء ضمن نفس السياقيجدر بالذكر هنا أثار ضبط قيم متعدّدة لنفس الإعدادات ضمن سياق واحد. فلنأخذ المثال التالي لمناقشة ذلك: # server context location ~ \.php$ { fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SCRIPT_FILENAME $request_uri; fastcgi_param DOCUMENT_ROOT initial; fastcgi_param DOCUMENT_ROOT override; fastcgi_param TEST one; fastcgi_param TEST two; fastcgi_param TEST three; fastcgi_pass 127.0.0.1:9000; } . . .في المثال السابق، قمنا بضبط الإعدادات TEST و DOCUMENT_ROOT عدّة مرات ضمن سياقٍ واحد. باعتبار أن fastcgi_param مصفوفة تعليمات فإن كلّ تصريح لاحق سيُضاف إلى سجلات Nginx الداخلية. سيحظى الإعداد TEST بعدة تصاريح ضمن المصفوفة يضبط من خلالها بالقيم one و two و three. من المهم أن ندرك هنا أنّ كلّ ذلك سيمرّر إلى خادوم FastCGI الخلفي دون أي معالجة إضافية من Nginx. يعني ذلك أنّ القرار حول كيفية معالجة تلك القيم يعود بشكل كامل إلى معالج Nginx المختار. للأسف، على اختلاف معالجات FastCGI فإن كلًا منها يعالج القيم الممرّرة بطريقة تختلف تمامًا عن الأخرى. فمثلًا، إن استقبلت PHP-FPM الإعدادات السابقة فستفسر القيم النهائية لتحل محل أي قيم سابقة. بهذه الحالة سيُضبط الإعداد TEST بالقيمة three. وبالمثل سيضبط الإعداد DOCUMENT_ROOT بالقيمة override. ولكن إن مُرّرت القيم السابقة لخادومٍ مثل FsgiWrap، فستفسَّر القيم بشكل مختلف كثيرًا. ففي البداية سيقوم بتمرير تمهيدي ليقرر أي القيم ينبغي استخدامها لتشغيل البريمج script. وسيستخدم القيمة initial للإعداد DOCUMENT_ROOT ليبحث عن البريمج. إلا أنه عندما سيمرّر القيم الفعلية للبريمج فسيمرر القيم الأخيرة كما يفعل PHP-FPM تمامًا. إن التضارب inconsistency وعدم إمكانية التنبؤ unpredictability يعني أنك لا تستطيع، بل أنه لا ينبغي لك، أن تعتمد على الخادوم الخلفي لتفسير نيّتك بشكل صحيح إذا ما ضبطّت الإعدادات أكثر من مرّة واحدة. فالطريقة الوحيدة الآمنة هي بالتصريح عن كل إعداد مرّةً واحدة. يعني ذلك أيضًا أنه لا يوجد طريقة آمنة لإعادة تعريف overriding القيمة الافتراضية باستخدام تعليمة fastcgi_param. طريقة تضمين إعدادات مكتوبة بملف منفصل ضمن ملف إعدادات FastCGI الأساسييوجد طريقة أخرى لتفصل بنود إعداداتك الشائعة، أي التي تقوم بها بشكل متكرّر. بإمكاننا استخدام تعليمة include لقراءة محتوى ملف منفصل وتضمينها في الموقع الذي تم فيه التصريح عن التعليمة. يعني ذلك أنه بإمكاننا الاحتفاظ بكل الإعدادات الشائعة في ملف واحد ومن ثمّ تضمينه في أي مكان ضمن إعداداتنا إن اقتضى الأمر. وباعتبار أن Nginx سيضع محتوى الملف حيثما وجدت تعليمة include فلن نكون قد ورثنا من سياق الأم إلى سياق الابن. سيمنع ذلك قيم fastcgi_param من أن تُمحى وسيمنحنا القدرة على ضبط إعدادات إضافية عند الحاجة. يمكننا في البداية أن نضبط قيم إعدادات FastCGI الشائعة لدينا في ملف منفصل وتخزينه ضمن المسار الحاوي على ملف الإعدادات الأصلي، سنقوم بتسمية ذلك الملف fastcgi_common: fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;يمكننا الآن أن نستورد محتوى الملف حيثما نريد استخدام قيم تلك الإعدادات: # server context root /var/www/html; location /scripts { include fastcgi_common; fastcgi_index index.php; fastcgi_pass unix:/var/run/php5-fpm.sock; } location ~ \.php$ { include fastcgi_common; fastcgi_param QUERY_STRING $query_string; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_index index.php; fastcgi_pass 127.0.0.1:9000; } . . . لقد قمنا هنا بنقل قيم بعض الإعدادات الشائعة إلى ملف اسمه fastcgi_common والموجود في المسار الافتراضي الحاوي على إعدادات NGINX. ثمّ نستورد القيم المصرّح عنها في الملف عندما نحتاج لإدخالها. هنالك العديد من الأمور التي يجب ملاحظتها حول هذه الإعدادات: أولًا، لم نضع في الملف الذي ننوي استيراده أي قيم قد نرغب بتخصيصها وفقًا لموقعه، بسبب مشكلة التفسير التي ذكرناها سابقًا والتي تحدث عندما نضبط عدة قيم لنفس الإعداد، ولأنه لا يمكن ضبط التعليمات التي ليست على شكل مصفوفة non-array directives إلا مرّةً واحدة خلال السياق، لذا نضع العناصر التي لن نرغب بتغييرها فقط في الملف المشترك. إنّ كلّ تعليمة (أو إعداد أساسي) قد نرغب بتخصيصه وفقًا لموقعه يجب ألا يضمّن في الملف المشترك. الأمر الآخر الذي ربما قد لاحظته أننا قد ضبطنا بعض إعدادات FastCGI الإضافية في المقطع البرمجي للموقع الثاني. وتلك هي الإمكانية التي كنا نأمل الوصول إليها. تمكّنا من إضافة إعدادات fastcgi_param إضافية كلما اقتضت الحاجة ومن دون أن تُمسح القيم المشتركة. استخدام الملف fastcgi_params أو الملف fastcgi.confبأخذ الاستراتيجية السابقة بالحسبان فإن مطوري Nginx والعديد من فرق تحزيم التوزيعات distribution packaging teams قد عملوا على توفير مجموعة من الإعدادات شائعة الاستخدام والتي يمكن أن تضمنها في مواقع تمرير FastCGI أي FastCGI pass location. تدعى تلك الإعدادات fastcgi_params أو fastcgi.conf. إنّ هذين الملفين متشابهين كثيرًا ولكن مع وجود اختلاف وحيد ممثّل في تمرير قيم متعدّدة لإعداد واحد، وهي المشكلة التي ناقشناها سابقًا، لا يحتوي fastcgi_params على تصريح عن الإعداد SCRIPT_FILENAME على عكس الملف fastcgi.conf. لقد أوجِد الملف fastcgi_params منذ وقتٍ أبعد. ثم ظهرت الحاجة لإنشاء ملف جديد عندما اتُّخذ قرارٌ لتوفير قيمةٍ افتراضية للإعداد SCRIPT_FILENAME وذلك حتى نتجنب تجزئة الإعدادات التي تعتمد على fastcgi_params، إن عدم فعل ذلك قد يؤدي إلى ضبط ذلك الإعداد في كل من الملف المشترك وموقع تمرير FastCGI. العديد من مشرفي تحزيم التوزيعات الشهيرة قد صوّتوا لتضمين إحدى هذين الملفين فقط أو لتطابق محتواهما تمامًا. إن توفّر لديك أحد هذين الملفين فقط فاستخدمه وعدّل عليه كما تشاء ليناسب احتياجاتك. إن كان كلا الملفين متوفرين لك، فقد يكون من الأفضل أن تضمّن الملف fastcgi.conf في معظم مواقع تمرير FastCGI، وذلك لأنه يتضمّن تصريحًا عن الإعداد SCRIPT_FILENAME. عادةً ما يُرغب بذلك إلا أنّ هنالك بعض الحالات التي قد ترغب فيها بتخصص هذه القيمة. يمكن تضمين هذين الملفين عبر الإشارة إلى موقعهما بالنسبة إلى المجلد الجذر root الحاوي على إعدادات Nginx. إن مسار المجلد الجذر لإعداداتNginx يكون مشابهًا لما يلي: etc/nginx/ وذلك إن تم تثبيته باستخدام مدير الحزم package manager. يمكنك تضمين الملفين كما يلي: # server context location ~ \.php$ { include fastcgi_params; # You would use "fastcgi_param SCRIPT_FILENAME . . ." here afterwards . . . }أو كما يلي: # server context location ~ \.php$ { include fastcgi.conf; . . . }تعليمات وإعدادات ومتغيرات FastCGI المهمّةلقد قمنا في القسم السابق بضبط عدد لا بأس به من الإعدادات بهدف شرح مفاهيمٍ أخرى، وعادةً ما كنا نضبطها بمتغيرات Nginx. كما أننا قدمنا بعض تعليمات FastCGI من دون شرحٍ وفير. سنناقش في هذا القسم بعض التعليمات شائعة الضبط، وبعض الإعدادات التي قد تحتاج لتعديلها، بالإضافة لبعض المتغيرات التي قد تحوي معلوماتٍ تحتاجها. تعليمات FastCGI الشائعة:سنستعرض فيما يلي بعض أكثر التعليمات المفيدة للعمل مع تمريرات FastCGI: fastcgi_pass: هي التعليمة التي تقوم بعملية تمرير الطلبات الموجودة ضمن السياق الحالي إلى الخادوم الخلفي. تعرٍّف هذه التعليمة الموقع الذي يمكن الوصول عبره إلى معالج FastCGI.fastcgi_param: وهي مصفوفة التعليمات التي يمكن استخدامها لضبط قيم الإعدادات. غالبًا ما يقترن استخدام هذه التعليمة مع متغيرات Nginx لضبط وسطاء FastCGI بالقيم المحدّدة في الطلب.try_files: ليست تعليمةً خاصة بـ FastCGI، إلا أنها استخدامها شائع في موقع تمرير FastCGI، عادةً ما تستخدم كجزء من إجراء الطلب الوقائي للتأكد من وجود الملف المطلوب قبل تمريره إلى معالج FastCGI.include: مجدّدًا، ليست تعليمةً خاصة بـ FastCGI، إلا أنها تستخدم بكثافة عالية في سياقات تمرير FastCGI أي FastCGI pass contexts. غالبًا ما تستخدم لتضمين تفاصيل الإعدادات الشائعة والمشتركة بين عدّة مواقع.fastcgi_split_path_info: تستخدم هذه التعليمة لتعريف التعابير النمطية regular expression مع مجموعتي التقاط captured groups. تُستخدم المجموعة الأولى كقيمة للمتغير fastcgi_script_name$ بينما تستخدم المجموعة الثانية كقيمة للمتغير fastcgi_path_info$. عادةً ما تستخدم كلا المجموعتين لتحليل parse الطلب بشكل صحيح حتى يعرف المعالج أي الأجزاء المشكّلة للطلب تمثل الملفات الواجب تشغيلها وأي الأجزاء تمثل المعلومات الإضافية الواجب تمريرها إلى السكربت.fastcgi_index: تعرّف هذه التعليمة ملف الفهرسة index file الذي ينبغي الحاقه بقيمة الإعداد fastcgi_script_name$ والتي تنتهي بـ (/.عادةً ما يكون ذلك مفيدًا إن كان الإعداد SCRIPT_FILENAME مضبوطًا بقيمة document_root$fastcgi_script_name$ وكان مقطع الموقع معدّا لقبول الطلبات الحاوية على معلومات المسار الإضافية.fastcgi_intercept_errors: تعرّف هذه التعليمة فيما إذا كان ينبغي معالجة الأخطاء المتلقاة من خادوم FastCGI أو تمريرها مباشرةً إلى المستخدم.تمثّل التعليمات السابقة أكثر التعليمات التي ستستخدمها عندما تصمّم تمرير FastCGI نموذجي. ومع أنك قد لا تستخدمها كلها بنفس الوقت، إلا أنك ستلاحظ أنها تتفاعل بشكل وثيق جدًا مع إعدادات ومتغيرات FastCGI التي سنتحدث عنها فيما يلي. المتغيرات شائعة الاستخدام مع FastCGI:قبل أن نتمكن من الكلام عن الإعدادات التي ستستخدمها على الأرجح مع تمريرات FastCGI، علينا الكلام قليلًا عن بعض نتغيرات Nginx الشائعة والتي ستستفيد منها في ضبط تلك الإعدادات. تعرّف وحدة FastCGI الخاصة بـ Nginx بـ Nginx’s FastCGI module بعض تلك المتغيرات، إلا أن معظمها من الوحدة الأساسية Core module. query_string$ أو args$: هي القيم الممرّرة arguments في الطلب الأصلي للمستخدم. is_args$: ستساوي قيمتها “?” إن حوى الطلب على قيم ممرّرة وإلا فستُضبط قيمتها بسلسلة نصية فارغة. يفيد ذلك عند إنشاء إعدادات قد تأخذ قيمًا أو لا تأخذ. request_method$: يشير هذا المتغير إلى نوع الطلب request method المستخدم الأصلي. يمكن أن يفيد ذلك بتحديد فيما إذا كان ينبغي السماح لعملية operation ما في السياق الحالي أم لا. content_type$: يضبط هذا المتغير بقيمة الحقل content-type الموجود في ترويسة الطلب. يحتاج الوسيط إلى هذه المعلومات إن كان نوع الطلب المستخدم هو POST وذلك حتى يتمكن من معالجة المحتويات اللاحقة بشكل صحيح. content_length$: يضبط هذا المتغير قيمة الحقل Content-Length الموجود في ترويسة المستخدم. هذه المعلومات لازمة لأي طلب للمستخدم من النوع POST. fastcgi_script_name$: يحتوي هذا المتغير على ملف السكربت الذي يجب تشغيله. إن انتهى الطلب بـ (/) فستحلق قيمة التعليمة fastcgi_index إلى النهاية. وبحال استخدام التعليمة fast_split_path_info فستضبط قيمة هذا المتحول بالمجموعة اللاقطة الأولى المعرّفة من قبل التعليمة. ينبغي على قيمة هذا المتغير أن تشير إلى الملف الفعلي الذي يجب تشغيله. request_filename$: يحتوي هذا المتغير على مسار الملف المطلوب. يحصل المتغير على هذه القيمة بأخذ المسار الحالي لمجلد المستندات الأساسي document root، مع أخذ كل من التعليمتين root و alias بعين الاعتبار. بالإضافة لأخذ قيمة المتغير fastcgi_script_name$. تعد هذه الطريقة مرنةً للغاية لإسناد قيمة للإعداد SCRIPT_FILENAME. request_uri$: يحتوي هذا المتغير على الطلب كاملًا كما استُقبل من المستخدم، يتضمّن ذلك السكربت، أية معلومات مسار إضافية، بالإضافة إلى أي عبارة استعلام query string. fastcgi_path_info$: يحتوي هذا المتغير على معلومات المسار الإضافية التي قد تتوفّر بعد اسم السكربت في الطلب. وقد تحوي قيمته أحيانًا على موقعٍ آخر ينبغي على السكربت أن يعلم به حتى يتم تنفيذه. يحصل هذا المتغير على قيمته من مجموعة التعابير النمطية regex الثانية عند استخدام تعليمة fastcgi_split_path_info. document_root$: يحتوي هذا المتغير على المسار الحالي لمجلد المستندات الأساسي. وسيتم ضبطه تبعًا لتعليمتي root و alias. uri$: يحتوي هذا المتغير على قيمة URI الحالية بعد تسويتها normalization. وبما أن تعليمات معينة تقوم بإعادة الكتابة rewrite أو بإعادة التوجيه الداخلي internallly redirect يمكن أن تترك أثرًا على معرّف URI فإن هذا المتغير سيستخلّص تلك التعديلات. كما ترى، يوجد العديد من المتغيرات المتوفّرة لك عندما تقرّر كيف تضبط إعدادات FastCGI. العديد منها متشابه إلا أنها تملك بعض الفوارق الصغيرة والتي ستنعكس على تنفيذ بريمجك. إعدادات FastCGI الشائعة:تمثّل إعدادات FastCGI المعلومات التي نرغب بجعلها متاحةً لمعالج FastCGI - الذي نرسل إليه الطلبات- على شكل أزواج مفتاح-قيمة key-value. لن تحتاج كلّ التطبيقات إلى نفس الإعدادات لذلك غالبًا ما ستحتاج لمراجعة توثيق التطبيق. تُعد بعض تلك الإعدادات ضروريةً للمعالج حتى يتمكن من تحديد السكربت - الذي ينبغي تشغيله- بشكل صحيح. بينما أُتيح البعض الآخر للسكربت، إذ أنها قد تغيّر سلوكه إن كان معدًّا ليعتمد على مجموعةٍ من الإعدادات. QUERY_STRING: ينبغي أن يُضبط هذا الإعداد بأي عبارة استعلام مقدّمة من قِبل المستخدم. عادةً ما تكون على شكل أزواج مفتاح-قيمة مزوّدةً بعد إشارة "?" في معرّف URI. عادةً ما يُضبط هذا الإعداد بقيمة أحد المتحولين query_string$ أو args$، إذ ينبغي عليهما أن يحويا على نفس البيانات. REQUEST_METHOD: يدّل هذا الإعداد معالجَ FastCGI على نوع الإجراء الذي طلبه المستخدم. وهو أحد الإعدادات القلائل التي يجب ضبطها حتى يتم التمرير بشكل صحيح. CONTENT_TYPE: إن ضُبط نوع الطلب في الإعداد السابق ليكون POST فيجب أن يُضبط هذا الإعداد. يشير هذا الإعداد إلى نوع البيانات التي ينبغي على معالج FastCGI توقعها. غالبًا ما يضبط هذا الإعداد بالمتغير content_type$ فقط، والذي يضبط بدوره وفقًا للمعلومات المأخودة من الطلب الأصلي. CONTENT_LENGTH: يجب أن يضبط هذا الإعداد إن كان نوع الطلب هو POST. يشير هذا الإعداد إلى طول المحتوى، وغالبًا ما يضبط بالمتغير content_length$ والذي يأخذ قيمته من المعلومات المتوفّرة في طلب المستخدم الأصلي. SCRIPT_NAME: يستخدم هذا الإعداد ليُشير إلى اسم السركبت الأساسي الذي يجب تشغيله. وهو إعداد هامٌ للغاية يمكن ضبطه بعدّة طرق وفقًا لاحتياجاتك. عادةً ما يُضبط بالمتغير fastcgi_script_name$ والذي يجب أن يكون معرّف URI للطلب، أو معرف URI للطلب متبوعًا بـ fastcgi_index إن انتهى المعرّف بـ (/)، أو المجموعة اللاقطة الأولى إن استُخدم fastcgi_fix_path_info. SCRIPT_FILENAME: يحدّد هذا الإعداد الموقع الفعلي على القرص للسكربت الذي يجب تشغيله. وبسبب ارتباطه مع الإعداد SCRIPT_NAME، تقترح بعض أدلة الاستخدام Guides أن تستعيض عن استخدامه بالمتغيرين document_root$fastcgi_script_name$. يتوفر بديلٌ آخر يملك العديد من المحاسن وهو استخدام request_filename$. REQUEST_URI: ينبغي أن تحوي على معرّف URI للطلب كاملًا ومن من دون تعديل، بالإضافة إلى السكربت الذي يجب تشغيله، ومعلومات المسار الإضافية، وأي قيمٍ ممرّرة. تفضّل بعض التطبيقات أن تحلّل تلك المعلومات بنفسها، ويقدم هذا الإعداد المعلومات اللازمة لهم للقيام بذلك. PATH_INFO: إن ضُبط cgi.fix_pathinfo بالقيمة “1” في ملف إعدادات PHP، فسيحوي هذا الإعداد على أية معلومات مسار إضافية additional path information مذكورة بعد اسم السكربت. عادةً ما يُستخدم هذا الإعداد لتعريف الملف الممرّر file argument والذي سيحدّد تصرُّف السكربت. قد يترتّب على ضبط cgi.fix_pathinfo بالقيمة "1" آثارٌ أمنية إن لم تكن طلبات السكربت قد فُحصت بوسائل أخرى (سنناقش ذلك لاحقًا). قد يُضبط هذا الإعداد أحيانًا بالمتغير fastcgi_path_info$ والذي يحوي على المجموعة اللاقطة لثانية من التعليمة fastcgi_split_path_info. وفي حالاتٍ أخرى، سيكون هنالك حاجةٌ لاستخدام متغير مؤقت، إذ أنه يمكن لمعالجةٍ أخرى أن تمسح تلك القيمة أحيانًا. PATH_TRANSLATED: يربط هذا الإعداد بين معلومات المسار المحتواة ضمن PATH_INFO والمسار الفعلي على نظام الملفات filesysem path. عادةً ما يُضبط هذا الإعداد بـ document_root$fastcgi_path_info$، ولكن أحيانًا يجب استبدال المتغير الأخير بالمتغير المؤقت الذي أشرنا إليه سابقًا. التحقق من الطلبات قبل تمريرها إلى FastCGI إنّ أحد المواضيع بالغة الأهمية التي لم نغطها حتى الآن هو كيفية تمرير الطلبات الديناميكية للخادوم بشكلٍ آمن. إن تمرير كلّ الطلبات للخادوم الخلفي بغض النظر عن صحتها لا يسبب خفض فاعلية الخادوم فحسب، بل إنه خَطِرٌ أيضًا. إذ أنه من الممكن للمهاجمين إنشاء طلباتٍ خبيثة في محاولةٍ منهم لتشغيل نص برمجي ما. في الواقع، حتى نعالج هذا الخطأ علينا أن نضمن إرسال الطلبات الصالحة فقط لمعالج FastCGI، يمكننا القيام بذلك بعدة طرق وذلك تبعًا لاحتياجات إعدادنا الخاص وفيما إن كان معالج FastCGI يعمل على نفس النظام المشغِّل لـ Nginx أم لا. إحدى القواعد الأساسية التي يجب أن تحدد لنا كيفية ضبط الإعدادات، هي أنه علينا ألا نسمح بأي معالجة أو تفسير لملفات المستخدم. إذ أنه من السهل نسبيًا على المستخدمين المخادعين malicious users أن يضمّنوا نصًا برمجيًا صالحًا valid code في ملفاتٍ تبدو في الظاهر أنها ملفاتٍ غيرُ مؤذيةٍ كالصور. حالما يُرفع ملفٌ كهذا إلى الخادوم علينا أن نضمن ألا يجد طريقًا للوصول إلى معالج FastCGI. إنّ المشكلة الأساسية التي نحاول حلها هنا هي في الواقع محدّدةٌ في مواصفات CGI specification. تتيح لك تلك المواصفات تحديد ملف السكربت الذي ترغب بتشغيله متبوعًا بمعلومات المسار الإضافية التي يمكن للسكربت أن يستخدمها. إن نموذج التنفيذ هذا يسمح للمستخدمين بطلب معرف URI قد يبدو كسكربت صالح legitimate، إلا أن الجزء الذي سيتم تنفيذه سيكون مذكورًا قبله في المسار. فالنأخذ مثلًا طلبًا لـ: test.jpg/index.php/ إن كانت إعداداتك تمرّر ببساطة كل الطلبات التي تنتهي بـ .php إلى المعالج دون التحقق من مدى صحتها فسيتحقق المعالج، إن كان يحقّق المواصفات، من وجود ذلك الموقع وسينفذّه إن أمكن. أما إذا لم يجد الملف فسيتّبع المواصفات وسيحاول تنفيذ الملف test.jpg/ وسيعتبر أن index.php/ تمثّل معلومات المسار الإضافية الخاصة بالسكربت. كما ترى يمكن لذلك أن يسمح ببعض النتائج غير المرغوب بها إطلاقًا عندما يجتمع مع فكرة رفع المستخدم للملفات. هنالك مجموعة من الطرق المختلفة لحل تلك المشكلة. أسهل تلك الطرق هو ببساطة تعطيل خاصية معالجة معلومات المسار من قِبَل المعالج، وذلك إن كان تطبيقك لا يعتمد على تلك المعلومات الإضافية. يمكن تعطيل تلك الخاصية لدى PHP-FPM من خلال الملف php.ini، فمثلًا إن كنت تستخدم نظام أوبونتو Ubuntu فيمكن تحرير الملف التالي: sudo nano /etc/php5/fpm/php.iniببساطة، ابحث عن الخيار cgi.fix_pathinfo وألغِ تعليقه ثم اضبطه بالقيمة “0” لتعطّل هذه “الميزة” كما يلي: cgi.fix_pathinfo=0أعد تشغيل عملية PHP-FPM حتى تأخذ التعديلات حيّز التنفيذ: sudo service php5-fpm restartسيدفع ذلك PHP لتنفيذ الجزء الأخير من المسار فقط، وبالتالي ففي المثال السابق إن لم يكن الملف test.jpg/index.php/ موجودًا فسيصدر PHP خطأ عوضًا عن محاولة تنفيذ الملف test.jpg/ إذا كان معالج FastCGI يعمل على نفس الخادوم الذي يُشغل Nginx فلدينا خيارٌ آخر وهو ببساطةٍ التحقق من وجود الملفات على القرص قبل تمريرها إلى المعالج. فإذا لم يكن الملف التالي موجودًا test.jpg/index.php/ فسيصدر خطأ. أما إن كان موجودًا فسيُرسل إلى الخادوم الخلفي لتتم معالجته. سينتج عن ذلك عمليًا سلوكٌ يشبه إلى حد بعيد ما رأيناه سابقًا. location ~ \.php$ { try_files $uri =404; . . . }إن كان تطبيقك يعتمد في سلوكه على معلومات المسار حتى يقوم بعملية التفسير بشكل صحيح فما زال بإمكانك السماح بتطبيق ذلك السلوك بأمان عبر القيام بالاختبارات قبل اتخاذ قرارٍ بإرسال الطلب إلى الخادوم الخلفي. فمثلًا، بإمكاننا مطابقة المجلدات التي نخرن فيها الملفات المرفوعة غير الموثوقة تحديدًا وضمان ألا تمرّر إلى المعالج. مثلًا، إن كان مجلد الملفات المرفوعة على المسار: /uploads/ فعلينا إنشاء مقطع موقع يتم مطابقته قبل أن يتم تقييم أي عبارة منتظمة: location ^~ /uploads { }بإمكاننا إضافة أي نوع من معالجة ملفات PHP داخل المقطع البرمجي: location ^~ /uploads { location ~* \.php$ { return 403; } }سيعمل الموقع الأم parent location على مطابقة أي طلب يبدأ بـ uploads/ وأيُّ طلب يتعامل مع ملفات PHP سيُرد عليه بالخطأ 403 عوضًا عن إرساله إلى الخادوم الخلفي. يمكنك أيضًا استخدام التعليمة fastcgi_splite_path_info للتعريف يدويًا عن الجزء من الطلب، الذي يجب تفسيره كسكربت والجزء الدي يجب تعريفه على أنه معلومات المسار الإضافية باستخدام التعابير النمطية. يسمح لك ذلك بأن تستمر في الاعتماد على وظيفة معلومات المسار، على أن تعرّف بدقة ما تعتبره بريمجًا وما تعتبره مسارًا. فمثلًا، بإمكاننا إعداد مقطع لموقع يقوم باعتبار أن أول جزء مكون للمسار ينتهي ب .php هو السكربت الذي ينبغي تشغيله. ويعتبر الباقي معلومات المسار الإضافية. وذلك يعني أنه من أجل الطلب: test.jpg/index.php/ فسيتم إرسال كامل المسار إلى المعالج باعتباره اسم السكربت ولا يحوي معلومات مسار إضافية. يمكن أن يكون الموقع كالآتي: location ~ [^/]\.php(/|$) { fastcgi_split_path_info ^(.+?\.php)(.*)$; set $orig_path $fastcgi_path_info; try_files $fastcgi_script_name =404; fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $request_filename; fastcgi_param PATH_INFO $orig_path; fastcgi_param PATH_TRANSLATED $document_root$orig_path; }ينبغي أن يعمل المقطع السابق عندما تكون قيمة cgi.fix_pathinfo في إعداد PHP مضبوطةً بالقيمة "1" حتى تسمح بمعلومات المسار الإضافية. وهنا، إن مقطع الموقع السابق لا يطابق الطلبات التي تنتهي بـ .php وحسب، بل تلك التي تنتهي بـ .php قبل إشارة "/" مباشرةً والتي تدل على مكون إضافي يتبع مسار المجلد directory. تعرّف التعليمة fastcgi_split_path_info داخل المقطع مجموعتين لاقطتين captured group باستخدام التعابير النمطية. تطابق المجموعة الأولى الجزء الممتد من بداية عنوان URI وحتى أول ورود لـ .php ويضعه في المتغير fastcgi_script_name$. ومن ثم يضع أي معلومة مذكورة في الجزء الباقي، والممتد بدءًا من النقطة التي انتهى عندها الجزء الأول وحتى نهاية العنوان، في المجموعة اللاقطة الثانية والتي تخزّن في متغير يدعى fastcgi_path_info$. نستخدم التعليمة set لنخزن القيمة المحفوظة في fastcgi_path_info$، حتى هذه النقطة، في متغير يدعى orig_path$. وذلك لأن قيمة المتغير fastcgi_path_info$ ستُمحى في لحظة عبر التعليمة try_files. بإمكاننا التحقق من اسم السكربت الذي التقطناه سابقًا باستخدام التعليمة try_files. وهي عبارة عن عملية تطبّق على ملف file operation تتأكد من أنّ السكربت الذي نحاول تشغيله مخزن على القرص. ولكن لذلك تأثيرًا جانبيًا يتمثل في مسح قيمة المتحول fastcgi_path_info$. بعد القيام بالتمرير التقليدي عبر FastCGI فإننا نضبط SCRIPT_FILENAME كما العادة. نضبط أيضًا الإعداد PATH_INFO بالقيمة التي المخزّنة في المتغير orig_path$. فبالرغم من أن قيمة fastcgi_path_info$ قد مُسحت، إلا أن قيمته الأصلية محفوظة في ذلك المتغير. كما أننا نضبط الوسيط PATH_TRANSLATED ليربط map بين معلومات المسار الإضافية والموقع الذي تُحفظ فيه على القرص، نقوم بذلك عبر دمج قيمتي المتغيرين document_root$ و orig_path$. يسمح لنا ذلك ببناء طلباتٍ مثل: index.php/users/view/ بحيث يتمكن الملف: index.php/ من معالجة المعلومات المتعلقة بالمجلد: users/view/ وبنفس الوقت نتجنب المواقف التي سيُشغّل بها الطلب: test.jpg/index.php/ سيُضبط البريمج دائمًا بأقصر مكوّن ينتهي بـ .php، وبالتالي سنتجنب تلك المشكلة. يمكننا أيضًا تحقيق ذلك باستخدام تعليمة alias إن احتجنا إلى تغيير موقع ملفات السكربتات. علينا فقط أن نأخذ ذلك بالحسبان في كل من ترويسة الموقع وتعريف fastcgi_split_path_info. location ~ /test/.+[^/]\.php(/|$) { alias /var/www/html; fastcgi_split_path_info ^/test(.+?\.php)(.*)$; set $orig_path $fastcgi_path_info; try_files $fastcgi_script_name =404; fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $request_filename; fastcgi_param PATH_INFO $orig_path; fastcgi_param PATH_TRANSLATED $document_root$orig_path; }سوف يسمح لك ذلك لتشغيل تطبيقاتك التي تستخدم الإعداد PATH_INFO بأمان. تذكّر أنه عليك تعديل الخيار cgi.fix_pathinfo في ملف php.ini وضبطه بالقيمة "1" حتى يعمل بشكل صحيح. قد يترتب عليك أيضًا أن تعطّل security.limit_extensions في الملف php-fpm.conf. الخلاصةنأمل أنك تملك الآن فهمًا أفضل لإمكانية قيام FastCGI بالوساطة عنNginx. تسمح هذه الإمكانية لـ Nginx باستخدام قوته في معالجة الاتصالات بسرعة وتحضير المحتوى الثابت static content، بينما تُحمّل مسؤولية المحتوى الديناميكي dynamic content لبرمجيات مناسبة لذلك. يسمح FastCGI لـ Nginx بالعمل مع عدد كبير من التطبيقات، عبر إعدادات آمنة وذات أداء. ترجمة – وبتصرّف– للمقال Understanding and Implementing FastCGI Proxying in Nginx لمؤلفه Justin Ellingwood.