<?xml version="1.0"?>
<rss version="2.0"><channel><title>DevOps</title><link>https://academy.hsoub.com/devops/</link><description/><language>ar</language><item><title>&#x643;&#x64A;&#x641; &#x62A;&#x633;&#x62A;&#x641;&#x64A;&#x62F; &#x645;&#x646; &#x62D;&#x627;&#x633;&#x648;&#x628;&#x643; &#x627;&#x644;&#x642;&#x62F;&#x64A;&#x645;</title><link>https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D9%81%D9%8A%D8%AF-%D9%85%D9%86-%D8%AD%D8%A7%D8%B3%D9%88%D8%A8%D9%83-%D8%A7%D9%84%D9%82%D8%AF%D9%8A%D9%85-r852/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2026_03/Untitleddesign(1).png.5524c6baa89b08bccef7b2cb65c5169a.png" /></p>
<p>
	تشير <a href="https://geomar.net.pl/en/blog/eko_rozwoj/240-million-pcs-will-become-e-waste-when-windows-10-support-ends/#:~:text=Microsoft's%20end%20of%20Windows%2010,capabilities%20in%20the%20tech%20industry." rel="external nofollow">بعض التقديرات</a> إلى أن 240 مليون حاسوب سيصبح خارج الخدمة بين عشية وضحاها بسبب فقد الدعم البرمجي مع انتهاء دعم نظام ويندوز 10 واشتراطات ويندوز 11 الصارمة للحواسيب التي يقبل التثبيت عليها -مثل TPM 2.0- إضافة إلى مشاكله المتعلقة بخصوصية بيانات المستخدمين وإلحاحه المستمر عليك لاستخدام خدمات مايكروسوفت منذ اللحظة التي تثبت فيها النظام، بدءًا من اشتراط حساب في مايكروسوفت عند التثبيت إلى مزامنة بياناتك في OneDrive إلى CoPilot الذي يصعب إلغاء تفعيله.
</p>

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

<h2 id="-">
	لماذا لم ينتشر لينكس من قبل؟
</h2>

<p>
	قبل أن نفصل في الفوائد التي ستجنيها من تثبيت لينكس على حاسوبك القديم ينبغي أن نجيب على السؤال الذي سيطرح نفسه على من يفكر في الانتقال إلى نظام بديل عن الذي تعود عليه لسنوات، وهو سبب قلة انتشار لينكس على الحواسيب قبل هذه الظاهرة الجديدة لويندوز 11، أليس منطقيًا أن ينتشر وتستخدمه فئة لا بأس بها من الناس إن كان بديلًا قويًا لويندوز؟ فما الذي يضمن لي أن ينفذ المهام التي أحتاج إليها في عملي أو حياتي الشخصية؟
</p>

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

<p>
	فنظام أندرويد مثلًا هو أحد الأنظمة المبنية على لينكس، وأغلب الأجهزة المنزلية الحديثة مثل أجهزة التلفاز وغيرها تشغلها أنظمة لينكس وإن كان المستخدم العادي لا يتعامل معه مباشرة في بعض الأحيان، وجميع الحواسيب الخارقة تقريبًا تعمل بأنظمة لينكس، وما بقي الآن إلا الحواسيب المكتبية والمحمولة.
</p>

<p>
	والسبب الذي جعل انتشاره بطيئًا في هذه الفئة وفقًا لرأي لينوس تورفالدز -مبتكر نواة لينكس- هو نفس سبب انتشارها الساحق في الأجهزة الأخرى، أن هذه الأجهزة تأتي بنظام لينكس مثبت مسبقًا عليها، أما الحواسيب المكتبية فالغالب أنها تأتي بدون نظام تشغيل أو مع نظام تشغيل ويندوز.
</p>

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

<h2 id="-">
	هل سيعمل لينكس على حاسوبي القديم؟
</h2>

<p>
	يستهلك نظام ويندوز في العادة بين 2-4 جيجابايت من الذاكرة العشوائية (الرام) دون استخدام الحاسوب فعليًا في تنفيذ أي مهام، بينما تستهلك أنظمة لينكس ذات الواجهات الخفيفة مثل XFCE أو LXQt حوالي 500 - 800 ميجابايت فقط، مما يترك لك مساحة أكبر في الذاكرة للمهام والبرامج التي ستعمل عليها. إليك بعض الأمثلة الأخرى التي تتميز فيها أنظمة لينكس:
</p>

<h3 id="-">
	تقليل الضغط على المعالج
</h3>

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

<h3 id="-">
	الأمان من الفيروسات
</h3>

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

<h3 id="-">
	خفة النظام وسرعته
</h3>

<p>
	يعتمد نظام ويندوز -وماك أيضًا- بكثافة على معالج رسوميات GPU لعرض مؤثرات بصرية للمستخدم مثل الظلال والشفافية وغيرها، وصحيح أن هذا موجود في أنظمة لينكس بل وربما أكثر من هذين النظامين، إلا أن الجميل في لينكس هو كثرة الخيارات المتكاملة المتاحة في هذا الشأن، فتستطيع استخدام واجهات خفيفة للغاية لا تكاد تستهلك شيئًا من موارد الحاسوب مقارنة بنظام ويندوز، ومن ثم لا تحتاج إلى معالج رسوميات قوي لتشغيله، بل تستطيع استخدام لينكس دون واجهة رسومية أصلًا إن شئت العمل من بيئة سطر الأوامر!
</p>

<p>
	ولقد جربت هذا في العديد من الحواسيب القديمة التي لدي على اختلاف أنواعها وأجيالها، فمنها حواسيب مكتبية من أوائل التسعينيات حرفيًا إلى أجيال من بداية الألفية، وحواسيب محمولة أعمارها تتراوح بين 15 وعشرين عامًا، وكنت أجد توزيعة لينكس تعمل بكفاءة على أي من هذه الحواسيب مهما كانت إمكانياته قديمة أو ضعيفة بسبب كثرة الخيارات المتاحة من التوزيعات المختلفة.
</p>

<h3 id="-hdd">
	مشاكل أقراص HDD
</h3>

<p>
	يعاني نظام ويندوز من مشكلة تجزئة الملفات fragmentation مع الأقراص الصلبة القديمة من نوع HDD بسبب نظام ملفات NTFS الذي يستخدمه، أما أنظمة لينكس فتستخدم أنظمة ملفات أسرع ولا تعاني من هذه المشكلة، مثل ext4 أو Btrfs.
</p>

<h3 id="-">
	الاستقرار البرمجي
</h3>

<p>
	لا شك أنك واجهت شاشة الموت الزرقاء في ويندوز مرة واحدة على الأقل، أو فتحته لتعمل عليه فوجدت النظام قد سقط فجأة ودون إنذار، وهي مشكلة تكاد تكون معدومة في أنظمة لينكس إذ هي مستقرة إلى أبعد الحدود، ويحتوي متجر البرامج على آلاف البرامج والأدوات المجانية والمدفوعة التي تستطيع تثبيتها مباشرة من المتجر بضغطة زر دون الحاجة إلى حساب للمتجر أو تحديث تلك البرمجيات يدويًا لاحقًا.
</p>

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

<h3 id="-">
	أنظمة لينكس حرة ومجانية
</h3>

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

<h2 id="-">
	ماذا عن البرامج التي أستخدمها؟
</h2>

<p>
	صحيح أنك قد لا تجد بعض البرامج المتخصصة التي اعتدت عليها في نظام ويندوز مثل طقم المكتب Ms Office مثلًا، لكن أغلب البرامج مغلقة المصدر أو التي تعمل على نظام ويندوز إما لها نسخ لأنظمة لينكس ستجدها في متجر التطبيقات الخاص بالنظام أو على الموقع الخاص بالجهة المطورة للتطبيق أو البرنامج، كما في حالة المتصفحات مثلًا، أو برامج بديلة تؤدي وظيفتها مثل المتصفحات ومحررات النصوص ومشغلات الصوت والفيديو وغيرها، كما في حالة أطقم المكتب حيث ستجد طقم ليبر أوفيس وأوبن أوفيس المقابلين لمكتب ويندوز MS Office.
</p>

<p>
	حتى في حالة البرامج المتخصصة قد تجد إما نسخة منها تعمل على الويب في المتصفح بغض النظر عن نظام التشغيل التي تعتمد عليه، أو برامج بديلة له، فمثلًا لدينا <a href="https://academy.hsoub.com/design/graphic/gimp/" rel="">برنامج جِمب GIMP</a> البديل لبرنامج فوتوشوب، و<a href="https://academy.hsoub.com/design/illustration/inkscape/" rel="">برنامج إنكسكيب Inkscape</a> البديل لبرنامج Illustrator، و<a href="https://academy.hsoub.com/design/3d/blender/" rel="">برنامج بلندر Blender</a> البديل لبرنامج التصميم ثلاثي الأبعاد 3ds Max، والذي <span ipsnoautolink="true">تستخدمه كل من وكالة ناسا ووكالة إيسا للفضاء في تصميم محاكاة واقعية للثقوب السوداء مثلًا، وكذلك شركة BMW في تصميم منتجاتها</span>، وقس عليها في البرامج الهندسية وبرامج التصميم والفيديو وغيرها.
</p>

<p>
	توجد بعض المواقع المفيدة لمعرفة البرامج البديلة المتاحة لتطبيق معين تستخدمه، مثل موقع <a href="https://www.opensourcealternative.to/" rel="external nofollow">opensourcealternative</a> أو <a href="https://alternativeto.net/" rel="external nofollow">alternativeto</a> حيث تكتب فيها اسم التطبيق الذي تستخدمه فيرشح لك البدائل المتاحة لأنظمة لينكس، والتي تكون غالبًا مفتوحة المصدر ومجانية معًا.
</p>

<h2 id="-">
	أمثلة التوزيعات المتاحة
</h2>

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

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

<h3 id="-">
	توزيعة فيدورا
</h3>

<p style="text-align: left;">
	توفر <a href="https://fedoraproject.org/ar/" rel="external nofollow">توزيعة فيدورا</a> Fedora Linux عدة إصدارات للتحميل مباشرة موجهة للاستخدامات المختلفة، لكن سنركز على إصدارين فقط هما جنوم Gnome وكِدي بلازما KDE Plasma، وهما بيئتا سطح مكتب أو واجهات مرئية لسطح المكتب، هذا ما تتعامل معه كل يوم من قوائم وأزرار وأيقونات وغيرها، وتشبه <a href="https://fedoraproject.org/ar/workstation/" rel="external nofollow">بيئة جنوم</a> الواجهة المرئية لنظام ماك. انظر الصورة التالية من بيئة جنوم لتوزيعة فيدورا:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="181587" href="https://academy.hsoub.com/uploads/monthly_2026_03/fedora_gnome.png.b8c97ba79a84d9e25f6d303fc9c6e123.png" rel=""><img alt="fedora_gnome.png" class="ipsImage ipsImage_thumbnailed" data-fileid="181587" data-ratio="56.33" data-unique="m3m218q49" style="width: 600px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2026_03/fedora_gnome.thumb.png.fc4b0d169fc3e77a70862d0ea2901951.png"></a>
</p>

<p>
	والصورة التالية من بيئة كِدي بلازما، وهي أشبه بنظام ويندوز في الاستخدام ومناسبة أكثر لمن تعود عليه:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="181588" href="https://academy.hsoub.com/uploads/monthly_2026_03/fedora_KDE.png.620798654211f97928de5de9230f7e5b.png" rel=""><img alt="fedora_KDE.png" class="ipsImage ipsImage_thumbnailed" data-fileid="181588" data-ratio="56.33" data-unique="td88tq4b0" style="width: 600px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2026_03/fedora_KDE.thumb.png.14006907f9ec5fb2defe349ed99b9b72.png"></a>
</p>

<p>
	تُبنى هذه التوزيعة على توزيعة أم لها تسمى ريدهات RedHat Linux، وهي موجهة إلى بيئة الشركات والخوادم أكثر ولها دعم مدفوع طويل الأمد، أما فيدورا نفسها فمجانية ولا تحتاج إلى اشتراك أو ترخيص أو غيره لاستخدامها، وتحديثاتها مجانية كذلك.
</p>

<p>
	يصدر الإصدار الجديد من فيدورا كل ستة أشهر، ويكون في أبريل وأكتوبر من كل عام، ويستمر دعم كل إصدار لمدة 13 شهر بعده، هذا غير التحديثات المستمرة التي تكون في كل أسبوع تقريبًا.
</p>

<h3 id="-">
	توزيعة أوبنتو
</h3>

<p>
	بالمثل هنا فإن <a href="https://ubuntu.com/download" rel="external nofollow">توزيعة أوبنتو</a> مبنية على توزيعة أخرى هي ديبيان Debian، وهي توزيعة حرة مجانية وموجهة كذلك إلى الاستخدام العام، ومنها عدة إصدارات مخصصة كما في فيدورا للاستخدامات المختلفة أو بيئات سطح المكتب المختلفة مثل جنوم وكِدي وغيرهما. انظر المثال التالي من بيئة جنوم في إحدى توزيعات أوبنتو:
</p>

<p style="text-align: center;">
	<span id="cke_bm_1566C" style="display: none;"> </span><a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="181589" href="https://academy.hsoub.com/uploads/monthly_2026_03/ubuntu_gnome.png.432c65fed4dcb701a9fc2cc666459037.png" rel=""><img alt="ubuntu_gnome.png" class="ipsImage ipsImage_thumbnailed" data-fileid="181589" data-ratio="56.33" data-unique="fu84j4gas" style="width: 600px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2026_03/ubuntu_gnome.thumb.png.90924a9bef7a9e36cb1751c99e3dcba8.png"></a>
</p>

<p>
	وهذا المثال من بيئة كِدي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="181590" href="https://academy.hsoub.com/uploads/monthly_2026_03/kubuntu_25_10.png.d3c36bffb3083a88b281182cb3de1f2d.png" rel=""><img alt="kubuntu_25.10.png" class="ipsImage ipsImage_thumbnailed" data-fileid="181590" data-ratio="56.22" data-unique="2yo76a588" style="width: 600px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2026_03/kubuntu_25_10.thumb.png.51ab28ad8a2406702219d8ae45bdd352.png"></a>
</p>

<p>
	تصدر توزيعة أوبنتو الجديدة كل ستة أشهر كذلك، مرة في أبريل ومرة في أكتوبر وتحمل التوزيعة حينها اسم العام والشهر -مثال: أوبنتو 25.10 صدرت في شهر أكتوبر عام 2025-، ويستمر الدعم لمدة 9 أشهر لكل إصدار. كذلك تُصدر كل عامين إصدارًا له دعم لمدة خمس سنوات فيما تسميه الدعم طويل الأمد Long Term Support LTS، ويصدر في العادة في شهر أبريل من الأعوام ذات الأرقام الزوجية، على سبيل المثال كانت الإصدارات طويلة الدعم السابقة في أبريل 2022، وأبريل 2024، ويُتوقع أن تخرج الثالثة وقت كتابة هذا المقال في أبريل 2026.
</p>

<h3 id="-">
	توزيعة أوبن سوزا
</h3>

<p>
	تُعد توزيعة <a href="https://www.opensuse.org/" rel="external nofollow">أوبن سوزا OpenSuse</a> إحدى أقدم التوزيعات الموجودة على الساحة إذ تعود جذور الشركة التي بدأتها إلى 1992، وتعتمد طريقتين في التحديثات كما في أوبنتو، لكنها تقسمها إلى إصدارين مستقلين:
</p>

<ul>
	<li>
		<a href="https://get.opensuse.org/leap/16.0/" rel="external nofollow">إصدار Leap</a>: يخرج هذا الإصدار مرة في كل عام، ويشبه تثبيت أي نظام حيث يستطيع المستخدم تحميل نسخته وتثبيته أو ترقيته من داخل نظامه الحالي إن كان يستخدم نسخة أقدم. يستمر دعم هذا الإصدار عامين.
	</li>
	<li>
		<a href="https://get.opensuse.org/tumbleweed/?type=desktop#download" rel="external nofollow">إصدار Tumbleweed</a>: لا توجد لهذا الإصدار نسخ أو ترقيات مرحلية في وقت معين، بل تثبتها مرة واحدة وكلما خرج تحديث جديد وصل إلى حاسوبك كأي تحديث عادي، هذا الإصدار لمن يريد تثبيت النظام مرة واحدة وينساه. دعم هذا الإصدار مستمر نظريًا إلى ما لا نهاية طالما حافظت على تثبيت التحديثات بانتظام.
	</li>
</ul>

<p>
	انظر المثال التالي من بيئة كِدي في أوبن سوزا Leap:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="181591" href="https://academy.hsoub.com/uploads/monthly_2026_03/opensuse_leap.png.4baf98c3fa1eabb694d7a525fc2d60c9.png" rel=""><img alt="opensuse_leap.png" class="ipsImage ipsImage_thumbnailed" data-fileid="181591" data-ratio="52.00" data-unique="f02w1evsl" style="width: 600px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2026_03/opensuse_leap.thumb.png.5e9ddfeaeadb990c3b0cf3d05400362a.png"></a>
</p>

<h3 id="-">
	كلمة حول بيئات سطح المكتب
</h3>

<p>
	كما نرى في الصور السابقة للتوزيعات فإن بيئتي كِدي KDE وجنوم Gnome تشبهان نظامي ماك وويندوز إلى حد كبير، لكن توجد بيئات أخرى غيرها توفرها كل توزيعة في موقعها للتثبيت والاستخدام الجاهز كما أشرنا من قبل في حالة الإصدارات المختلفة والاستخدامات المتخصصة، فالتوزيعة ما هي إلا مجموعة من البرمجيات تتكامل مع بعضها لتعطيك تجربة استخدام معينة وفقًا للهدف منها.
</p>

<p>
	وإذا جربت إحدى التوزيعات ولم تعجبك البيئة الموجودة فيها فتستطيع تثبيت واحدة أخرى على نفس النظام دون أن تغير النسخة التي لديك كما كان يحدث في ويندوز، تستطيع تخيل الأمر كأن لديك إصدارات ويندوز 10 و8 و7 وXP على نفس الحاسوب، وتختار ما تشاء منها في كل مرة تسجل الدخول!
</p>

<h4 id="-">
	النظام الحي
</h4>

<p>
	لتجنب تكديس حاسوبك بالبرامج التي تأتي بها كل بيئة افتراضيًا، تستطيع تحميل إصدار التوزيعة بالبيئة التي تريد تجربتها وحرقها على إصبع ذاكرة USB flash باستخدام برنامج مثل <a href="https://rufus.ie/en/" rel="external nofollow">Rufus</a>، والإقلاع منها كنظام حي، حيث تستطيع تجربة النظام بالكامل ببرامجه لتعتاد عليه قبل تثبيته على حاسوبك.
</p>

<h2 id="-">
	تثبيت النظام
</h2>

<p>
	يطلب برنامج التثبيت الخاص بكل توزيعة بعض الخطوات الأساسية مثل اختيار البلد واللغة ولوحة المفاتيح، واسم المستخدم وكلمة المرور ومكان تثبيت التوزيعة، ويفضل تخصيص مساحة مخصصة من القرص للنظام أو تثبيته داخل القسم المخصص لنظام ويندوز، لكن بما أننا نفترض أن الحاسوب قديم هنا فسيحل لينكس نفسه مكان القسم المخصص لويندوز.
</p>

<h3 id="-">
	انتبه إلى خطوة تقسيم القرص الصلب
</h3>

<p>
	هنا أمر ينبغي التنبيه عليه إذ يقع فيه أغلب من يثبتون النظام لأول مرة، وقد وقعت فيه بنفسي حين ثبت توزيعة أوبنتو لأول مرة في 2010، وهو اختيار مسح القرص الصلب وتثبيت لينكس دون الانتباه إلى أن هذا سيمحو كل بياناتي الموجودة على القرص الصلب، وليس نظام ويندوز فقط.
</p>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="181592" href="https://academy.hsoub.com/uploads/monthly_2026_03/erase_disk.png.ee5d177f96900443d159db4dc8879626.png" rel=""><img alt="erase_disk.png" class="ipsImage ipsImage_thumbnailed" data-fileid="181592" data-ratio="62.11" data-unique="vf4lhfz7l" style="width: 600px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2026_03/erase_disk.thumb.png.a54351abf11ac019f5597b23e383fa52.png"></a>
</p>

<p>
	تظهر الصورة الخيار الثاني الذي يمسح البيانات الموجودة على القرص ويثبت التوزيعة عليه بدلًا منها، لا ننصح بهذا الخيار أبدًا إلا إن كنت تعلم ما تفعل، والخيار الآمن والأفضل هو أن تختار التثبيت إلى جانب النظام الموجود، والذي سيظهر إن كان لديك نظام ويندوز -في حالتنا هنا يقترح النظام مسح أوبنتو وإعادة تثبيتها لأنها مثبتة بالفعل على الحاسوب وأستخدمها-، أو اختيار التقسيم اليدوي الذي سيظهر لك الأقسام الموجودة على القرص الصلب كما في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="181593" href="https://academy.hsoub.com/uploads/monthly_2026_03/manual.png.534c86fe8f5b19690d6c6661dd3f8f3a.png" rel=""><img alt="manual.png" class="ipsImage ipsImage_thumbnailed" data-fileid="181593" data-ratio="53.22" data-unique="reosdg064" style="width: 600px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2026_03/manual.thumb.png.668bd2331e22a19c629b217d545331a0.png"></a>
</p>

<p>
	والتي تستطيع منها اختيار القسم الذي تريد حذفه ثم تثبيت أوبنتو عليه، لكن هذا الخيار متقدم قليلًا وربما تحتاج الاطلاع على بعض الشرح المتقدم حوله، لكن عمومًا يفضل أن تجعل القسم الذي تثبت عليه لينكس من نوع <code>ext4</code> أو <code>btrfs</code> وهو المقابل لنظام ملفات <code>NTFS</code> في ويندوز -لاحظ الجزء البرتقالي في الشريط الموجود في الصورة أعلاه والذي يمثل القسم الخاص بنظام لينكس على هذا الحاسوب-، وأن تجعل <code>/</code> هي نقطة الضم الخاصة بذلك القسم، وهي تشير ببساطة إلى أن هذا القسم سيكون هو القسم الجذر الذي يحمل بيانات النظام -يماثل قسم <code>C</code> في نظام ويندوز-.
</p>

<h2 id="-">
	خاتمة
</h2>

<p>
	تعرفنا في هذا المقال على بعض مزايا لينكس التي تساهم في إحياء كثير من الحواسيب القديمة وإعادتها للخدمة مرة أخرى، وكذلك استعرضنا فلسفته الحرة ومفتوحة المصدر التي تحمي حقوق المستخدم وخصوصيته على خلاف بعض أنظمة التشغيل الأخرى، نرجو لك تجربة إحياء ممتعة لحواسيبك القديمة!
</p>

<p>
	تبقى نصيحة أخيرة أجلناها لأنها قد تكون غير مناسبة لجميع الحواسيب القديمة لأن هذه قد لا تصلح للحواسيب الأقدم ولأننا نفترض استخدام الحاسوب كما هو، تلك النصيحة هي زيادة حجم الرام أو تغيير القرص الصلب إلى قرص من نوع SSD إذا كان الحاسوب يدعم هذا، فكلا الأمرين يزيد سرعة الحاسوب إلى حد كبير إما بزيادة قدرته على فتح برامج أكثر في نفس الوقت في حالة الرام أو بزيادة سرعة تنفيذ المهام العادية عند تغيير القرص الصلب إلى نوع SSD.
</p>

<h2 id="-">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%85%D9%86-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%A8%D8%B3%D8%B7-%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-r575/" rel="">كيفية تثبيت توزيعة أوبنتو من لينكس بأبسط طريقة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">ما هو نظام التشغيل لينكس؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/apps/operating-systems/linux/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%B3%D8%B7%D8%AD-%D9%85%D9%83%D8%AA%D8%A8-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-2004-r779/" rel="">تعرف على سطح مكتب أوبونتو 20.04</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D9%88%D9%84%D9%85%D8%A7%D8%B0%D8%A7-%D8%AA%D9%88%D8%AC%D8%AF-100-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D9%85%D9%86%D9%87%D8%9F-r698/" rel="">ما هو نظام لينكس ولماذا توجد 100 توزيعة منه؟</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">852</guid><pubDate>Sat, 07 Mar 2026 08:20:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x648;&#x627;&#x645;&#x631; &#x627;&#x644;&#x623;&#x633;&#x627;&#x633;&#x64A;&#x629; &#x644;&#x645;&#x62D;&#x631;&#x631; &#x627;&#x644;&#x646;&#x635;&#x648;&#x635; &#x646;&#x627;&#x646;&#x648; Nano</title><link>https://academy.hsoub.com/devops/general/%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-%D9%84%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%86%D8%A7%D9%86%D9%88-nano-r851/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_05/Nano.png.292ff91fb17b73b1f9a2b80cec487b79.png" /></p>
<p>
	جنو نانو GNU Nano هو محرر نصوص لأنظمة التشغيل التي تشبه نظام التشغيل يونكس أو بيئات التشغيل التي تستخدم سطر الأوامر كواجهة للإدخال، طُرح لأول مرة عام 1999 بواسطة Chris Allegretta وهو يحاكي محرر النصوص بيكو Pico.
</p>

<p>
	يُعَد محرر النصوص نانو nano المحرر المفضل لدى كثير من المستخدمين لأنه يحتوي على كل ما يحتاجه المستخدم سواءً المبتدئ أو المتوسط أو المتقدم؛ إذ يُعَد محرر سهلًا جدًا في عملية التنقل، ويمتلك بعض المميزات الرائعة التي ربما لا تكون على دراية كافية بها، لذلك سنحاول في هذا المقال تقديم كل الأساسيات التي قد تحتاجها للبدء باستخدام محرر النصوص نانو Nano.
</p>

<h2 id="nano">
	اختصارات لوحة المفاتيح الشائعة لمحرر النصوص نانو Nano
</h2>

<p>
	عند العمل مع أي أداة من أدوات سطر الأوامر، فلا بد من معرفة معظم اختصارات الأوامر، لأن ذلك يسرع من تنفيذ تلك الأوامر، وبالتالي سنبدأ بمعرفة أهم الاختصارات لمحرر النصوص نانو Nano من خلال القائمة التالية:
</p>

<table>
	<thead>
		<tr>
			<th>
				الاختصار
			</th>
			<th>
				الوصف أو الوظيفة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>Ctrl+G</code>
			</td>
			<td>
				عرض قائمة المساعدة وقائمة الاختصارات
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+X</code>
			</td>
			<td>
				الخروج من محرر النصوص نانو Nano
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+O</code>
			</td>
			<td>
				حفظ الملف الحالي
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+R</code>
			</td>
			<td>
				فتح الملف للقراءة ضمن ذاكرة التخزين المؤقت buffer
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+C</code>
			</td>
			<td>
				إظهار موقع المؤشر (سطر، عمود، حرف)
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+V</code>
			</td>
			<td>
				نقل المؤشر إلى الشاشة التالية
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+Y</code>
			</td>
			<td>
				نقل المؤشر إلى الشاشة السابقة
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+K</code>
			</td>
			<td>
				قص السطر الحالي وتخزينه في ذاكرة التخزين المؤقت cutbuffer
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+U</code>
			</td>
			<td>
				لصق محتويات ذاكرة التخزين المؤقت cutbuffer في المكان الحالي للمؤشر
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+A</code>
			</td>
			<td>
				نقل المؤشر إلى بداية السطر الحالي
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+E</code>
			</td>
			<td>
				نقل المؤشر إلى نهاية السطر الحالي
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+W</code>
			</td>
			<td>
				البحث عن سلسلة نصية أو تعبير نمطي
			</td>
		</tr>
		<tr>
			<td>
				<code>\+ctrl</code>
			</td>
			<td>
				استبدال السلسلة النصية أو التعبير النظامي
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+T</code>
			</td>
			<td>
				النقل إلى الكلمة السابقة في السطر الحالي
			</td>
		</tr>
		<tr>
			<td>
				<code>Ctrl+F</code>
			</td>
			<td>
				النقل إلى الكلمة التالية في السطر الحالي
			</td>
		</tr>
		<tr>
			<td>
				<code>Alt+A</code>
			</td>
			<td>
				اختيار بداية النص من المكان الحالي للمؤشر
			</td>
		</tr>
		<tr>
			<td>
				<code>Alt+6</code>
			</td>
			<td>
				نسخ النص المحدد إلى ذاكرة التخزين المؤقت cutbuffer
			</td>
		</tr>
		<tr>
			<td>
				<code>Alt+U</code>
			</td>
			<td>
				التراجع عن آخر عملية
			</td>
		</tr>
		<tr>
			<td>
				<code>Alt+E</code>
			</td>
			<td>
				إعادة آخر عملية تم التراجع عنها
			</td>
		</tr>
	</tbody>
</table>

<h2 id="nano-1">
	البداية مع محرر النصوص نانو Nano
</h2>

<p>
	سنطلعك في هذه الفقرة على جميع الأمور الأساسية التي قد تحتاجها عند استخدام محرر النصوص نانو Nano.
</p>

<h3 id="1">
	1. التعرف على الواجهة
</h3>

<p>
	سيظهر لنا الخرج التالي عند فتح محرر النصوص نانو Nano عن طريق فتح نافذة الطرفية وكتابة الأمر <code>nano</code> فيها كما في الشكل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172911" href="https://academy.hsoub.com/uploads/monthly_2025_05/1.png.b86602c8eaf0a1efe7f357bcbf087ab0.png" rel=""><img alt="1" class="ipsImage ipsImage_thumbnailed" data-fileid="172911" data-unique="875ooe1wp" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/1.thumb.png.9164242d2bee52d68992e510d38d44bb.png"> </a>
</p>

<p>
	تظهر الاختصارات المهمة لمحرر النصوص نانو Nano في أسفل الشاشة عند تشغيل محرر النصوص نانو nano، سواءً كان ذلك مع تحميل ملف أو بدون تحميل للرجوع اليها بسرعة ولسهولة استخدامها.
</p>

<p>
	يستخدم محرر النصوص نانو nano رمزين مرتبطين لكل اختصار، وذلك بخلاف محررات واجهة المستخدم الرسومية GUI والتي تعرض الرمز مباشرةً كما يلي:
</p>

<ul>
	<li>
		الرمز <code>^</code> وهو الرمز الأساسي الذي يشير إلى المفتاح <code>CTRL</code>
	</li>
	<li>
		الرمز <code>M</code> والمعروف باسم مفتاح الوصف meta key ويشير إلى المفتاح <code>ALT</code>
	</li>
</ul>

<p>
	وبالتالي إذا أردنا استخدام الاختصار <code>G^</code> فلا بد من الضغط على <code>CTRL+G</code>؛ أما عندما نريد استخدام الاختصار <code>M-A</code>، فسيتوجب علينا الضغط على <code>ALT+A</code>.
</p>

<h3 id="2nano">
	2. فتح أو إنشاء الملفات باستخدام محرر النصوص نانو Nano
</h3>

<p>
	لفتح ملف باستخدام محرر النصوص نانو nano، كل ما علينا فعله هو كتابة الأمر <code>nano</code> ثم إتباعه باسم الملف `nano <filename>.</filename>
</p>

<p>
	على سبيل المثال إذا أردنا فتح الملف mylogs.log بمحرر النصوص نانو nano، فيكفي كتابة الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_3418_6" style=""><span class="pln">nano mylogs</span><span class="pun">.</span><span class="pln">log</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="gif" data-fileid="172908" href="https://academy.hsoub.com/uploads/monthly_2025_05/1.gif.99a19e5258a0ae6d3483b6caa8d5f44a.gif" rel=""><img alt="1" class="ipsImage ipsImage_thumbnailed" data-fileid="172908" data-unique="gkwawqpej" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/1.thumb.gif.22f50aae3e11514ae0d89b7f3200ab2a.gif"> </a>
</p>

<p>
	أما إذا كان الملف غير موجود وأردنا إنشاء ملف جديد باستخدام محرر النصوص نانو nano والكتابة فيه، فيجب علينا كتابة اسم الملف غير الموجود بعد الأمر <code>nano</code>، وسيفتح لنا ملف فارغ يمكننا إجراء التغييرات التي نريدها فيه <code>&lt;nano  &lt;NewFile</code>. على سبيل المثال، يمكننا إنشاء ملف جديد باسم <code>unknown.txt</code> وكتابة بعض الأسطر فيه ثم نحفظ التغييرات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_3418_8" style=""><span class="pln">nano </span><span class="pun">&lt;</span><span class="typ">NewFile</span><span class="pun">&gt;</span></pre>

<p>
	من ناحية أخرى، إذا لم نرد إضافة اسم للملف في البداية، معناها يمكننا إنشاء ملف فارغ بدون اسم عن طريق تنفيذ الأمر <code>nano</code> في محرر النصوص nano، ومن ثم كتابة الأسطر التي نريد فيه؛ وعندما نرغب في إغلاق الملف، سيطلب منا المحرر إدخال اسم للملف.
</p>

<p>
	على سبيل المثال، سنكتب رسالة تهنئة في محرر النصوص نانو nano ثم نحفظ الملف باسم Hello.txt كما في الشكل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="gif" data-fileid="172907" href="https://academy.hsoub.com/uploads/monthly_2025_05/3.gif.51a6327c565803463b6d52cfc4528323.gif" rel=""><img alt="3" class="ipsImage ipsImage_thumbnailed" data-fileid="172907" data-unique="fmzl0lvct" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/3.thumb.gif.39c266b42c670b024229c0ae2bdefbc1.gif"> </a>
</p>

<h3 id="3">
	3. التنقل ضمن الملف
</h3>

<p>
	يمكن التنقل ضمن محرر النصوص نانو Nano عن طريق مفاتيح الأسهم على عكس محررات النصوص المتقدمة مثل محرر النصوص فيم <a href="https://academy.hsoub.com/programming/workflow/%d8%aa%d8%b9%d8%b1%d9%81-%d8%b9%d9%84%d9%89-%d9%85%d8%ad%d8%b1%d8%b1-%d8%a7%d9%84%d9%86%d8%b5%d9%88%d8%b5-%d9%81%d9%8a%d9%85-vim-r1769/" rel="">Vim</a>، وفيما يلي بعض النصائح والملاحظات من أجل التنقل ضمن الملف بسرعة أكبر:
</p>

<ul>
	<li>
		الضغط <code>\ + ALT</code> للذهاب إلى بداية الملف
	</li>
	<li>
		الضغط <code>/ + ALT</code> للذهاب إلى نهاية الملف
	</li>
</ul>

<p>
	يمكن أيضًا القفز إلى سطر محدد من خلال الضغط على <code>_ + CTRL</code> وإدخال رقم السطر الذي نريد كما في الشكل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="gif" data-fileid="172905" href="https://academy.hsoub.com/uploads/monthly_2025_05/4.gif.dba9be53f2afba507c6ef0cb73426ad4.gif" rel=""><img alt="4" class="ipsImage ipsImage_thumbnailed" data-fileid="172905" data-unique="0ywfuge4p" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/4.thumb.gif.ab29bca6739eba77a8f180f017880619.gif"> </a>
</p>

<h3 id="4nano">
	4. عمليات القص والنسخ واللصق في محرر النصوص نانو Nano
</h3>

<p>
	يمكن في حال استخدامنا لبيئة سطح المكتب اختيار النص باستخدام الفأرة والضغط على <code>Ctrl + Shift + C</code> لنسخ النص المحدد ومن ثم لصقه باستخدام <code>Ctrl + Shift + V</code>.
</p>

<p>
	ولكن ما الحل إذا كنا لا نملك واجهة مستخدم رسومية GUI أو كنا نريد قص النص؟ بإمكاننا هنا ببساطة قص أو نسخ النص ولصقه في محرر النصوص نانو nano واستخدام الاختصارات، ولكن ذلك بعد تحديد النص المطلوب.
</p>

<p>
	من أجل تحديد النص، سنستخدم مفاتيح التنقل للوصول إلى نقطة البداية التي نريد تحديد النص منها، ثم نضغط على الاختصار <code>Alt + A</code> لوضع علامة التحديد، ومن ثمة نستخدم مفاتيح التنقل لتحديد النص الذي نريد قصه أو نسخه كما هو موضح بالشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="gif" data-fileid="172906" href="https://academy.hsoub.com/uploads/monthly_2025_05/5.gif.6e66191eb78aafb4fe9fbb5e389af2cc.gif" rel=""><img alt="5" class="ipsImage ipsImage_thumbnailed" data-fileid="172906" data-unique="dpixpv0gj" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/5.thumb.gif.3e6faffa9d6cc98f025fd6bd956cfff4.gif"> </a>
</p>

<p>
	نستطيع بعد تحديد النص القيام بأحد الأمرين:
</p>

<ul>
	<li>
		الضغط <code>Alt + 6</code> لنسخ النص المحدد
	</li>
	<li>
		الضغط <code>Ctrl + K</code> لقص النص المحدد
	</li>
	<li>
		وبعد قص أو نسخ النص، نضغط <code>Ctrl + U</code> للصق النص.
	</li>
</ul>

<p>
	يمكننا على سبيل المثال لصق النص المنسوخ في نهاية الملف كما في الشكل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="gif" data-fileid="172912" href="https://academy.hsoub.com/uploads/monthly_2025_05/copy-and-paste-text-in-nano-editor-1.gif.dc6c8e5e2d3660a087479027e8c21e26.gif" rel=""><img alt="copy-and-paste-text-in-nano-editor-1.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="172912" data-ratio="40.11" data-unique="mjcdgqutb" style="width: 700px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_05/copy-and-paste-text-in-nano-editor-1.thumb.gif.4f36c74265d18dd553f02004c7729a27.gif"></a>
</p>

<h3 id="5nano">
	5. حذف أسطر في محرر النصوص نانو Nano
</h3>

<p>
	لا يوجد خيار محدد لحذف النصوص أو الأسطر في محرر النصوص نانو Nano للأسف، ولكن يمكننا استخدام الطرق التالية للحصول على نفس النتيجة في المحرر:
</p>

<ol>
	<li>
		استخدام زر التراجع backspace عدة مرات لحذف النصوص أو الأسطر
	</li>
	<li>
		استخدام قص الأسطر بالضغط على الاختصار <code>Ctrl + K</code>
	</li>
</ol>

<p>
	تُعَد عملية قص الأسطر هي الخيار الأفضل لحذف النص أو الأسطر، وذلك لأن عملية اللصق لا تحدث كثيرًا، وهي الطريقة الأكثر فعالية لحذف النصوص.
</p>

<p>
	يمكننا تحديد عدة أسطر أو كلمات لقصها من أجل التخلص منها (حذفها) من خلال استخدام مفاتيح الأسهم كما يلي:
</p>

<ol>
	<li>
		الانتقال باستخدام مفاتيح الأسهم إلى النقطة التي تريد حذف الأسطر ابتداءً منها
	</li>
	<li>
		الضغط على الاختصار <code>Alt + A</code> لوضع العلامة
	</li>
	<li>
		الضغط على الاختصار <code>/ + ALT</code> للإنتقال إلى نهاية الملف
	</li>
	<li>
		الضغط على الاختصار <code>Ctrl + K</code> لقص النص المحدد
	</li>
</ol>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="gif" data-fileid="172913" href="https://academy.hsoub.com/uploads/monthly_2025_05/Delete-multiple-lines-in-the-nano-editor.gif.204f843c58017bc5119a97c19e3eb331.gif" rel=""><img alt="Delete-multiple-lines-in-the-nano-editor.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="172913" data-ratio="40.11" data-unique="jpi0fuowv" style="width: 700px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_05/Delete-multiple-lines-in-the-nano-editor.thumb.gif.aa4937d67a55a2de182537199d6f7c27.gif"></a>
</p>

<h3 id="6">
	6. التراجع والإعادة
</h3>

<p>
	تشير عملية التراجع Undo إلى العدول عن آخر عملية قام بها المستخدم، بينما تشير عملية الإعادة Redo إلى تكرار آخر عملية تم التراجع عنها.
</p>

<p>
	يمكننا استخدام الاختصارات التالية للقيام بعملية التراجع أو الإعادة في محرر النصوص نانو Nano:
</p>

<ul>
	<li>
		للتراجع عن آخر عملية، نستخدم الاختصار <code>Alt + U</code>
	</li>
	<li>
		لتكرار أو إعادة آخر عملية تم التراجع عنها، نستخدم الاختصار <code>Alt + E</code>
	</li>
</ul>

<p>
	سنحذف في المثال التالي سطرًا معينًا من الملف ثم نتراجع عن عملية الحذف، ثم نعيد عملية الحذف مرةً أخرى كما يلي:
</p>

<p style="text-align: center;">
	<img alt="undo-and-redo-in-nano-editor.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="172914" data-ratio="30.13" data-unique="04y5dxgbl" style="width: 700px; height: auto;" width="823" src="https://academy.hsoub.com/uploads/monthly_2025_05/undo-and-redo-in-nano-editor.gif.afb3790a07aa54f33f72c1497a6cb348.gif">
</p>

<h3 id="7">
	7. البحث والاستبدال
</h3>

<p>
	تتم عملية البحث والاستبدال في محرر النصوص نانو Nano باستخدام ثلاث خطوات بسيطة جدًا وهي:
</p>

<ol>
	<li>
		الضغط <code>Ctrl + R</code> ثم كتابة الكلمة أو المصطلح الذي نريد البحث عنه ثم نضغط Enter
	</li>
	<li>
		ندخل الكلمة الجديدة المراد الاستبدال بها
	</li>
	<li>
		ستظهر الكلمات المطابقة واحدةً تلو الأخرى وسيسألنا إذا كنا نريد استبدالها أم لا، وللقيام بعمليات الاستبدال دفعةً واحدة، سنضغط على الرمز <code>A</code>.
	</li>
</ol>

<p>
	سنستبدل في المثال التالي الكلمة quick بالكلمة lazy كما في الشكل:
</p>

<p style="text-align: center;">
	<img alt="search-and-replace-in-the-nano-editor.gif" class="ipsImage ipsImage_thumbnailed" data-fileid="172915" data-ratio="30.13" data-unique="g7bxx21j3" style="width: 700px; height: auto;" width="823" src="https://academy.hsoub.com/uploads/monthly_2025_05/search-and-replace-in-the-nano-editor.gif.f28f9b424c571b1d1b60dec48c042025.gif">
</p>

<h3 id="8nano">
	8. الحفظ والخروج من محرر النصوص نانو Nano
</h3>

<p>
	تتم عملية الحفظ والخروج من محرر النصوص نانو Nano بطريقة سهلة للغاية وعن طريق خطوتين فقط هما:
</p>

<ol>
	<li>
		الضغط على الاختصار <code>Ctrl + O</code> لحفظ التغييرات
	</li>
	<li>
		الضغط على الاختصار <code>Ctrl + X</code> للخروج من محرر النصوص
	</li>
</ol>

<p>
	ملاحظة: يمكن الخروج من محرر النصوص نانو Nano بدون حفظ التغييرات، وذلك بالضغط مباشرةً على الاختصار <code>Ctrl + X</code>، ومن ثم الضغط على المفتاح <code>N</code>.
</p>

<p>
	عند الضغط على الاختصار <code>Ctrl + O</code> لحفظ التغييرات، سيسألنا المحرر عما إذا كنا نريد حفظ تلك التغييرات باسم الملف نفسه أو باسم ملف مختلف، لذا يمكن انتقاء أحد الخيارين ثم الضغط المفتاح Enter لحفظ التغييرات.
</p>

<p>
	هناك ميزة أخرى رائعة في محرر النصوص نانو Nano، وهي الطريقة التي يخبرنا بها بالتغييرات التي لم تُحفظ بعد، حيث ستظهر علامة النجمة <code>*</code> بجانب اسم الملف كما في الشكل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172909" href="https://academy.hsoub.com/uploads/monthly_2025_05/2.png.9000d1705fecb22465df01713e9ea65d.png" rel=""><img alt="2" class="ipsImage ipsImage_thumbnailed" data-fileid="172909" data-unique="fn8b8x62t" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/2.png.9000d1705fecb22465df01713e9ea65d.png"> </a>
</p>

<p>
	بعد الانتهاء من ذلك، سنضغط على الاختصار <code>Ctrl + X</code> للخروج من محرر النصوص نانو Nano.
</p>

<p>
	إذا رغبنا في الخروج من محرر النصوص نانو nano بدون حفظ التغييرات، سنتبع الخطوات البسيطة التالية:
</p>

<ol>
	<li>
		الضغط على الاختصار <code>Ctrl + X</code>
	</li>
	<li>
		الضغط على الاختصار <code>N</code> للخروج من محرر النصوص نانو Nano بدون حفظ التغييرات
	</li>
</ol>

<h3 id="9nano">
	9. طريقة الحصول على المساعدة من محرر النصوص نانو Nano
</h3>

<p>
	تُعَد عملية المساعدة في محرر النصوص نانو nano مفيدةً جدًا في الأوقات الصعبة التي لا تعرف فيها ماذا تفعل، وكل ما علينا القيام به هو الضغط على الاختصار <code>Ctrl + G</code> وسيحضر لنا قائمةً بجميع الاختصارات المتوفرة في محرر النصوص نانو nano مع وصف بسيط لها كما في الشكل:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172910" href="https://academy.hsoub.com/uploads/monthly_2025_05/3.png.0fe24861ec1a4d559827e0fe2e79f9d9.png" rel=""><img alt="3" class="ipsImage ipsImage_thumbnailed" data-fileid="172910" data-unique="teublda9p" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/3.png.0fe24861ec1a4d559827e0fe2e79f9d9.png"> </a>
</p>

<h2 id="">
	الخلاصة
</h2>

<p>
	بهذا نكون قد تعرفنا على الأوامر الأساسية لمحرر النصوص نانو Nano. نتمنى لكم الاستفادة الجيدة من هذا المقال، وفي حال كان لديكم أي تساؤل يمكن تركه في قسم التعليقات أسفل المقال أو كتابته في <a href="https://academy.hsoub.com/questions/" rel="">قسم الأسئلة والأجوبة</a> في الأكاديمية.
</p>

<p>
	ترجمة وبتصرف للمقال <a href="https://linuxhandbook.com/nano-editor-basics/" rel="external nofollow">Nano Editor Basic Commands</a> لكاتبه Sagar Sharma.
</p>

<h2 id="-1">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A%D9%85-vim-%D9%88%D9%86%D8%A7%D9%86%D9%88-nano-r1590/" rel="">مقارنة بين محرر النصوص فيم Vim ونانو Nano</a>
	</li>
	<li>
		<a href="http://%20%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9%20%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5%20%D8%B9%D9%84%D9%89%20RedHat%20Enterprise%20Linux:%20%D8%AA%D8%AD%D8%B1%D9%8A%D8%B1%D9%87%D8%A7%20%D8%A8%D9%80%20Nano%20%D9%88%20Vim%20%D9%88%D8%A7%D9%84%D8%A8%D8%AD%D8%AB%20%D9%81%D9%8A%D9%87%D8%A7%20%D8%A8%D9%80%20grep%20" rel="external nofollow">معالجة النصوص على RedHat Enterprise Linux: تحريرها بـ Nano و Vim والبحث فيها بـ grep</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A%D9%85-vim-r1769/" rel="">تعرف على محرر النصوص فيم Vim</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">851</guid><pubDate>Thu, 28 Aug 2025 16:09:02 +0000</pubDate></item><item><title>&#x637;&#x631;&#x642; &#x627;&#x644;&#x627;&#x62A;&#x635;&#x627;&#x644; &#x628;&#x62E;&#x627;&#x62F;&#x645; MongoDB</title><link>https://academy.hsoub.com/devops/servers/databases/mongodb/%D8%B7%D8%B1%D9%82-%D8%A7%D9%84%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D8%A8%D8%AE%D8%A7%D8%AF%D9%85-mongodb-r850/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_05/---MongoDB.png.552b184eb3dbfc1104fde9194c8dccd1.png" /></p>
<p>
	سنناقش بهذا المقال الخيارات المتنوعة للاتصال بخادم MongoDB، سواءً كان يتضمن قاعدة بيانات وحيدة، أو عدة قواعد بيانات تتمتع كل منها بمَنْفَذٍ خاص. سنعرض أيضًا طريقة الاتصال مع خوادم تعمل على أجهزة متعددة ضمن أمر اتصال واحد، لكن البداية ستكون من تشغيل خادم MongoDB.
</p>

<h2 id="mongodb">
	تشغيل خادم MongoDB
</h2>

<p>
	يمكن تشغيل خادم MongoDB من الملف التنفيذي <code>mongod</code> الموجود في المجلد <code>bin</code> ضمن المجلد الخاص بملفات برنامج MongoDB، حيث بعد تنفيذ الأمر <code>mongod</code>، لن تظهر أي معلومات جديدة على شاشة موجه الأوامر لبعض الوقت حتى يتأسس الاتصال مع خادم MongoDB. وبمجرد إنشاء الاتصال، سيبدأ سجل معلومات الخادم بالظهور.
</p>

<p>
	يمكن الاتصال مع خادم MongoDB بواسطة الصدفة MongoDB Shell، وعند بدء التشغيل سيكون المنفذ 27017 هو المنفذ الافتراضي للاتصال بخادم MongoDB؛ أما إذا أردنا الوصول إلى الخادم من واجهة الويب فيمكننا ذلك باستخدام المنفذ الناتج عن إضافة 1000 إلى رقم منفذ بدء التشغيل، فنكتب عندها الرابط <code><a href="http://localhost:28017" ipsnoembed="true" rel="external nofollow">http://localhost:28017</a></code> في متصفحنا.
</p>

<h2 id="mongodbshell">
	الاتصال بخادم MongoDB من الصدفة Shell
</h2>

<p>
	يبين السطر أبسط صيغة لكتابة أمر الاتصال مع <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">خادم</a> MongoDB بعد أن شغلناه، علمًا أننا اكتفينا بخيار واحد فقط من خيارات الاتصال المتعددة، وهو خيار لا غنى عنه؛ إذ إنه يشير إلى اسم الحاسوب المضيف hostname، وهو في حالتنا <code>localhost</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4000_6" style=""><span class="pln">mongodb</span><span class="pun">:</span><span class="com">//localhost</span></pre>

<p>
	وبمجرد تنفيذ الأمر سنحصل على الخرج التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172761" href="https://academy.hsoub.com/uploads/monthly_2025_05/img01-mongodb-connect.png.431ffc56ff326794093295333ac114f8.png" rel=""><img alt="img01 mongodb connect" class="ipsImage ipsImage_thumbnailed" data-fileid="172761" data-unique="zfofppv18" src="https://academy.hsoub.com/uploads/monthly_2025_05/img01-mongodb-connect.png.431ffc56ff326794093295333ac114f8.png"> </a>
</p>

<p>
	إذا عُدنا الآن إلى نافذة موجه الأوامر التي شغلنا فيها خادم MongoDB، وهو نفسه الملف التنفيذي <code>mongod</code>، سنجد أنها تتضمن خرجًا يشبه التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172758" href="https://academy.hsoub.com/uploads/monthly_2025_05/img02-mongodb-server.png.8b214e5c69ef250613308f01745b3b08.png" rel=""><img alt="img02 mongodb server" class="ipsImage ipsImage_thumbnailed" data-fileid="172758" data-unique="w8k4954r1" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img02-mongodb-server.png.8b214e5c69ef250613308f01745b3b08.png"> </a>
</p>

<p>
	يمكننا ملاحظة السطر الأخير، فهو يشير إلى إنشاء اتصال جديد مع خادم MongoDB ويتضمن معلومات عن الجهة المتصلة.
</p>

<p>
	ملاحظة: سيظهر هذا السطر عندما ينجح اتصالنا مع الخادم
</p>

<h2 id="mongodb-1">
	صيغ متنوعة لأمر الاتصال مع خادم MongoDB
</h2>

<p>
	يمكن ذكر الصيغ الآتية لأوامر الاتصال مع خادم MongoDB
</p>

<h3 id="">
	الاتصال بقاعدة البيانات الافتراضية باستخدام اسم مستخدم وكلمة مرور
</h3>

<p>
	يمكننا الاتصال مع خادم MongoDB بواسطة اسم مستخدم وكلمة مرور محددين وفق الصيغة العامة <code>username@hostname/dbname</code>، بحيث نعدل اسم المستخدم username إلى اسم المستخدم الذي نود الاتصال بواسطته مع كلمة المرور الخاصة به، كما نبدل اسم الحاسوب المضيف hostname إلى اسم الحاسوب المضيف الفعلي، و dbname إلى اسم قاعدة البيانات؛ وإذا أردنا الاتصال بقاعدة البيانات الافتراضية، يكفي أن نترك اسم <a href="https://academy.hsoub.com/devops/servers/databases/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-database/" rel="">قاعدة البيانات</a> فارغ، كما هو الحال في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4000_8" style=""><span class="pln">mongodb</span><span class="pun">:</span><span class="com">//mongo_admin:AxB6_w3r@localhost/</span></pre>

<p>
	ولتوضيح الشيفرة أعلاه، يشير mongo_admin إلى اسم المستخدم، و AxB6_w3r إلى كلمة المرور الخاصة به؛ وهو يحاول الاتصال بقاعدة البيانات الافتراضية الموجودة على الحاسوب المحلي <code>localhost</code>.
</p>

<p>
	وسيكون الخرج كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172760" href="https://academy.hsoub.com/uploads/monthly_2025_05/img03-mongodb-connect-to-default-database.png.57a75cc325e5e98392e48f1a00af5c99.png" rel=""><img alt="img03 mongodb connect to default database" class="ipsImage ipsImage_thumbnailed" data-fileid="172760" data-unique="yx1fj9ujc" src="https://academy.hsoub.com/uploads/monthly_2025_05/img03-mongodb-connect-to-default-database.png.57a75cc325e5e98392e48f1a00af5c99.png"> </a>
</p>

<h3 id="-1">
	الاتصال بقاعدة بيانات معينة باستخدام اسم مستخدم وكلمة مرور
</h3>

<p>
	سنستخدم الصيغة العامة السابقة نفسها <code>username@hostname/dbname</code> مع تعديل كلمة dbname إلى اسم قاعدة البيانات التي نريد بالاتصال بها، وفق التالي:
</p>

<pre class="ipsCode">mongodb://mongo_admin:AxB6_w3r@localhost/w3r
</pre>

<p>
	وباستخدام هذا الأمر سيتصل المستخدم mongo_admin صاحب كلمة المرور AxB6_w3r بقاعدة البيانات w3r الموجودة على الحاسوب المحلي، وسيكون الخرج كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172762" href="https://academy.hsoub.com/uploads/monthly_2025_05/img04-mongodb-connect-with-username-and-password.png.5918bde7294820fb8bdbf5b1dab535fc.png" rel=""><img alt="img04 mongodb connect with username and password" class="ipsImage ipsImage_thumbnailed" data-fileid="172762" data-unique="mgsx8eobj" style="width: 354px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img04-mongodb-connect-with-username-and-password.png.5918bde7294820fb8bdbf5b1dab535fc.png"> </a>
</p>

<p>
	وتجدر الإشارة هنا إلى أننا تستطيع الاتصال بأكثر من مُضيف hostname ضمن أمر اتصال واحد.
</p>

<h3 id="-2">
	الاتصال بقاعدة بيانات معينة باستخدام اسم مستخدم وكلمة مرور ومَنفَذ معين
</h3>

<p>
	يجري هذا الاتصال بطريقة مشابهة للسابق، بحيث نستخدم الصيغة <code>username@hostname/dbname</code> ونضع اسم المستخدم وكلمة المرور؛ وبدلًا من dbname، نكتب اسم قاعدة البيانات التي نريد الاتصال بها، يتبعها رقم المَنفَذ port الذي حددناه للاتصال معها، كما هو الحال في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4000_11" style=""><span class="pln">mongodb</span><span class="pun">:</span><span class="com">//mongo_admin:AxB6_w3r@localhost/w3r:29000</span></pre>

<p>
	بمعنى أن المستخدم mongo_admin الذي كلمة مروره AxB6_w3r سيحاول الاتصال بقاعدة البيانات w3r الموجودة على الحاسوب المحلي، وذلك عبر المنفذ 29000. وفي الحالات التي لا نذكر فيها رقم منفذ محدد، سيجري الاتصال عبر المنفذ الافتراضي 27107، بحيث سيكون خرج الأمر كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172759" href="https://academy.hsoub.com/uploads/monthly_2025_05/img05-mongodb-connect-to-database-specific-port.png.bccd98a682d6d8e6b27219cb7c2d9bbb.png" rel=""><img alt="img05 mongodb connect to database specific port" class="ipsImage ipsImage_thumbnailed" data-fileid="172759" data-unique="uom7vmrot" src="https://academy.hsoub.com/uploads/monthly_2025_05/img05-mongodb-connect-to-database-specific-port.png.bccd98a682d6d8e6b27219cb7c2d9bbb.png"> </a>
</p>

<h3 id="mongodb-2">
	الاتصال بخوادم MongoDB متعددة تعمل على أجهزة متعددة
</h3>

<p>
	تعمل حالة الاتصال بخوادم MongoDB على أجهزة متعددة عند استخدام مجموعات النسخ المتماثلة replica sets، التي تتألف من عدة عقد تُعدّ نسخًا متطابقة عن بعضها؛ إذ تكتشف المجموعة العقدة الرئيسية Master node تلقائيًا وفي حال فشلت، فإن العقد الثانوية تنوب عنها، وتساعدنا على استرداد النظام من حالات الفشل.
</p>

<p>
	في المثال التالي صيغة الأمر المناسب لهذا النوع من الاتصال:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4000_13" style=""><span class="pln">mongodb</span><span class="pun">:</span><span class="com">//example_host1.com:27017,example_host2.com:27017</span></pre>

<h3 id="mongodb-3">
	الاتصال بخوادم MongoDB متعددة تعمل على الجهاز نفسه ولكل منها منفذ خاص
</h3>

<p>
	سنستخدم الأمر التالي للاتصال بخوادم MongoDB المتعددة التي تعمل على الجهاز نفسه، لكن على منافذ ports مختلفة. سيفيدنا هذا أيضًا في مجموعات النسخ المتماثلة replica sets:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4000_15" style=""><span class="pln">mongodb</span><span class="pun">:</span><span class="com">//example_host1.com:27110,example_host1.com:27111</span></pre>

<h3 id="-3">
	الخيارات المرتبطة بالصيغ السابقة
</h3>

<p>
	يبين الجدول التالي الخيارات التي يمكننا استخدامها مع صيغ أوامر الاتصال السابقة:
</p>

<table>
	<thead>
		<tr>
			<th>
				الخيار Option
			</th>
			<th>
				الوصف
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<code>replicaSet=name</code>
			</td>
			<td>
				عند العمل مع أي لغة برمجة يدعمها MongoDB فإن مُشَغِّل driver لهذه اللغة سيستخدم اسم مجموعة النسخ المتماثلة ليعرف العقد التي تنتمي إليها؛ علمًا أن مُشَغِّل لغة البرمجة أو driver هو جزء التعليمات البرمجية المسؤول عن الاتصال بين لغة برمجة التطبيق ونظام MongoDB.
			</td>
		</tr>
		<tr>
			<td>
				<code>slaveOk=true|false‎</code>
			</td>
			<td>
				يستخدم هذا الخيار للتعامل مع حالتي الكتابة والقراءة في أنظمة النسخ المتماثلة ذات الخوادم المتعددة، فعند الكتابة تُرسل البيانات إلى الخادم الرئيسي primary وعند القراءة تُرسل البيانات إلى جميع الخوادم الاحتياطية أو الثانوية slaves.
			</td>
		</tr>
		<tr>
			<td>
				<code>safe=true|false</code>
			</td>
			<td>
				إذا كانت قيمة <code>safe</code> هي <code>true</code>، فإن مُشَغِّل لغة البرمجة سيُرسل الأمر <code>getLastError</code> بعد تحديث للبيانات ليتأكد من إتمامه بدون أخطاء؛ أما إذا كانت قيمته <code>false</code>، فلن يُرسل المشغل <code>getLastError</code> بعد التحديثات.
			</td>
		</tr>
		<tr>
			<td>
				<code>w=n</code>
			</td>
			<td>
				يُضاف <code>{ w : n }</code> إلى الأمر <code>getLastError</code> الذي يرسله مُشَغِّل لغة البرمجة، وهو يفرض بطبيعة الحال إسناد القيمة <code>true</code> للخيار <code>safe=true</code>.
			</td>
		</tr>
		<tr>
			<td>
				<code>wtimeoutMS=ms</code>
			</td>
			<td>
				يُضاف <code>{ wtimeout : ms }</code> إلى الأمر <code>getLastError</code> الذي يرسله مُشَغِّل لغة البرمجة، وهو يعني حكمًا أن <code>safe=true</code>.
			</td>
		</tr>
		<tr>
			<td>
				<code>fsync=true|false</code>
			</td>
			<td>
				إذا كانت قيمة هذا الخيار هي <code>true</code>، فسيُضاف الخيار <code>{ wtimeout : ms }</code> إلى الأمر <code>getLastError</code> الذي يُرسله مُشَغِّل لغة البرمجة، وهذا يفرض بالطبع أن تكون <code>safe=true</code>؛ أما إذا أعطينا <code>fsync</code> القيمة <code>false</code>، فإن الأمر <code>getLastError</code> لن يُرسل أبدًا.
			</td>
		</tr>
		<tr>
			<td>
				<code>journal=true|false</code>
			</td>
			<td>
				إذا أُعطيت القيمة <code>true</code> لهذا الخيار فستجري مزامنة مع يومية قاعدة البيانات journal التي تتضمن عادةً إدخالات قاعدة البيانات التي لم تُثَبَّت بعد uncommitted، وهذه الحالة تفرض دائمًا إسناد القيمة <code>true</code> للخيار <code>safe=true</code>.
			</td>
		</tr>
		<tr>
			<td>
				<code>connectTimeoutMS=ms</code>
			</td>
			<td>
				يُحدد الزمن الأقصى لمحاولة الاتصال مع خادم MongoDB قبل تصنيفه على أنه اتصال غير ممكن.
			</td>
		</tr>
		<tr>
			<td>
				<code>socketTimeoutMS=ms</code>
			</td>
			<td>
				يحدد الزمن الأقصى لمحاولة الإرسال أو الاستقبال على مِقبس التوصيل socket قبل إنهاء المحاولة.
			</td>
		</tr>
	</tbody>
</table>

<h2 id="-4">
	خاتمة
</h2>

<p>
	تعرفنا في هذا المقال على الطرق المتنوعة للاتصال مع خادم MongoDB عبر الصدفة mongo shell. وسواءٌ كان ذلك على مُضيف واحد أو على مضيفين متعددين، يرتكز الاتصال على تحديد أربع أمور أساسية هي بيانات المستخدم الذي سيُنجِز الاتصال، واسم قاعدة البيانات، والمَنْفَذ الخاص بها، واسم المُضيف؛ ويمكننا استخدام الخيارات التي عرضناها في الجدول حسب ما تقتضيه طبيعة مشروعنا.
</p>

<p>
	ترجمة -وبتصرف- لمقال <a href="https://www.w3resource.com/mongodb/connections.php" rel="external nofollow">MongoDB connections</a> من موقع w3resource.
</p>

<h2 id="-5">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mongodb/%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mongodb-%D8%B9%D9%84%D9%89-%D9%88%D9%8A%D9%86%D8%AF%D9%88%D8%B2-%D9%88%D9%84%D9%8A%D9%86%D9%83%D8%B3-r849/" rel="">طريقة تثبيت MongoDB على ويندوز ولينكس</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mongodb/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D9%88%D8%AA%D8%A3%D9%85%D9%8A%D9%86-mongodb-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r435/" rel="">كيفية تثبيت وتأمين MongoDB على أوبونتو 18.04</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mongodb/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%8A%D8%A7%D8%B7%D9%8A-%D9%84%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mongodb-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-r276/" rel="">كيفية إنشاء واستخدام النسخ الاحتياطي لقواعد بيانات MongoDB على نظام أوبنتو</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-database/" rel="">دليلك الشامل إلى قواعد البيانات DataBase</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">850</guid><pubDate>Fri, 15 Aug 2025 16:06:02 +0000</pubDate></item><item><title>&#x637;&#x631;&#x64A;&#x642;&#x629; &#x62A;&#x62B;&#x628;&#x64A;&#x62A; MongoDB &#x639;&#x644;&#x649; &#x648;&#x64A;&#x646;&#x62F;&#x648;&#x632; &#x648;&#x644;&#x64A;&#x646;&#x643;&#x633;</title><link>https://academy.hsoub.com/devops/servers/databases/mongodb/%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-mongodb-%D8%B9%D9%84%D9%89-%D9%88%D9%8A%D9%86%D8%AF%D9%88%D8%B2-%D9%88%D9%84%D9%8A%D9%86%D9%83%D8%B3-r849/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_05/-MongoDB---.png.fca1ff49337653133d51d4230891068b.png" /></p>
<p>
	سنعرض في هذا المقال طريقة تثبيت MongoDB على نظامي تشغيل ويندوز ولينكس، مع تمهيد بسيط لبدء العمل مع الصدفة mongo shell التي تمكننا من التفاعل مع قاعدة البيانات.
</p>

<blockquote class="ipsQuote" data-gramm="false" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		<strong>ملاحظة</strong>: تنطبق الخطوات الواردة هنا على نظامي التشغيل ويندوز ولينكس ما لم يُذكر خلاف ذلك وحتى عندما تكون الصور التوضيحية من أحد النظامين فقط.
	</p>
</blockquote>

<h2 id="download">
	التحميل Download
</h2>

<p>
	يمكن تحميل ذ الثنائية المُعدّة مسبقًا Pre-built binary packages مباشرةً من <a href="https://www.mongodb.com/download-center" rel="external nofollow">موقعها الرسمي</a>، لذا ما علينا سوى اختيار النسخة المناسبة ورقم الإصدار، ونظام التشغيل ويندوز أم <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-linux-%D9%88%D8%A3%D8%A8%D8%B1%D8%B2-%D9%85%D9%85%D9%8A%D8%B2%D8%A7%D8%AA%D9%87-%D9%88%D8%B9%D9%8A%D9%88%D8%A8%D9%87-r2252/" rel="">لينكس</a>، ونوعه 32 بت أم 64 بت؛ وعند انتهاء التحميل سيكون من السهل تثبيتها كما سنعرض في الفقرات التالية.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172754" href="https://academy.hsoub.com/uploads/monthly_2025_05/img01-mongodb-download.png.9eee2c622034f6323fa0b54f44d4d9d4.png" rel=""><img alt="img01 mongodb download" class="ipsImage ipsImage_thumbnailed" data-fileid="172754" data-unique="utwr0x8w7" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img01-mongodb-download.thumb.png.999ce3ccd43b071005988c831baade1e.png"> </a>
</p>

<h2 id="unzip">
	فك الضغط Unzip
</h2>

<p>
	الخطوة التالية بعد تحميل حزمة MongoDB هي فك ضغطها إلى المجلد المراد تثبيت MongoDB ضمنه؛ وهنا سننشئ مجلدًا خاصًا لهذا الغرض، وسيكون اسمه في مثالنا هو <code>mongodb</code>.
</p>

<h2 id="mongodb">
	إنشاء مجلد بيانات MongoDB
</h2>

<p>
	سننشئ أيضًا مجلدًا خاصًا بالبيانات نسميه <code>data</code>، ثم ننشئ بداخله مجلدًا آخر يسمى <code>db</code>، وهذا هو الاسم الافتراضي للمجلد الذي تخزن فيه MongoDB البيانات، ولأن هذا المجلد لا ينشأ تلقائيًا عند تثبيت MongoDB، فقد أنشأناه يدويًا.
</p>

<p>
	ينبغي إنشاء المجلد <code>data</code> على الجذر مباشرةً، أي على (<code>\:C</code> أو <code>\:D</code> أو نحو ذلك) في ويندوز، وعلى (<code>/</code>) في لينكس، كما يلي:
</p>

<p>
	إنشاء المجلدات في ويندوز (أنشأنا المجلدات على القرص <code>\:D</code> في مثالنا):
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172752" href="https://academy.hsoub.com/uploads/monthly_2025_05/img02-mongodb-installation-windows.png.7d5ee4326eed5fb557bb2f58aafe15f5.png" rel=""><img alt="img02 mongodb installation windows" class="ipsImage ipsImage_thumbnailed" data-fileid="172752" data-unique="xecx4yhiq" src="https://academy.hsoub.com/uploads/monthly_2025_05/img02-mongodb-installation-windows.png.7d5ee4326eed5fb557bb2f58aafe15f5.png"> </a>
</p>

<p>
	إنشاء المجلدات في لينكس:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172755" href="https://academy.hsoub.com/uploads/monthly_2025_05/img03-mongodb-installation-Linux.png.15aef0ae2aa0f1392ebf1dae34fab11a.png" rel=""><img alt="img03 mongodb installation linux" class="ipsImage ipsImage_thumbnailed" data-fileid="172755" data-unique="uelp20tjd" src="https://academy.hsoub.com/uploads/monthly_2025_05/img03-mongodb-installation-Linux.png.15aef0ae2aa0f1392ebf1dae34fab11a.png"> </a>
</p>

<p>
	يمكن إنشاء المجلدات في كلا النظامين من الواجهة الرسومية ومستعرض الملفات في حال الرغبة بذلك من دون الحاجة لموجه الأوامر.
</p>

<h2 id="mongodb-1">
	تشغيل خادم MongoDB من موجه الأوامر
</h2>

<p>
	لتشغيل خادم MongoDB من موجه الأوامر، لا بد من فتح المجلد <code>mongodb</code> الذي أجرينا فيه عملية فك الضغط سابقًا، ثم نفتح المجلد الفرعي ضمنه <code>bin</code>، ونشَغِّل الملف التنفيذي <code>mongod.exe</code>.
</p>

<h2 id="mongodb-2">
	تشغيل خادم MongoDB كخدمة ويندوز
</h2>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<blockquote class="ipsQuote" data-gramm="false" data-ipsquote="">
		<div class="ipsQuote_citation">
			اقتباس
		</div>

		<p>
			هذه الفقرة خاصة بنظام ويندوز فقط.
		</p>
	</blockquote>
</blockquote>

<p>
	سنحتاج لتنفيذ الأمر التالي بصلاحيات مدير نظام administrator لتشغيل خادم MongoDB كخدمة ويندوز:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5504_7" style=""><span class="pln">mongod </span><span class="pun">--</span><span class="pln">bind_ip  yourIPadress  </span><span class="pun">--</span><span class="pln">logpath  </span><span class="str">"C:\data\dbConf\mongodb.log"</span><span class="pln">  </span><span class="pun">--</span><span class="pln">logappend  </span><span class="pun">--</span><span class="pln">dbpath  </span><span class="str">"C:\data\db"</span><span class="pln">  </span><span class="pun">--</span><span class="pln">port yourPortNumber </span><span class="pun">--</span><span class="pln">serviceName </span><span class="str">"YourServiceName"</span><span class="pln"> </span><span class="pun">--</span><span class="pln">serviceDisplayName </span><span class="str">"YourServiceName"</span><span class="pln"> </span><span class="pun">--</span><span class="pln">install</span></pre>

<p>
	لا ننسَ ضبط قيم الوسطاء Arguments ضمن الأمر بما يناسب حالتنا كما هو موضح في الجدول التالي:
</p>

<table>
	<thead>
		<tr>
			<th>
				الوسيط Argument
			</th>
			<th>
				الوصف
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				‎--bind_ip
			</td>
			<td>
				يُكتب بعده عنوان IP الخاص بالحاسوب الذي نستخدمه
			</td>
		</tr>
		<tr>
			<td>
				‎--logpath
			</td>
			<td>
				يليه مسار تخزين ملف تسجيل الأحداث logfile لخادم MongoDB
			</td>
		</tr>
		<tr>
			<td>
				‎--logappend
			</td>
			<td>
				يُفَعِّل هذا الوسيط كتابة سجلات الأحداث logs التي تتولد ضمن ملف تسجيل الأحداث logfile الذي حددناه بالوسيط السابق
			</td>
		</tr>
		<tr>
			<td>
				‎--dbpath
			</td>
			<td>
				يُحَدَّدْ بعده مسار وجود المجلد <code>db</code> ضمن المجلد <code>data</code>
			</td>
		</tr>
		<tr>
			<td>
				‎--port
			</td>
			<td>
				يُكتب بعده المنفذ port الذي خصصناه لخادم MongoDB، وإذا لم نضع أي قيمة هنا، فإن MongoDB سيستخدم المنفذ الافتراضي وهو 20127
			</td>
		</tr>
		<tr>
			<td>
				‎--serviceName
			</td>
			<td>
				يوضع بعده اسم خدمة MongoDB الذي اخترناه
			</td>
		</tr>
		<tr>
			<td>
				‎--serviceDisplayName
			</td>
			<td>
				يُكتب بعده اسم عرض الخدمة أو اسم الإظهار، فلكل خدمة ويندوز اسمين، ويكون هذا الاسم عادةً أوضح ويعبر أكثر عن طبيعة الخدمة. تتضح فائدة هذا الوسيط عندما يكون لدينا أكثر من خدمة MongoDB مُشغلة على نفس الحاسب
			</td>
		</tr>
		<tr>
			<td>
				install--
			</td>
			<td>
				يشير هذا الوسيط إلى أمر تثبيت أو إنشاء MongoDB كخدمة من خدمات ويندوز
			</td>
		</tr>
	</tbody>
</table>

<h2 id="shellmongodb">
	تمهيد لاستخدام الصدفة Shell الخاصة بنظام MongoDB
</h2>

<p>
	لا بد لنا من الحرص على العمل من مستخدم يملك صلاحيات مدير نظام، ثم نشَغَّل الملف التنفيذي <code>mongo.exe</code> الموجود ضمن المجلد <code>bin</code>، أحد المجلدات الفرعية للمجلد الذي ثَبَّتنا فيه MongoDB، وبمجرد تشغيله ستفتح أمامنا الصدفة shell الخاصة بإدارة نظام MongoDB، وهي من نوع JavaScript shell، كما في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172756" href="https://academy.hsoub.com/uploads/monthly_2025_05/img04-run-mongo-shell.png.94bfd97f59d74225df93bca930ed0084.png" rel=""><img alt="img04 run mongo shell" class="ipsImage ipsImage_thumbnailed" data-fileid="172756" data-unique="5js4b2ldo" src="https://academy.hsoub.com/uploads/monthly_2025_05/img04-run-mongo-shell.png.94bfd97f59d74225df93bca930ed0084.png"> </a>
</p>

<p>
	يمكننا ملاحظة أن MongoDB قد اتصل مباشرةً بقاعدة بيانات مستندية document database تجريبية تدعى test.
</p>

<p>
	يوفر MongoDB لمستخدميه إمكانية الاتصال المباشر بهذا المستند التجريبي أو قاعدة البيانات التجريبية بعد التثبيت.
</p>

<p>
	سنجري الآن بعض العمليات البسيطة لاختبار الصدفة shell، ونكتب مثلًا عمليةً حسابيةً طالما أن الصدفة مكتوبة <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1689/" rel="">بلغة JavaScript</a>، ويمكن أيضًا استخدام الأمر <code>db</code> فهو يعطينا اسم قاعدة البيانات المتصل بها:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172753" href="https://academy.hsoub.com/uploads/monthly_2025_05/img05-mongo-shell-exampel.png.abad8f290af60be8bf3dadc14aed15a5.png" rel=""><img alt="img05 mongo shell exampel" class="ipsImage ipsImage_thumbnailed" data-fileid="172753" data-unique="m26152bxp" src="https://academy.hsoub.com/uploads/monthly_2025_05/img05-mongo-shell-exampel.png.abad8f290af60be8bf3dadc14aed15a5.png"> </a>
</p>

<p>
	لنجرب الآن إدخال سجل بسيط وإرجاع بعض المعلومات منه:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172757" href="https://academy.hsoub.com/uploads/monthly_2025_05/img06-mongo-insert-find.png.faa0c79a13a1ae4fddf4506f56622b96.png" rel=""><img alt="img06 mongo insert find" class="ipsImage ipsImage_thumbnailed" data-fileid="172757" data-unique="nwhnnkeso" style="width: 374px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img06-mongo-insert-find.png.faa0c79a13a1ae4fddf4506f56622b96.png"> </a>
</p>

<p>
	أدخلنا في السطر الأول القيمة 8 إلى الحقل z التابع للمجموعة w3r، ثم استعلمنا عن البيانات؛ علمًا أن المجموعات collections في MongoDB تكافئ الجداول في <a href="https://academy.hsoub.com/programming/sql/%D9%81%D9%87%D9%85-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D9%8A%D8%A9-r2216/" rel="">قواعد البيانات العلائقية</a>.
</p>

<h2 id="mongodb-3">
	واجهة الويب لنظام MongoDB
</h2>

<p>
	يمكن الوصول إلى واجهة الويب الخاصة بإدارة MongoDB ومراقبته عبر منفذٍ port خاص، يكون رقمه هو نفسه رقم المنفذ المخصص لخادم MongoDB مضافًا إليه 1000.
</p>

<p>
	فعلى سبيل المثال، إذا كنا نستخدم المنفذ الافتراضي لخادم MongoDB وهو 27017 فمعنى ذلك أنه يمكن الوصول إلى واجهة الويب من المنفذ 28017.
</p>

<h2 id="">
	الخاتمة
</h2>

<p>
	اطلعنا في هذا المقال على خطوات تثبيت MongoDB على نظامي تشغيل ويندوز ولينكس وهي خطوات بسيطة ومتشابهة، ثم تعرفنا على أمر تشغيل خادم MongoDB كخدمة ويندوز وطريقة تعديله ليناسب بيئتك، ووضحنا آلية التعامل مع صفة MongoDB.
</p>

<p>
	ترجمة -وبتصرف- لمقال <a href="https://www.w3resource.com/mongodb/installation-windows.php" rel="external nofollow">Install MongoDB on Windows</a> ومقال <a href="https://www.w3resource.com/mongodb/installation-Linux.php" rel="external nofollow">Install MongoDB on Linux</a> من موقع w3resource.
</p>

<h2 id="-1">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mongodb/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D9%88%D8%AA%D8%A3%D9%85%D9%8A%D9%86-mongodb-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r435/" rel="">كيفية تثبيت وتأمين MongoDB على أوبونتو 18.04</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mongodb/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%8A%D8%A7%D8%B7%D9%8A-%D9%84%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mongodb-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-r276/" rel="">كيفية إنشاء واستخدام النسخ الاحتياطي لقواعد بيانات MongoDB على نظام أوبنتو</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-database/" rel="">دليلك الشامل إلى قواعد البيانات DataBase</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">849</guid><pubDate>Wed, 23 Jul 2025 16:06:01 +0000</pubDate></item><item><title>&#x645;&#x642;&#x627;&#x631;&#x646;&#x629; &#x628;&#x64A;&#x646; MariaDB &#x648; MySQL</title><link>https://academy.hsoub.com/devops/servers/databases/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-mariadb-%D9%88-mysql-r848/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_05/10037215.---MariaDB--MySQL-------.png.1727e3ababe7e6995141ed90dc60fdc1.png" /></p>
<p>
	قد يكون اختيار قاعدة البيانات المناسبة للمشروع أمرًا صعبًا، خاصةً مع وجود العديد من الخيارات.
</p>

<p>
	تقدم قاعدتا بيانات MariaDB و MySQL ميزات كثيرة، ولكن يمكن أن تؤثر نقاط القوة والضعف لكل منهما على الأداء وقابلية التوسع والتوافق مع التطبيقات المختلفة مثل WordPress، لذا سنوضح في هذا المقال الاختلافات الأساسية بين قاعدتي البيانات MariaDB و MySQL، وسنتحدث عن ميزاتهما ومدى سرعتهما وأمانهما وكيفية استخدامهما.
</p>

<h2 id="mariadbmysql">
	نظرة عامة على MariaDB و MySQL
</h2>

<p>
	نشأت كل من MariaDB <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D8%AA%D8%B9%D9%84%D9%85-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-mysql-r297/" rel="">وMySQL</a> من الجذور نفسها، إذ يعود أصلهما إلى نظام قاعدة بيانات إنجرس Ingres الذي طوّرته جامعة كاليفورنيا في بيركلي.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172725" href="https://academy.hsoub.com/uploads/monthly_2025_05/001_mysql.png.91ad6265b3b87b9f97ffafd8e707c94d.png" rel=""><img alt="001 mysql" class="ipsImage ipsImage_thumbnailed" data-fileid="172725" data-unique="fc0m8txik" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/001_mysql.thumb.png.92d3db649010f05310cfd880c0db119f.png"> </a>
</p>

<p>
	طُوِّرت <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a> لأول مرة في عام 1995 واكتسبت شعبيةً كبيرةً لأنها كانت سهلة الاستخدام وسريعة، ولكن أصبح هناك خلاف بين مالكي MySQL وأوراكل Oracle لاحقًا، لذا جرى تطوير قاعدة بيانات جديدة بالاسم MariaDB.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172726" href="https://academy.hsoub.com/uploads/monthly_2025_05/002_mariadb.png.76ca8c91307f524c84a08767be883ee4.png" rel=""><img alt="002 mariadb" class="ipsImage ipsImage_thumbnailed" data-fileid="172726" data-unique="9wa8yhljl" src="https://academy.hsoub.com/uploads/monthly_2025_05/002_mariadb.png.76ca8c91307f524c84a08767be883ee4.png"> </a>
</p>

<p>
	تم تطوير قاعدةَ بيانات <a href="https://mariadb.org/" rel="external nofollow">MariaDB</a> من طرف نفس مطوري MySQL في عام 2009، وكان الهدف هو الحفاظ على ميزات MySQL نفسها مع إضافة ميزات جديدة أيضًا.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172723" href="https://academy.hsoub.com/uploads/monthly_2025_05/003____DBMS.png.49f59d617d64d113ddd2f20c51cc5fd7.png" rel=""><img alt="003_الحصة_السوقية_لأنظمة_DBMS.png" class="ipsImage ipsImage_thumbnailed" data-fileid="172723" data-ratio="48.33" data-unique="1q2ie65kv" style="width: 700px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_05/003____DBMS.thumb.png.4723a8dd8b5d3ce61ade5c3dcf422352.png"></a>
</p>

<p>
	تنمو الآن MariaDB و MySQL كل على حدة، ولكل منهما ميزاتها وفوائدها الرئيسية.
</p>

<h2 id="">
	الميزات والفوائد الرئيسية
</h2>

<p>
	تتشابه MariaDB و MySQL في الكثير من الأمور، لأنهما نشأتا من قاعدة الشيفرة البرمجية نفسها، ولكنهما تطورتا بطريقة مستقلة، مما أدى إلى بعض الاختلافات في ميزاتهما وفوائدهما.
</p>

<h3 id="mariadb">
	ميزات وفوائد MariaDB
</h3>

<p>
	تشتهر MariaDB بكونها سريعة وقادرة على التعامل مع عدد كبير من العمليات، ولديها ميزات خاصة تسرّع من عمل الاستعلامات وتجعلها تعمل بطريقة أفضل مع حركة المرور الكبيرة.
</p>

<p>
	تحتوي MariaDB أيضًا على طرق جديدة لتخزين البيانات، والتي يمكن أن تساعدها على التصرّف بطريقة أفضل في مواقف معينة.
</p>

<p>
	تتضمن بعض ميزات MariaDB الميزة MCS لتحليل البيانات وميزة MaxScale للمحافظة على سير الأمور وموازنة العمل وميزة Galera Cluster لنسخ البيانات بدقة.
</p>

<h3 id="mysql">
	ميزات وفوائد MySQL
</h3>

<p>
	تحسّنت <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D8%AA%D8%B9%D9%84%D9%85-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-mysql-r297/" rel="">قاعدة بيانات MySQL</a> بمرور الوقت، ولا تزال تحظى بشعبية كبيرة لأن العديد من الأشخاص يستخدمونها مع إتاحة المساعدة؛ وتحتوي على طرق مختلفة لتخزين البيانات مثل InnoDB و MyISAM ولكل منها إيجابياتها وسلبياتها.
</p>

<p>
	تحتوي MySQL على ميزات مثل البحث عن الكلمات الكاملة والمنبّهات Trigger والإجراءات المُخزَّنة، والتي يمكن أن تكون مفيدةً لإنشاء تطبيقات قواعد بيانات معقدة.
</p>

<h2 id="mariadbmysql-1">
	مقارنة بين MariaDB و MySQL
</h2>

<p>
	هناك العديد من الاختلافات الرئيسية بين MySQL و MariaDB بالرغم من وجود بنية ووظائف متشابهة كما هو موضح في الجدول التالي:
</p>

<table>
	<thead>
		<tr>
			<th>
				الميزة
			</th>
			<th>
				قاعدة بيانات MariaDB
			</th>
			<th>
				قاعدة بيانات MySQL
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				محركات التخزين
			</td>
			<td>
				محركات InnoDB و Aria و MyISAM و TokuDB و XtraDB و MariaDB Column Store
			</td>
			<td>
				محرّكات InnoDB و MyISAM و Aria و NDB و TokuDB
			</td>
		</tr>
		<tr>
			<td>
				تحسين السرعة
			</td>
			<td>
				تحسين تنفيذ الاستعلامات وفهرسة أسرع وتحسين أداء مع مجموعات البيانات الكبيرة
			</td>
			<td>
				تحسينات مستمرة في الأداء والتركيز على السرعة
			</td>
		</tr>
		<tr>
			<td>
				اتصالات أكبر وأسرع
			</td>
			<td>
				تدعم المزيد من الاتصالات في وقت واحد وتحسّن الأداء عند الحِمل الكبير
			</td>
			<td>
				تحسين التعامل مع الاتصالات وتحسينات قابلية التوسع
			</td>
		</tr>
		<tr>
			<td>
				تحسين النسخ المتماثل Replication
			</td>
			<td>
				تقنية Galera Cluster للتوافرية العالية والنسخ المتماثل المتزامن ونسخ المجموعات المتماثل
			</td>
			<td>
				النسخ المتماثل غير المتزامن والنسخ المتماثل شبه المتزامن ونسخ المجموعات المتماثل
			</td>
		</tr>
		<tr>
			<td>
				الميزات أو الإضافات الجديدة
			</td>
			<td>
				ميزات وإضافات JSON وأنواع البيانات المكانية Spatial ودوال النافذة و MariaDB Serverless و MariaDB MaxScale
			</td>
			<td>
				ميزات وإضافات JSON وأنواع البيانات المكانية ودوال النافذة و MySQL Shell
			</td>
		</tr>
		<tr>
			<td>
				نموذج قاعدة البيانات الثانوية
			</td>
			<td>
				MariaDB Column Store لأحمال العمل التحليلي
			</td>
			<td>
				MySQL Enterprise Backup للاستعادة في الحالات الكارثية وتخزين البيانات
			</td>
		</tr>
		<tr>
			<td>
				تقنيع البيانات Data Masking
			</td>
			<td>
				إمكانات تقنيع البيانات المُضمَّنة لأمن البيانات والالتزام بها
			</td>
			<td>
				ميزات تقنيع البيانات المتاحة باستخدام إضافات خارجية
			</td>
		</tr>
		<tr>
			<td>
				الأعمدة الديناميكية
			</td>
			<td>
				MariaDB Column Store للتخزين العمودي الديناميكي
			</td>
			<td>
				دعم محدود للأعمدة الديناميكية
			</td>
		</tr>
		<tr>
			<td>
				المراقبة Monitoring
			</td>
			<td>
				أدوات مراقبة متقدمة مدمجة بما في ذلك مخطط الأداء Performance Schema وإضافات المراقبة
			</td>
			<td>
				ميزات المراقبة الأساسية مع الأدوات الإضافية المتوفرة في Enterprise Edition
			</td>
		</tr>
		<tr>
			<td>
				التوجيه Routing
			</td>
			<td>
				إمكانات التوجيه المُضمَّنة مع MaxScale لتحسين معالجة الاستعلامات
			</td>
			<td>
				إمكانات التوجيه المحدودة باستخدام أدوات خارجية أولًا
			</td>
		</tr>
		<tr>
			<td>
				التحليلات Analytics
			</td>
			<td>
				ميزات تحليلية متقدمة مع دعم للاستعلامات وأنواع البيانات المعقدة
			</td>
			<td>
				دعم التحليلات الأساسية مع ميزات أكثر تقدمًا في MySQL Enterprise Edition
			</td>
		</tr>
		<tr>
			<td>
				نجوم GitHub
			</td>
			<td>
				أكثر من 11000 نجمة
			</td>
			<td>
				أكثر من 17000 نجمة
			</td>
		</tr>
		<tr>
			<td>
				عمليات نسخ GitHub
			</td>
			<td>
				أكثر من 2500 عملية نسخ
			</td>
			<td>
				أكثر من 3000 عملية نسخ
			</td>
		</tr>
	</tbody>
</table>

<h3 id="-1">
	المقارنة بين معايير قياس الأداء
</h3>

<p>
	قد نجد صعوبةً في توفير معايير قياس أداء نهائية تنطبق على جميع السيناريوهات، ولكن أظهرت دراسات مختلفة وتجارب واقعية أن كلًا من MariaDB و MySQL يمكنهما تقديم أداء ممتاز في مجموعة متنوعة من حالات الاستخدام.
</p>

<p>
	يوضح الرسم البياني التالي عدد العمليات التي يمكن إجراؤها في الثانية OPS لأربعة إصدارات مختلفة من قواعد البيانات (MariaDB 10.0.21 و MariaDB 10.0.18 و MySQL 5.6.27 و MySQL 5.7.9) عند إجراء عمليات قراءة بسيطة مع أعداد مختلفة من المستخدمين من 1 إلى 256. يُظهِر الرسم البياني التالي أن الإصدار MySQL 5.7.9 أفضل من الإصدارات الأخرى دائمًا، مما يعني أن الإصدارات الأحدث أفضل.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172729" href="https://academy.hsoub.com/uploads/monthly_2025_05/1003721004mariadboltp.png.50d2fde7be94543dc6948c60a7e88890.png" rel=""><img alt="1003721004mariadboltp.png" class="ipsImage ipsImage_thumbnailed" data-fileid="172729" data-ratio="44.00" data-unique="mxm4axh5m" style="width: 700px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_05/1003721004mariadboltp.thumb.png.779c298399eaf5f0d8b6d77d26eeb8fe.png"></a>
</p>

<p>
	ترتفع الأرقام مع زيادة عدد المستخدمين، ولكنها تتوقف عن الارتفاع عند حوالي 128 مستخدمًا لمعظم الإصدارات، وهذا يعني أن هناك حدًا لكمية العمليات التي يمكن لقاعدة البيانات التعامل معها بسبب العتاد أو البرمجيات.
</p>

<p>
	تُعَد هذه المقارنة مهمةً لفهم الاختلافات في السرعة وكمية العمل التي يمكن لكل إصدار التعامل معها بين MariaDB وMySQL، مما يساعدنا في اتخاذ خيارات مناسبة لترقية قاعدة بياناتنا وإعدادها الصحيح، ولكن يمكن أن تختلف خصائص الأداء المحددة وفقًا لعوامل مثل العتاد وحِمل العمل والضبط.
</p>

<h3 id="-2">
	التعامل مع أحمال العمل ذات حركة المرور العالية
</h3>

<p>
	يمكن ضبط كل من MariaDB و MySQL وتحسينهما للتعامل مع متطلبات التطبيقات ذات الطلب الكبير عند التعامل مع أحمال العمل ذات حركة المرور العالية، حيث تشمل العوامل الرئيسية التي يجب مراعاتها ما يلي:
</p>

<table>
	<thead>
		<tr>
			<th>
				العامل
			</th>
			<th>
				وصفه
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				العتاد Hardware
			</td>
			<td>
				تُعَد الموارد الكافية لوحدة المعالجة المركزية CPU والذاكرة والتخزين ضروريةً للتعامل مع أحمال العمل ذات الحركة العالية
			</td>
		</tr>
		<tr>
			<td>
				الفهرسة Indexing
			</td>
			<td>
				يمكن أن تحسّن الفهرسة المناسبة كثيرًا من أداء الاستعلامات من خلال تقليل كمية البيانات التي يجب مسحها
			</td>
		</tr>
		<tr>
			<td>
				تحسين الاستعلامات Query Optimization
			</td>
			<td>
				يمكن أن يساعد تحسين استعلامات SQL في تقليل استهلاك الموارد وتحسين أوقات الاستجابة
			</td>
		</tr>
		<tr>
			<td>
				التخبئة Caching
			</td>
			<td>
				يمكن أن يقلّل استخدام آليات التخبئة من عدد استعلامات قاعدة البيانات وتحسين الأداء العام
			</td>
		</tr>
		<tr>
			<td>
				النسخ المتماثل Replication والعنقدة Clustering
			</td>
			<td>
				يمكن أن يساعد تطبيق النسخ المتماثل والعنقدة في توزيع حِمل العمل عبر خوادم متعددة، مما يعزز قابلية التوسع والتوافرية
			</td>
		</tr>
	</tbody>
</table>

<h3 id="-3">
	المقارنة من حيث محركات التخزين
</h3>

<p>
	يؤثر اختيار محرّك التخزين تأثيرًا كبيرًا على أداء قاعدة البيانات ووظائفها، حيث تدعم كل من MariaDB و MySQL محرّكات متعددة، ولكن MariaDB تقدم مجموعةً أوسع من الخيارات، بما في ذلك XtraDB و ColumnStore، مما يوسّع إمكاناتها إلى ما هو أبعد من InnoDB و MyISAM وغيرها من محرّكات MySQL.
</p>

<p>
	تدعم MariaDB أيضًا كلًا من Blackhole و CSV و Aria و InnoDB و Archive و Connect و Cassandra Storage Engine، والعديد من المحرّكات الأخرى؛ بينما تتضمن محركات التخزين التي تدعمها MySQL أيضًا MyISAM و Merge و Federated و Archive و Memory و CSV و Blackhole و Example.
</p>

<p>
	<strong>ملاحظة</strong>: لا يهم عدد محركات التخزين التي تدعمها قاعدة البيانات، فالمهم هو استخدام قاعدة البيانات التي تدعم المحرّك المناسب للمتطلبات.
</p>

<h3 id="-4">
	المقارنة من حيث تحسين الاستعلامات
</h3>

<p>
	تتضمن كل من MariaDB و MySQL ميزات تحسين الاستعلام لتحسين الأداء وتقليل استهلاك الموارد، إذ يمكن أن تساعد هذه الميزات في ضمان تنفيذ الاستعلامات بكفاءة وتجنب الأعباء غير الضرورية.
</p>

<p>
	تتضمن بعض تقنيات تحسين الاستعلامات الشائعة ما يلي:
</p>

<table>
	<thead>
		<tr>
			<th>
				تقنية التحسين
			</th>
			<th>
				وصفها
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				الفهرسة
			</td>
			<td>
				يمكن أن يحسّن إنشاء الفهارس المناسبة كثيرًا من أداء الاستعلامات من خلال تقليل كمية البيانات التي يجب مسحها
			</td>
		</tr>
		<tr>
			<td>
				تخبئة الاستعلامات
			</td>
			<td>
				يمكن أن تقلّل تخبئة الاستعلامات التي يتكرر تنفيذها من الحاجة إلى عمليات بحث متكررة في قاعدة البيانات
			</td>
		</tr>
		<tr>
			<td>
				إعادة كتابة الاستعلام
			</td>
			<td>
				يمكن لقاعدة البيانات في بعض الأحيان إعادة كتابة الاستعلامات لتحسين أدائها أو تجنب الاختناقات المحتملة
			</td>
		</tr>
		<tr>
			<td>
				شرح الخطط
			</td>
			<td>
				يمكن استخدام شرح الخطط لتحليل خطط تنفيذ الاستعلام وتحديد مشكلات الأداء المحتملة
			</td>
		</tr>
	</tbody>
</table>

<h3 id="-5">
	المقارنة من حيث البحث عن نص كامل
</h3>

<p>
	يمكن لكل من MariaDB و MySQL البحث عن كلمات كاملة في النص، ويُعَد ذلك مفيدًا لأشياء مثل محركات البحث وأنظمة إدارة المستندات والمتاجر الإلكترونية.
</p>

<p>
	قد تختلف طريقة البحث عن الكلمات الكاملة التي تستخدمها MariaDB و MySQL بعض الشيء في كيفية عملها ومدى سرعتها، لذا يُفضَّل التحقق من احتياجات تطبيقنا ومقارنة كيفية بحث كلتا قاعدتي البيانات عن الكلمات الكاملة.
</p>

<h3 id="json">
	دعم JSON
</h3>

<p>
	تدعم كل من MariaDB و MySQL تنسيق <a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%85%D8%A7-%D8%B9%D9%84%D9%8A%D9%83-%D9%85%D8%B9%D8%B1%D9%81%D8%AA%D9%87-%D8%B9%D9%86-%D8%AD%D9%82%D9%88%D9%84-json-%D9%81%D9%8A-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mysql-r340/" rel="">JSON</a> وتنفذان العديد من الدوال نفسها، ولكن تخزّن MySQL تقارير JSON ككائنات ثنائية، بينما تخزنها MariaDB كسلاسل نصية.
</p>

<p>
	تظهر MySQL و MariaDB اختلافات في إمكاناتهما على التعامل مع JSON بالرغم من ارتباطهما الوثيق، حيث تفتخر MariaDB بمجموعة أوسع من دوال JSON بما في ذلك JSON_QUERY و JSON_EXISTS، والتي تفتقر MySQL إليها؛ ولكن MySQL تقدم الدالة JSON_TABLE لهيكلة بيانات JSON ضمن جدول، والتي هي ميزة غير موجودة في MariaDB.
</p>

<table>
	<thead>
		<tr>
			<th>
				الدالة
			</th>
			<th>
				قاعدة بيانات MariaDB
			</th>
			<th>
				قاعدة بيانات MySQL
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				الدالة JSON_ARRAY
			</td>
			<td>
				✔
			</td>
			<td>
				✔
			</td>
		</tr>
		<tr>
			<td>
				الدالة JSON_EXISTS
			</td>
			<td>
				✔
			</td>
			<td>
				✘
			</td>
		</tr>
		<tr>
			<td>
				الدالة JSON<em>OBJECT</em>AGG
			</td>
			<td>
				✔
			</td>
			<td>
				✔
			</td>
		</tr>
		<tr>
			<td>
				الدالة JSON_QUERY
			</td>
			<td>
				✔
			</td>
			<td>
				✘
			</td>
		</tr>
		<tr>
			<td>
				الدالة JSON_VALUE
			</td>
			<td>
				✔
			</td>
			<td>
				✔
			</td>
		</tr>
		<tr>
			<td>
				الدالة JSON_TABLE
			</td>
			<td>
				✘
			</td>
			<td>
				✔
			</td>
		</tr>
		<tr>
			<td>
				الدالة IS_JSON
			</td>
			<td>
				‫JSON_VALID
			</td>
			<td>
				‫JSON_VALID
			</td>
		</tr>
	</tbody>
</table>

<h3 id="oracle">
	التوافق مع Oracle
</h3>

<p>
	تتمتع MySQL ببعض ميزات أوراكل Oracle الأساسية المتوافقة، ولكن تُعَد MariaDB هي الوحيدة مفتوحة المصدر المتوافقة مع إجراءات وتسلسلات وأنواع البيانات المخزَّنة وغير ذلك لقاعدة بيانات أوراكل.
</p>

<p>
	قد تجد المؤسسات ذات الاستثمار الأكبر في نظام أوراكل البيئي أن قاعدة بيانات MySQL بديل جذاب بسبب وضع التوافق مع أوراكل، ولكن يُعَد دعم MySQL للغة PL/SQL الخاصة بأوراكل محدودًا. تُعَد قاعدة بيانات MariaDB خيارًا أفضل، إذ يمكنها العمل مع صيغة أوراكل ولديها دعم كامل للغة PL/SQL، ويكون ذلك مناسبًا للشركات التي تريد التحول من أوراكل مع الاحتفاظ بشيفرتها البرمجية القديمة واستخدام ميزات إجرائية متقدمة.
</p>

<h3 id="-6">
	ميزات الأمان
</h3>

<p>
	تحمي كلٌّ من MySQL و MariaDB البيانات من خلال التحقق من هوية المستخدمين واستخدام التشفير، ولكنهما تطبّقان التشفير بطريقة مختلفة، إذ تتيح MySQL إمكانية اختيار كيفية تشفير سجلات الإعادة والتراجع، ولكنها لا تحمي مساحات الجدول المؤقتة أو السجلات الثنائية. وتحتوي MariaDB على مزيدٍ من الخيارات للتشفير بما في ذلك حماية السجلات الثنائية والجداول المؤقتة، مما يجعل البيانات أكثر أمانًا.
</p>

<p>
	تستخدم MySQL و MariaDB أيضًا طرقًا مختلفةً للتحقق من هوية المستخدمين، إذ تحتوي MySQL على <code>validate_password</code> للتأكد من قوة كلمات المرور، وتحتوي MariaDB على مزيد من الخيارات مع إضافات مختلفة للتحقق من صحة البيانات. يُعَد أمان كلمة المرور أفضل في MariaDB باستخدام إضافة الاستيثاق ed25519 في الإصدار 10.4، والتي تُعَد أكثر أمانًا من طريقة SHA-1 القديمة، مما يعني أن MariaDB ملتزمة بأن تكون آمنة جدًا.
</p>

<h4 id="threadpooling">
	تجمع الخيوط Thread Pooling
</h4>

<p>
	تستخدم قواعد البيانات تجمع الخيوط للتعامل مع العديد من الاتصالات في وقت واحد، إذ تستخدم هذه الطريقة الخيوط نفسها للاتصالات الجديدة، مما يوفر من الموارد.
</p>

<p>
	تُعَد قاعدة بيانات MariaDB جيدةً جدًا في استخدام تجمّع الخيوط، إذ يحتوي إصدار المجتمع الخاص بها على تجمّع خيوط قوي يمكنه التعامل مع أكثر من 200000 اتصال في وقت واحد؛ وتحتوي MySQL أيضًا على تجمّع خيوط، ولكنها ليست جيدةً بالمقارنة مع تجمع الخيوط الموجود في MariaDB، وهي متوفرة في إصدار المؤسسة المدفوع.
</p>

<p style="text-align: center;">
	<img alt="1003721005تجمعالخيوط.png" class="ipsImage ipsImage_thumbnailed" data-fileid="172730" data-ratio="82.71" data-unique="kkdbml801" width="700" src="https://academy.hsoub.com/uploads/monthly_2025_05/1003721005.png.907496ed1bc3b9e6d3d8059080674a2e.png">
</p>

<h4 id="-7">
	التراخيص والقيود
</h4>

<p>
	تستخدم كل من MariaDB و MySQL ترخيص جنو العمومي General Public License -أو GPL اختصارًا، لكنهما يتبعان خطط ترخيص مختلفة. تُعَد MariaDB مرخصةً بالكامل بموجب ترخيص GPL، مما يعني أنها ستكون مجانيةً ومفتوحة المصدر دائمًا، ويُعَد ذلك مهمًا للمستخدمين الذين يقدّرون أهمية مجانية البرمجيات.
</p>

<p>
	تُعَد MySQL مُرخصَّة بترخيص مزدوج، فهي تحتوي على إصدار ترخيص GPL عام وإصدار تجاري خاص؛ إذ يحتوي الإصدار التجاري على مزيد من الميزات والدعم مثل تجمّع الخيوط، كما قد يؤدي ذلك إلى جعلها أسرع، ولكنه يمنع المستخدمين من تغيير الشيفرة البرمجية.
</p>

<p>
	قد تختار الشركات استخدام MySQL بسبب دعمها التجاري، ولكنها قد تحدّ من الوصول إلى الشيفرة البرمجية الأساسية نظرًا لاستراتيجيتها المزدوجة.
</p>

<h2 id="-8">
	اختيار قاعدة البيانات الصحيحة
</h2>

<p>
	إذا أردنا قاعدة بيانات أنشأها المجتمع وتتمتع بسرعة وأمان أفضل، فقد يكون استخدام قاعدة بيانات MariaDB مناسبًا. فقد أثبتت بعض الاختبارات أن MariaDB أسرع ويمكنها التعامل مع كمية عمليات أكبر من MySQL، كما أن MariaDB تتمتع ببعض ميزات أمان لا تتوفر في MySQL مثل تشفير البيانات أثناء تخزينها ونقلها، إلى جانب تمتع MariaDB بأمور لا تتوفر في MySQL، مثل الأعمدة الافتراضية ومحرّكات التخزين التسلسلية واستخدام محرّكات تخزين متعددة في جدول واحد.
</p>

<p>
	من ناحية أخرى، تم إنشاء قاعدة بيانات MariaDB من طرف المجتمع، لذا تُعَد أكثر انفتاحًا ووضوحًا من MySQL التي تملكها شركة أوراكل، حيث يمكن للمستخدمين المساعدة في جعل البرنامج وتطويره أكثر تركيزًا على احتياجات المجتمع.
</p>

<h2 id="mariadbmysql-2">
	حالات استخدام MariaDB و MySQL
</h2>

<p>
	تكون قاعدة MariaDB غالبًا أفضل للتطبيقات الكبيرة والسريعة والاستعلامات المعقدة، فهي تجعلها ميزاتها الخاصة خيارًا جيدًا للعمل الشاق، مثل ميزة Galera Cluster للحفاظ على تشغيل الأشياء وتحسينات محرّك تخزين InnoDB، بينما تكون MySQL غالبًا أفضل للتطبيقات الأصغر واحتياجات قواعد البيانات الأبسط.
</p>

<table>
	<thead>
		<tr>
			<th>
				شركات تستخدم MariaDB
			</th>
			<th>
				شركات تستخدم MySQL
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				سامسونج Samsung
			</td>
			<td>
				بي بي سي BBC
			</td>
		</tr>
		<tr>
			<td>
				شركة Financial Network, Inc.‎
			</td>
			<td>
				شركة ألعاب Big Fish
			</td>
		</tr>
		<tr>
			<td>
				شركة Virgin Media O2
			</td>
			<td>
				سبوتيفاي Spotify
			</td>
		</tr>
		<tr>
			<td>
				شركة Campus Cloud Services
			</td>
			<td>
				نتفليكس Netflix
			</td>
		</tr>
		<tr>
			<td>
				شركة Auto Europe
			</td>
			<td>
				ناسا NASA
			</td>
		</tr>
		<tr>
			<td>
				نوكيا Nokia
			</td>
			<td>
				 
			</td>
		</tr>
	</tbody>
</table>

<h2 id="mariadbwordpress">
	استخدام MariaDB مع WordPress
</h2>

<p>
	يمكن أن يؤدي استخدام WordPress مع قاعدة بيانات MariaDB إلى تحميل أسرع لموقع الويب، وهذا سيجذب زوّار الموقع أكثر؛ إذ تتميز MariaDB بقدرتها على تحسين الاستعلامات، ويمكنها التعامل مع عدد أكبر من الاتصالات في وقت واحد.
</p>

<p>
	من ناحية أخرى، يمكن لقاعدة بيانات MariaDB التعامل مع عدد أكبر من الاتصالات والمعاملات مقارنةً بقاعدة بيانات MySQL.
</p>

<p>
	كذلك، تتمتع MariaDB بميزات أمان أفضل مثل تشفير البيانات أثناء تخزينها، وطرق محسّنة للتحقق من هوية المستخدمين، وطرق أفضل لتعقّب ما يحدث.
</p>

<h2 id="mariadbcloudways">
	استخدام MariaDB مع موقع Cloudways
</h2>

<p>
	يوفر موقع Cloudways أحدث إصدارات MariaDB على جميع خوادمه التي أطلقها حديثًا، ويمكن تحديد إصدار MariaDB بناءً على متطلبات المشروع.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172728" href="https://academy.hsoub.com/uploads/monthly_2025_05/006_mariadb_cloudways.png.d8e4d67f6a87eb222dc0d94da7b66278.png" rel=""><img alt="006 mariadb cloudways" class="ipsImage ipsImage_thumbnailed" data-fileid="172728" data-unique="ku420vw2v" style="width: 700px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/006_mariadb_cloudways.thumb.png.cf8af91c94688b46a5be135c63fd76b7.png"> </a>
</p>

<p>
	<strong>ملاحظة</strong>: لا يمكن تخفيض إصدار MariaDB مرةً أخرى بعد الترقية إلى الإصدار الأعلى.
</p>

<h2 id="-9">
	الخلاصة
</h2>

<p>
	بهذا نكون قد وضحنا ميزات MariaDB و MySQL الرئيسية لإظهار الاختلاف بينهما لتسهيل المفاضلة بين قاعدة بيانات MariaDB أو MySQL.
</p>

<p>
	ترجمة -وبتصرّف- للقسم <a href="https://www.cloudways.com/blog/mariadb-vs-mysql/#comparison" rel="external nofollow">MariaDB vs MySQL: Understanding Key Differences and Choosing the Right Database</a> لصاحبته Hafsa Tahir.
</p>

<h2 id="-10">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D9%8A%D8%B1%D8%A7%D8%AF-%D9%88%D8%AA%D8%B5%D8%AF%D9%8A%D8%B1-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mysql-%D8%A3%D9%88-mariadb-r361/" rel="">كيفية استيراد وتصدير قواعد بيانات MySQL أو MariaDB</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%A3%D9%85%D9%8A%D9%86-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mysql-mariadb-%D8%B9%D9%84%D9%89-%D8%AE%D9%88%D8%A7%D8%AF%D9%8A%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r54/" rel="">كيفية تأمين قواعد البيانات MySQL ,MariaDB على خواديم لينكس</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%BA%D9%8A%D9%8A%D8%B1-%D9%85%D8%AC%D9%84%D8%AF-%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mariadb-%D8%A5%D9%84%D9%89-%D9%85%D9%83%D8%A7%D9%86%D9%8D-%D8%A2%D8%AE%D8%B1-r349/" rel="">كيفية تغيير مجلد تخزين بيانات MariaDB إلى مكانٍ آخر</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/mysql/%D8%AA%D8%B9%D9%84%D9%85-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-mysql-r297/" rel="">تعلم أساسيات MySQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%B1%D8%A8%D8%B7-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mariadb-%D8%A8%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%84%D8%A7%D8%B1%D8%A7%D9%81%D9%8A%D9%84-laravel-r2580/" rel="">كيفية ربط قاعدة بيانات MariaDB بتطبيق لارافيل Laravel</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">848</guid><pubDate>Wed, 16 Jul 2025 16:07:01 +0000</pubDate></item><item><title>&#x623;&#x647;&#x645; &#x645;&#x645;&#x64A;&#x632;&#x627;&#x62A; &#x635;&#x62F;&#x641;&#x629; Fish</title><link>https://academy.hsoub.com/devops/linux/%D8%A3%D9%87%D9%85-%D9%85%D9%85%D9%8A%D8%B2%D8%A7%D8%AA-%D8%B5%D8%AF%D9%81%D8%A9-fish-r847/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_05/02Fish.png.0ca499582ecf8d949988ced075866e19.png" /></p>
<p>
	تُعَد صدفة Fish واحدة من أكثر أنواع الصدفات shell تميزًا رغم كونها بذات الوقت من أقل الصدفات شيوعًا، بالرغم من أنّها مناسبة للمبتدئين والمحترفين في تطوير البرمجيات الحرّة و<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوحة المصدر</a> على حدٍ سواء.
</p>

<p>
	توفّر Fish مجموعةً من الميزات التي تجعلها خيارًا جاذبًا، انطلاقًا من تمييز الصيغة لونيًا أي تلوين التعليمات البرمجية لتمييزها بناءً على صيغتها، ووصولًا إلى الاختصارات المفضّلة لدى العديد من المستخدمين، ومرورًا بالعديد من الإضافات المبتكرة.
</p>

<p>
	لذا سنعرض بهذا المقال بعضًا من أبرز ميزات صدفة Fish.
</p>

<h2 id="1">
	1. تمييز الصيغة لونيا
</h2>

<p>
	يعدّ المبدأ <strong>من الأفضل اكتشاف الأخطاء قبل تنفيذها</strong> أساسيًا، إذ يوفًر الكثير من الوقت والجهد ويجنبنا الإحباط، فرغم دعم معظم محرّرات النصوص الحديثة لتمييز الصيغة لونيًا كميزة مُدمجة فيها، إلّا أنّ وجه التميز في Fish يتمثّل في توفيرها لهذه الميزة ضمن الصدفة نفسها، وكونها تعمل حتى مع أوامر <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-linux-%D9%88%D8%A3%D8%A8%D8%B1%D8%B2-%D9%85%D9%85%D9%8A%D8%B2%D8%A7%D8%AA%D9%87-%D9%88%D8%B9%D9%8A%D9%88%D8%A8%D9%87-r2252/" rel="">لينكس Linux</a>.
</p>

<p>
	ستظهر الأوامر باللون الأحمر في حال كونها غير صحيحة، والأمر ذاته ينطبق على الخيارات والوسطاء التي لا تتناسب مع السياق.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172709" href="https://academy.hsoub.com/uploads/monthly_2025_05/001_____.png.ee4d3877849bd5d703de1fef45cf989a.png" rel=""><img alt="001_كيفية_تمييز_الأخطاء_باللون_الأحمر.png" class="ipsImage ipsImage_thumbnailed" data-fileid="172709" data-ratio="21.89" data-unique="de9oex8ji" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_05/001_____.thumb.png.4674c14bbb40a22dc68af397eba98a71.png"></a>
</p>

<h2 id="2">
	2. الاقتراحات التلقائية
</h2>

<p>
	توفّر صدفة Fish اقتراحات للأوامر أثناء كتابتنا لها، ويمكننا تطبيقها بكل سهولة باستخدام مفتاح Tab <a data-fileext="svg" data-fileid="172720" href="https://academy.hsoub.com/applications/core/interface/file/attachment.php?id=172720&amp;key=e9e6f7cbd2f60b5642a682545448f970" rel="">كما يتضح بهذا الشكل بنسخة SVG</a>.
</p>

<p>
	ستظهر الاقتراحات بلون باهت أثناء الكتابة لتسهيل آلية الاستفادة منها، وفي حال كان سطر الاقتراح مناسبًا بالكامل، فيمكننا الضغط على مفتاح السهم الأيمن لاستكماله تلقائيًا.
</p>

<h2 id="3">
	3. صفحة المساعدة التفاعلية لخيارات الأوامر
</h2>

<p>
	تُعدّ من المزايا الرائعة التي تُتيح إمكانية استكمال خيارات الأوامر بالاستعانة بصفحة المساعدة الخاصة بالبرنامج أو الأداة التي نستخدمها على نحوٍ تفاعليّ.
</p>

<p>
	سنحتاج بدايةً إلى قراءة المعلومات من صفحة المساعدة لتحليل النصوص وتنظيمها في صيغة تجعل الصدفة قادرةً على عرض الاقتراحات التفاعلية وعرض خيارات الأوامر بناءً على المعلومات المستخرجة، ويمكننا القيام بذلك عبر تشغيل الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4749_24" style=""><span class="pln">fish_update_completions</span></pre>

<p>
	إذ سيُحلّل هذا الأمر محتوى صفحات المساعدة.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172701" href="https://academy.hsoub.com/uploads/monthly_2025_05/003____.png.f5ad4b74b07ec16e60300d1422cfe9f1.png" rel=""><img alt="003 تحليل محتوى صفحات المساعدة" class="ipsImage ipsImage_thumbnailed" data-fileid="172701" data-unique="ea0nfg44d" src="https://academy.hsoub.com/uploads/monthly_2025_05/003____.thumb.png.f9ed5259ab92ace403bad094974557e9.png"> </a>
</p>

<p>
	وبذلك وعند كتابة أمر معين، ما علينا سوى وضع إشارة الشرِطة (-) لإضافة الخيارات ثم الضغط على مفتاح Tab، وستظهر اقتراحات <a data-fileext="svg" data-fileid="172721" href="https://academy.hsoub.com/applications/core/interface/file/attachment.php?id=172721&amp;key=e6d78a16ac6220a907d38b9d6b19ceba" rel="">كما هو واضح هنا</a> من صفحة المساعدة للخيارات المُتاحة للأمر مع توصيف مختصر لكل منها.
</p>

<p>
	يمكننا التمرير عبر الخيارات وقراءة وصفها ضمن أداة عرض النصوص التفاعلية pager التي تظهر، ثم اختيار المناسب منها بناءً على وظيفته.
</p>

<p>
	تُسهّل هذه الميزة العمل مقارنةً بحالة استخدام أمر من قبيل <code>command -h</code>.
</p>

<h2 id="4">
	4. استخدام الاختصارات بدلًا من الأسماء البديلة
</h2>

<p>
	تشبه الاختصارات في صدفة Fish أدوات توسيع النصوص تلقائيًا، إذ يمكننا تعيين شيفرة مُستخدمة على نحوٍ متكرّر إلى اختصار يسهل الوصول إليه.
</p>

<p>
	فعلى سبيل المثال، سنعين فيما يلي المقطع <code>sch</code> كاختصار للأمر <code>pacman -Ss</code> المُتعلّق بالبحث عن الحزم:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4749_26" style=""><span class="pln">abbr </span><span class="pun">-</span><span class="pln">a sch pacman </span><span class="pun">-</span><span class="typ">Ss</span></pre>

<p>
	وبذلك في كل مرة نكتب فيها <code>sch</code> ونضغط على مفتاح المسافة، سيتم استبداله تلقائيًا بالأمر <code>pacman -Ss</code>.
</p>

<p>
	تجدر الإشارة إلى أن هذه الاختصارات مؤقتة وتقتصر على جلسة العمل الحالية، ولجعل الاختصار دائمًا، يمكننا كتابته في ملف التهيئة Config.
</p>

<p>
	<strong>ملاحظة</strong>: يكمن الفرق بين الأسماء البديلة والاختصارات في كون الاسم البديل يعمل في الخلفية دون أن يظهر الأمر الفعلي الذي يشير إليه; أمّا الاختصار, فيُظهر الأوامر الفعلية، كما تُسجّل بشكلها الصحيح في سجل الأوامر.
</p>

<h2 id="5">
	5. مساعدة شاملة قائمة على الويب
</h2>

<p>
	يميل المستخدمون المتمرسون في لينكس إلى الاعتماد على صفحات المساعدة كمصدر للدعم حول الأوامر، في حين ينصبّ أكثر اعتماد الجيل الجديد من مستخدمي لينكس على الإنترنت للحصول على مثل هذه المعلومات.
</p>

<p>
	وفي هذا الصدد، تُقدّم صدفة Fish مزيجًا من الطريقتين من خلال ميزة المساعدة الشاملة القائمة على الويب، والمتوفّرة بسهولة على النظام حتى في حال عدم الاتصال بالإنترنت، إذ تُخزّن محليًا. وللحصول على المساعدة أثناء استخدام صدفة Fish، نكتب الأمر التالي:
</p>

<pre class="ipsCode">help
</pre>

<p>
	وسيؤدي ذلك إلى فتح صفحة المساعدة في متصفح الويب.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172700" href="https://academy.hsoub.com/uploads/monthly_2025_05/006________Fish.png.9d2d6a38ca0dc527ad51218b7de20131.png" rel=""><img alt="006_صفحة_المساعدة_القائمة_على_الويب_في_صدفة_Fish.png" class="ipsImage ipsImage_thumbnailed" data-fileid="172700" data-ratio="54.78" data-unique="bw7u8chz4" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_05/006________Fish.thumb.png.bd3ac5c9430e95c58aa093e88cfddbf1.png"></a>
</p>

<p>
	ومنها يُمكن الرجوع إلى التوثيق الشامل بكل سهولة.
</p>

<h2 id="6">
	6. الإعدادات المعتمدة على الويب
</h2>

<p>
	توفّر Fish ميزةً أخرى تعتمد على الويب، فلتغيير لون الموجّه أو إجراء تعديلات أخرى على الإعدادات مثلًا، لسنا مُضطرين إلى تعديل ملفات الضبط يدويًا عبر واجهة أسطر الأوامر؛ إذ من الممكن استخدام الإعدادات المعتمدة على الويب.
</p>

<p>
	يمكن كتابة الأمر التالي أثناء تشغيل صدفة Fish:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4749_28" style=""><span class="pln">fish_config</span></pre>

<p>
	وسيؤدي ذلك إلى فتح صفحة الإعدادات كواجهة رسومية ضمن متصفح الويب.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="webp" data-fileid="172707" href="https://academy.hsoub.com/uploads/monthly_2025_05/007___Fish___.webp.51d1d7e37e0553646935162816ba6ff7.webp" rel=""><img alt="007_إعدادات_صدفة_Fish_المعتمدة_على_الويب.webp" class="ipsImage ipsImage_thumbnailed" data-fileid="172707" data-ratio="54.78" data-unique="hpw5fkide" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_05/007___Fish___.thumb.webp.9c669673847d2daa30e44bdeba628311.webp"></a>
</p>

<p>
	يمكننا من خلال هذه الصفحة تغيير الألوان، أو اختيار موجّه مختلف من الخيارات المُتاحة وضبط غيرها من التعديلات.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172699" href="https://academy.hsoub.com/uploads/monthly_2025_05/008___.png.9f91ac8fc10dced1203b77059e8037bb.png" rel=""><img alt="008_قائمة_خيارات_الموجّه.png" class="ipsImage ipsImage_thumbnailed" data-fileid="172699" data-ratio="58.00" data-unique="qt43vtlgv" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_05/008___.thumb.png.7cd8141eb5e5507e5fa3bb79bc2091ac.png"></a>
</p>

<h2 id="7cd">
	7. الانتقال التلقائي إلى المجلدات كبديل عن الأمر CD
</h2>

<p>
	لسنا بحاجة إلى كتابة الأمر cd إذا أردنا الانتقال إلى مجلد معيّن، إذ يكفي كتابة اسم المجلد، وهذا كل ما في الأمر!
</p>

<p>
	فعلى سبيل المثال، إذا كنا في المجلد Home ورغبنا بالانتقال إلى المجلد الفرعي Downloads، فيكفي أن نكتب كلمة Downloads، كما ستعرض الصدفة اقتراحات للإكمال التلقائي أثناء الكتابة.
</p>

<p>
	أمّا إذا كنا في دليل معيّن ورغبنا بالانتقال إلى فرع مختلف تمامًا، فسنحتاج إلى استخدام المسار المطلق.
</p>

<h2 id="8">
	8. تنقل أسهل بين المسارات
</h2>

<p>
	إذا كنا بحاجة للتنقّل مرارًا بين المجلدات التي زرناها مؤخرًا، فما من داعٍ لإعادة كتابة المسار أو استخدام أمر <code>cd</code>، إذ يكفي الضغط على مفتاح Alt مع السهم اليميني للانتقال إلى المسارات السابقة أو السهم اليساري <a data-fileext="svg" data-fileid="172718" href="https://academy.hsoub.com/applications/core/interface/file/attachment.php?id=172718&amp;key=df8daf61e1f5ef82b20baf081a0ccc75" rel="">لهذه المسارات</a>.
</p>

<p>
	وكبديل، يمكننا كتابة الأمر <code>cdh</code> والضغط على مفتاح Enter، وستظهر أداة عرض نصوص تفاعلية تتيح لنا اختيار المجلد الذي نريد العودة إليه باستخدام الرقم المقابل له.
</p>

<h2 id="9">
	9. البحث التفاعلي في السجل
</h2>

<p>
	يمكننا البحث عن أمر معيّن في سجل الأوامر على نحوٍ تفاعليّ في صدفة Fish، وللقيام بذلك نستخدم الاختصار المعروف وهو مفتاح CTRL مع حرف R.
</p>

<p>
	سيُظهر ذلك <a data-fileext="svg" data-fileid="172719" href="https://academy.hsoub.com/applications/core/interface/file/attachment.php?id=172719&amp;key=d945ef2c3f35539edab3cb092c4af2fc" rel="">أداة عرض النصوص التفاعلية هذه</a> تطلب إدخال نص البحث، فما علينا سوى كتابة اسم الأمر الذي نحتاجه، وستظهر النتائج مباشرةً.
</p>

<h2 id="10">
	10. المتغيرات العمومية
</h2>

<p>
	في حال تعيّين متغير على أنّه متغير عمومي في صدفة Fish، فسيبقى مُتاحًا حتى في حال إعادة تشغيل الصدفة أو حتى النظام بأكمله.
</p>

<p>
	ولإنشاء متغير عمومي، سنستخدم الأمر التاليك
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4749_12" style=""><span class="kwd">set</span><span class="pln"> </span><span class="pun">-</span><span class="pln">U my_variable </span><span class="lit">10</span></pre>

<p>
	ستُحفظ قيمة المتغير <code>my_variable</code> لتكون 10، وستبقى محفوظةً حتى بعد إعادة تشغيل النظام.
</p>

<p>
	ولا حاجة لإضافتها إلى ملف تهيئة بيئة الصدفة RC أو ملف التكوين الشخصي Profile.
</p>

<h2 id="11">
	11. الوضع الخاص
</h2>

<p>
	توفّر صدفة Fish وضعًا خاصًا، وفيه لا تُحفظ الأوامر التي نُدخلها في سجل الأوامر ولا تُخزّن على القرص.
</p>

<p>
	وللانتقال إلى الوضع الخاص، نستخدم الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_4749_14" style=""><span class="pln">fish </span><span class="pun">-</span><span class="pln">P</span></pre>

<p>
	وبمجرد الانتهاء من العمل، يمكننا الخروج من الوضع الخاص بكتابة الأمر:
</p>

<pre class="ipsCode">exit
</pre>

<h2 id="">
	الخلاصة
</h2>

<p>
	لا يُعدّ ما قدمناه في هذا المقال إلّا غيضًا من فيض لاستكشافه في صدفة Fish، إذ توفّر <a href="https://fishshell.com/" rel="external nofollow">صدفة Fish</a> العديد من الميزات التي تُسهّل الاستخدام، من قبيل:
</p>

<ul>
	<li>
		تحرير الأوامر مُتعدّدة الأسطر بسهولة بالضغط على مفتاح ALT مع مفتاح ENTER
	</li>
	<li>
		التبديل بين أنماط اختصارات لوحة المفاتيح الخاصّة بمحرّريّ النصوص Emacs (الافتراضي) وVim
	</li>
	<li>
		صيغة بسيطة وواضحة لكتابة النصوص البرمجية
	</li>
</ul>

<p>
	ولاشكّ أنّه من الممكن تحقيق معظم الميزات التي ناقشناها في هذا المقال ببذل بعض الجهد الإضافي في صدفاتٍ أخرى، ولكن وجودها مفعّلة افتراضيًا في Fish يجعل الأمر مختلفًا تمامًا.
</p>

<p>
	فمن الممكن أن تساعدنا Fish على أن نكون أكثر إنتاجية وفعالية في عملنا كمطوّرين، لاسيما في حال كنا نتحكّم في بيئة التطوير الخاصّة بنا.
</p>

<p>
	ولا يُنصح عمومًا بكتابة نصوص برمجية خاصّة بصدفة Fish على نحوٍ حصريّ إذا كان من المتوقّع أن يعمل الآخرون عليها في صدفة Bash، إذ قد نواجه مشكلات في التوافق بين الصدفات في البيئات المشتركة مُتعدّدة المستخدمين.
</p>

<p>
	ننصحكم بتجربة صدفة Fish ومراقبة سير الأمور، فربما ستجعلونها صدفتكم الافتراضية!
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://itsfoss.com/fish-shell-features/" rel="external nofollow">11 Fish Shell Features That Make it More Awesome Than Bash</a> لصاحبيه Abhishek Prakash وSreenath.
</p>

<h2 id="-1">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D8%AF%D9%81%D8%A9-%D8%A8%D8%A7%D8%B4-bash-r606/" rel="">مدخل إلى صدفة باش Bash</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/databases/postgresql/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B5%D8%AF%D9%81%D8%A9-psql-r473/" rel="">أساسيات استخدام صدفة psql</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%82%D8%B1%D8%A7%D8%A1%D8%A9-%D9%88%D8%B6%D8%A8%D8%B7-%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%B5%D8%AF%D9%81%D8%A9-shell-%D9%88%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A9-%D9%81%D9%8A-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r697/" rel="">قراءة وضبط متغيرات الصدفة Shell والبيئة في لينكس</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">847</guid><pubDate>Mon, 14 Jul 2025 16:04:04 +0000</pubDate></item><item><title>&#x628;&#x646;&#x627;&#x621; &#x645;&#x646;&#x647;&#x62C;&#x64A;&#x629; &#x639;&#x645;&#x644; DevOps &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x623;&#x62F;&#x648;&#x627;&#x62A; &#x645;&#x641;&#x62A;&#x648;&#x62D;&#x629; &#x627;&#x644;&#x645;&#x635;&#x62F;&#x631;</title><link>https://academy.hsoub.com/devops/general/%D8%A8%D9%86%D8%A7%D8%A1-%D9%85%D9%86%D9%87%D8%AC%D9%8A%D8%A9-%D8%B9%D9%85%D9%84-devops-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D9%81%D8%AA%D9%88%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-r846/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_05/DevOps.png.4077a9445a365bc8591048b83ca550cd.png" /></p>
<p>
	أصبح اعتماد منهجية DevOps هو الحل البديهي لتحسين البرامج البطيئة وغير الناجحة وأتمتة المهام اليدوية. ولذلك، سنشرح في هذا المقال ما هي منهجية عمل إنتاج البرمجيات في DevOps أو ما يُعرف بمصطلح DevOps pipeline وما هي خطوات إنشائها.
</p>

<h2 id="devopscicd">
	مقدمة عن منهجية DevOps وخطوط التكامل والتسليم المستمر CI/CD
</h2>

<p>
	تطورت منهجية DevOps لتشتمل على عديد من التخصصات المختلفة، ولكن معظم الناس يتفقون على أن DevOps هي منهجية لتطوير البرمجيات أو دورة حياة تطوير البرمجيات SDLC، ومبدؤها الأساسي هو تغيير ثقافة العمل واعتماد أتمتة المهام اليدوية لزيادة الإنتاجية وتحسين مرونة تنفيذ المهام.
</p>

<p>
	هناك بعض الأدوات الضرورية لتحقيق بيئة DevOps، لكن المفتاح الرئيسي هو إنشاء منهجية عمل Pipeline يُطبّق فيها <a href="https://academy.hsoub.com/devops/deployment/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D9%83%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-%D9%88%D8%A7%D9%84%D9%86%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-cicd-r644/" rel="">التكامل المستمر والنشر المستمر CI/CD</a>، بحيث تحتوي على مراحل مختلفة مثل مرحلة التطوير DEV، والتكامل INT، والاختبار TST، والتحقق من الجودة QA، واختبار قبول المستخدم UAT، والتحضير STG، والانتاج PROD؛ وتؤتمت فيها المهام اليدوية، لكي يستطيع المطورون العمل بمرونة وكتابة شيفرة عالية الجودة، والنشر مرات عديدة.
</p>

<p>
	يقدّم هذا المقال طريقةً مكونةً من خمس خطوات لإنشاء منهجية عمل لإنتاج البرمجيات بأسلوب <a href="https://academy.hsoub.com/devops/general/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%81%D9%8A-%D9%85%D8%AC%D8%A7%D9%84-devops-%D9%88%D8%A3%D9%87%D9%85-%D9%85%D9%85%D8%A7%D8%B1%D8%B3%D8%A7%D8%AA%D9%87-%D9%88%D8%A3%D8%AF%D9%88%D8%A7%D8%AA%D9%87-r844/" rel="">DevOps</a> باستخدام أدوات <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوحة المصدر</a>، مثل ماهو موضح في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172403" href="https://academy.hsoub.com/uploads/monthly_2025_05/1_finaldevopspipeline.png.3d972ba702e1f0cda0f20024a3367eba.png" rel=""><img alt="1 finaldevopspipeline" class="ipsImage ipsImage_thumbnailed" data-fileid="172403" data-unique="vm91q4lgg" src="https://academy.hsoub.com/uploads/monthly_2025_05/1_finaldevopspipeline.png.3d972ba702e1f0cda0f20024a3367eba.png"> </a>
</p>

<h2 id="1cicd">
	1. استخدام اُطر عمل التكامل والنشر المستمر CI/CD
</h2>

<p>
	أول ما نحتاج إليه هو إحدى أدوات <a href="https://academy.hsoub.com/devops/deployment/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D9%83%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-%D9%88%D8%A7%D9%84%D9%86%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-cicd-r644/" rel="">التكامل والنشر المستمر CI/CD</a>، ويُنصح لهذا الغرض باستخدام أداة <a href="https://academy.hsoub.com/devops/linux/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-jenkins-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1804-r462/" rel="">جنكينز Jenkins</a>، وهي أداة مفتوحة المصدر ومبنية على <a href="https://academy.hsoub.com/programming/java/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-java-r2275/" rel="">لغة جافا Java</a> وترخيص MIT، وهي ما ساهم في نشر منهجية DevOps لتصبح المعيار الأساسي.
</p>

<p>
	ولتبسيط الأمر، يمكننا تخيل أن أداة Jenkins هي جهاز تحكم عن بعد يمكنه التخاطب مع خدمات وأدوات عدة المختلفة وإدارتها، وهي عديمة النفع بمفردها، ولكنها تصير أكثر قوةً عند استخدامها مع أدوات وخدمات مختلفة. وهي واحدة من العديد من أدوات CI/CD مفتوحة المصدر التي يمكن الاستفادة منها لإنشاء منهجية عمل DevOps.
</p>

<p>
	فيما يلي بعض الأدوات الأخرى:
</p>

<table>
	<thead>
		<tr>
			<th>
				الأداة
			</th>
			<th>
				الرخصة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<a href="https://github.com/jenkinsci/jenkins" rel="external nofollow">Jenkins</a>
			</td>
			<td>
				المشاع الإبداعي و MIT
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://github.com/travis-ci/travis-ci" rel="external nofollow">Travis CI</a>
			</td>
			<td>
				MIT
			</td>
		</tr>
		<tr>
			<td>
				<a href="http://cruisecontrol.sourceforge.net/" rel="external nofollow">CruiseControl</a>
			</td>
			<td>
				BSD
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://github.com/buildbot/buildbot" rel="external nofollow">Buildbot</a>
			</td>
			<td>
				GPL
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://gump.apache.org/" rel="external nofollow">Apache Gump</a>
			</td>
			<td>
				Apache 2.0
			</td>
		</tr>
		<tr>
			<td>
				<a href="http://cabie.tigris.org/" rel="external nofollow">Cabie</a>
			</td>
			<td>
				GNU
			</td>
		</tr>
	</tbody>
</table>

<p>
	توضح الصورة التالية سير عمليات DevOps باستخدام أدوات التكامل والنشر المستمر CI/CD:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172400" href="https://academy.hsoub.com/uploads/monthly_2025_05/2_runningjenkins.png.770ea43e3dbeea2577f3919e691d55b0.png" rel=""><img alt="2 runningjenkins" class="ipsImage ipsImage_thumbnailed" data-fileid="172400" data-unique="zznfl9rp8" src="https://academy.hsoub.com/uploads/monthly_2025_05/2_runningjenkins.png.770ea43e3dbeea2577f3919e691d55b0.png"> </a>
</p>

<p>
	وبهذا تكون قد أصبحت لدينا أداة CI/CD قيد التشغيل في مضيفنا المحلي، ولكن لا يمكن الاستفادة منها في شيء الآن. سنتابع شرح باقي الخطوات كي نتعلم كيفية استخدام الأدوات لبناء منهجية عمل DevOps.
</p>

<h2 id="2sourcecodemanagement">
	2. إدارة الشيفرة المصدرية Source Code Management
</h2>

<p>
	إن أفضل وأسهل طريقة للتأكد من أن أدوات CI/CD تعمل جيدًا هي استخدامها مع إحدى أدوات إدارة الشفرة المصدرية Source Code Management أو اختصارًا: SCM، وذلك لأن الشيفرة التي نكتبها عند تطوير تطبيق ما باستخدام إحدى لغات البرمجة مثل لغة جافا Java أو <a href="https://academy.hsoub.com/python/" rel="">بايثون Python</a> أو <a href="https://academy.hsoub.com/programming/cpp/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-c-r802/" rel="">C++</a>‎ أو <a href="https://academy.hsoub.com/programming/go/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-go-r222/" rel="">لغة جو Go</a>، أو <a href="https://academy.hsoub.com/javascript/" rel="">جافاسكربت Javascript</a>، أو غيرها، يطلق عليها اسم الشيفرة المصدرية Source Code.
</p>

<p>
	عند العمل بمفردنا، سيكون من الأسهل هو حفظ كل ما يلزمنا في مجلد واحد على جهازنا؛ ولكن عند العمل على مشروع كبير يتم فيه التعاون مع الآخرين، فسوف نحتاج إلى طريقة لمشاركة التعديلات على الشيفرة بفعالية وتجنب التعارض عند التعديل؛ كما سنحتاج أيضًا إلى طريقة لاستعادة النسخ السابقة من الشيفرة، وأحدث من طريقة النسخ الاحتياطي أو طريقة النسخ واللصق التقليدية.
</p>

<p>
	هنا علينا استخدام أدوات إدارة الشيفرة المصدرية SCM التي تساعد في تخزين الشيفرة في مستودعات، والتحكم في إصدار الشيفرة، والتنسيق بين أعضاء المشروع.
</p>

<p>
	ثمة أدوات عدة لإدارة الشيفرة المصدرية، لكن <a href="https://academy.hsoub.com/programming/workflow/git/" rel="">غيت Git</a> أفضلها، وفيما يلي ذكر لأدوات أخرى مفتوحة المصدر:
</p>

<table>
	<thead>
		<tr>
			<th>
				الأداة
			</th>
			<th>
				الرخصة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<a href="https://git-scm.com/" rel="external nofollow">غيت</a>
			</td>
			<td>
				GPLv2 &amp; LGPL v2.1
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://subversion.apache.org/" rel="external nofollow">Subversion</a>
			</td>
			<td>
				Apache 2.0
			</td>
		</tr>
		<tr>
			<td>
				<a href="http://savannah.nongnu.org/projects/cvs" rel="external nofollow">Concurrent Versions System (CVS) </a>
			</td>
			<td>
				GNU
			</td>
		</tr>
		<tr>
			<td>
				<a href="http://www.vestasys.org/" rel="external nofollow">Vesta</a>
			</td>
			<td>
				LGPL
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://www.mercurial-scm.org/" rel="external nofollow">Mercurial</a>
			</td>
			<td>
				GNU GPL v2+
			</td>
		</tr>
	</tbody>
</table>

<p>
	توضح الصورة الآتية كيف تبدو منهجية عمل DevOps عند استخدام إحدى أدوات إدارة الشيفرة المصدرية SCM:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172402" href="https://academy.hsoub.com/uploads/monthly_2025_05/3_sourcecontrolmanagement.png.7635b36441c2fa6b326fdf3afa354fbd.png" rel=""><img alt="3 sourcecontrolmanagement" class="ipsImage ipsImage_thumbnailed" data-fileid="172402" data-unique="uy5bb0d2w" src="https://academy.hsoub.com/uploads/monthly_2025_05/3_sourcecontrolmanagement.png.7635b36441c2fa6b326fdf3afa354fbd.png"> </a>
</p>

<p>
	وكما هو واضح، يمكن لأداة التكامل والنشر المستمر CI/CD أتمتة التعديلات على الشيفرة المصدرية وتسهيل التعاون بين أعضاء الفريق. وسنتعرف في باقي الخطوات على كيفية الاستفادة من ذلك في تطبيق عملي حتى يتمكن العديد من الناس من استخدامه.
</p>

<h2 id="3">
	3. إنشاء أداة لأتمتة عمليات بناء الشيفرة
</h2>

<p>
	يمكن الآن الإطلاع على تعديلات الشيفرة وإضافة تعديلاتنا، كما يمكن التعاون مع مطورين آخرين في كتابة الشيفرة. للتأكيد، يجب علينا تجميع التطبيق قبل ذلك ووضعه في تنسيق حزمة قابلة للنشر أو تشغيله كملف قابل للتنفيذ Executable لجعله تطبيق ويب. يمكننا ملاحظة أن لغات البرمجة المُفسَّرة مثل جافاسكربت JavaScript أو <a href="https://academy.hsoub.com/programming/php/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%84%D8%BA%D8%A9-php-r2040/" rel="">PHP</a> لا تحتاج إلى <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%A3%D9%88%D9%84-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-compilation-%D9%81%D9%8A-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r976/" rel="">تجميع Compilation</a>، وهنا علينا استخدام إحدى أدوات أتمتة عمليات بناء البرامج Build Automation Tool، والتي تهدف إلى إنشاء شيفرة مصدرية بتنسيق معين، وأتمتة عمليات تنظيف الشيفرة وتجميعها واختبارها ونشرها في موقع معين.
</p>

<p>
	تختلف أدوات البناء وفقًا للغة البرمجة، وفيما يلي بعض الأدوات الشائعة مفتوحة المصدر:
</p>

<table>
	<thead>
		<tr>
			<th>
				الأداة
			</th>
			<th>
				الرخصة
			</th>
			<th>
				لغة البرمجة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<a href="https://maven.apache.org/" rel="external nofollow">Maven</a>
			</td>
			<td>
				أباتشي Apache 2.0
			</td>
			<td>
				جافا Java
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://ant.apache.org/" rel="external nofollow">Ant</a>
			</td>
			<td>
				أباتشي Apache 2.0
			</td>
			<td>
				جافا Java
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://gradle.org/" rel="external nofollow">Gradle</a>
			</td>
			<td>
				أباتشي Apache 2.0
			</td>
			<td>
				جافا Java
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://bazel.build/" rel="external nofollow">Bazel</a>
			</td>
			<td>
				أباتشي Apache 2.0
			</td>
			<td>
				جافا Java
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://www.gnu.org/software/make" rel="external nofollow">Make</a>
			</td>
			<td>
				GNU
			</td>
			<td>
				---
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://gruntjs.com/" rel="external nofollow">Grunt</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				جافا سكربت JavaScript
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://gulpjs.com/" rel="external nofollow">Gulp</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				جافا سكربت JavaScript
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://github.com/ruby/rake" rel="external nofollow">Rake</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				Ruby
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://buildr.apache.org/" rel="external nofollow">Buildr</a>
			</td>
			<td>
				Apache
			</td>
			<td>
				Ruby
			</td>
		</tr>
		<tr>
			<td>
				<a href="http://www.a-a-p.org/" rel="external nofollow">A-A-P</a>
			</td>
			<td>
				GNU
			</td>
			<td>
				بايثون Python
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://www.scons.org/" rel="external nofollow">SCons</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				بايثون Python
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://www.yoctoproject.org/software-item/bitbake" rel="external nofollow">BitBake</a>
			</td>
			<td>
				GPLv2
			</td>
			<td>
				بايثون Python
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://github.com/cake-build/cake" rel="external nofollow">Cake</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				C#‎
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://common-lisp.net/project/asdf" rel="external nofollow">ASDF</a>
			</td>
			<td>
				Expat (MIT)
			</td>
			<td>
				LISP
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://www.haskell.org/cabal" rel="external nofollow">Cabal</a>
			</td>
			<td>
				BSD
			</td>
			<td>
				Haskell
			</td>
		</tr>
	</tbody>
</table>

<p>
	يمكننا الآن إضافة ملفات إعدادات أداة أتمتة بناء الشيفرة إلى أداة إدارة الشيفرة المصدرية SCM وستتولى أداة التكامل والنشر المستمر CI/CD أمر بنائها.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172401" href="https://academy.hsoub.com/uploads/monthly_2025_05/4_buildtools.png.19fe6b657aca376d7320fa1fe91bfd26.png" rel=""><img alt="4 buildtools" class="ipsImage ipsImage_thumbnailed" data-fileid="172401" data-unique="dbdv7jeui" src="https://academy.hsoub.com/uploads/monthly_2025_05/4_buildtools.png.19fe6b657aca376d7320fa1fe91bfd26.png"> </a>
</p>

<h2 id="4">
	4. اختيار خادم تطبيقات الويب
</h2>

<p>
	للحصول على فائدة من التطبيق، يجب أن يكون التطبيق قادرًا على توفير خدمة أو واجهة ما، كما يجب استضافته على خادم تطبيقات ويب؛ إذ يوفر الخادم بيئةً قادرةً على قراءة المنطق البرمجي داخل الحزمة القابلة للنشر، وعرض واجهة التطبيق، وتقديم خدمات الويب عن طريق إتاحة مآخذ هذه الخدمات، ولذلك ستحتاج إلى <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-http-%D8%B4%D8%B1%D8%AD-%D8%A7%D9%84%D8%AA%D8%AE%D8%A7%D8%B7%D8%A8-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%88%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r74/" rel="">خادم HTTP</a> وآلة افتراضية Virtual machine لتثبيت خادم التطبيقات.
</p>

<p>
	فيما يلي عدد من خوادم تطبيقات الويب مفتوحة المصدر التي يمكن المفاضلة بينها واختيار الأنسب بينها:
</p>

<table>
	<thead>
		<tr>
			<th>
				الأداة
			</th>
			<th>
				الرخصة
			</th>
			<th>
				لغة البرمجة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<a href="https://tomcat.apache.org/" rel="external nofollow">Tomcat</a>
			</td>
			<td>
				Apache 2.0
			</td>
			<td>
				جافا
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://www.eclipse.org/jetty/" rel="external nofollow">Jetty</a>
			</td>
			<td>
				Apache 2.0
			</td>
			<td>
				جافا
			</td>
		</tr>
		<tr>
			<td>
				<a href="http://wildfly.org/" rel="external nofollow">WildFly</a>
			</td>
			<td>
				GNU Lesser Public
			</td>
			<td>
				جافا
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://javaee.github.io/glassfish" rel="external nofollow">GlassFish</a>
			</td>
			<td>
				CDDL &amp; GNU Less Public
			</td>
			<td>
				جافا
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://www.djangoproject.com/" rel="external nofollow">Django</a>
			</td>
			<td>
				3-Clause BSD
			</td>
			<td>
				بايثون
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://github.com/cdent/paste" rel="external nofollow">Python Paste</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				بايثون
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://rubyonrails.org/" rel="external nofollow">Rails</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				Ruby
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://nodejs.org/en" rel="external nofollow">Node.js</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				جافا سكربت
			</td>
		</tr>
	</tbody>
</table>

<p>
	بعد تحديد <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">خادم الويب</a> المناسب، نستطيع القول أنه قد أصبحت لدينا منهجية عمل DevOps شبه مُعَدّة للاستخدام.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172404" href="https://academy.hsoub.com/uploads/monthly_2025_05/5_applicationserver.png.9f82b944fea2f0f3bf037521753d0254.png" rel=""><img alt="5 applicationserver" class="ipsImage ipsImage_thumbnailed" data-fileid="172404" data-unique="54agzjuod" src="https://academy.hsoub.com/uploads/monthly_2025_05/5_applicationserver.png.9f82b944fea2f0f3bf037521753d0254.png"> </a>
</p>

<p>
	يمكن التوقف هنا عند هذه الخطوة والمتابعة بمفردنا، لكن سنخصص الخطوة التالية للتحدث عن جودة الشيفرة لأهميتها.
</p>

<h2 id="5codetestingcoverage">
	5. اختبار تغطية الشيفرة Code Testing Coverage
</h2>

<p>
	قد يكون اختبار الشيفرة أمرًا مرهقًا ولكنه ضروري ويجب إجراؤه باكرًا لاكتشاف الأخطاء ورفع جودة الشيفرة وضمان رضا العملاء؛ ولكن توجد أدوات عدة مفتوحة المصدر لاختبار الشيفرة وتحسين جودتها, ويمكن توصيل معظم أدوات التكامل والنشر المستمر CI/CD مع أدوات الاختبار وأتمتة العملية.
</p>

<h3 id="codetestframeworks">
	أُطُر اختبار الشيفرة البرمجية Code Test Frameworks
</h3>

<p>
	هناك نوعان من أدوات اختبار الشيفرة، هما:
</p>

<ol>
	<li>
		أُطُر اختبار الشيفرة البرمجية Code test framework: وتساعد في كتابة الاختبارات وتشغيلها
	</li>
	<li>
		أدوات تحسين الجودة Code Quality Suggestion Tools: وتقدم اقتراحات تساعد على تحسين جودة الشيفرة البرمجية
	</li>
</ol>

<p>
	في الآتي جدول مكون من أهم أُطُر الاختبار:
</p>

<table>
	<thead>
		<tr>
			<th>
				إطار العمل
			</th>
			<th>
				الرخصة
			</th>
			<th>
				لغة البرمجة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<a href="https://junit.org/junit5" rel="external nofollow">JUnit</a>
			</td>
			<td>
				Eclipse Public License
			</td>
			<td>
				جافا
			</td>
		</tr>
		<tr>
			<td>
				<a href="http://easymock.org/" rel="external nofollow">EasyMock</a>
			</td>
			<td>
				أباتشي Apache
			</td>
			<td>
				جافا
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://docs.pytest.org/" rel="external nofollow">Pytest</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				Python
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://github.com/tox-dev/tox" rel="external nofollow">Tox</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				Python
			</td>
		</tr>
	</tbody>
</table>

<p>
	أما عن أدوات تحسين الجودة Code Quality Suggestion Tools، فيمكن ذكر الأدوات الآتية:
</p>

<table>
	<thead>
		<tr>
			<th>
				الأداة
			</th>
			<th>
				الرخصة
			</th>
			<th>
				لغة البرمجة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<a href="http://cobertura.github.io/cobertura" rel="external nofollow">Cobertura</a>
			</td>
			<td>
				GNU
			</td>
			<td>
				جافا
			</td>
		</tr>
		<tr>
			<td>
				<a href="http://emma.sourceforge.net/" rel="external nofollow">Emma</a>
			</td>
			<td>
				common Public License
			</td>
			<td>
				جافا
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://github.com/nedbat/coveragepy" rel="external nofollow">Coverage.py</a>
			</td>
			<td>
				Apache 2.0
			</td>
			<td>
				بايثون
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://github.com/tox-dev/tox" rel="external nofollow">Tox</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				Python
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://jasmine.github.io/" rel="external nofollow">Jasmine</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				جافا سكربت
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://jestjs.io/" rel="external nofollow">Jest</a>
			</td>
			<td>
				MIT
			</td>
			<td>
				جافا سكربت
			</td>
		</tr>
	</tbody>
</table>

<p>
	وكما نلاحظ، معظم الأدوات والأطر التي ذكرناها مكتوبة بلغة جافا، أو بايثون، أو جافا سكربت؛ أما لغتا C++‎ وC#‎ فهما من لغات البرمجة الخاصة Proprietary لكن المُترجِم المستخدم لترجمتهما <strong>GCC</strong> مفتوح المصدر.
</p>

<p>
	والآن بعد تطبيق أدوات اختبار التغطية، يجب أن يكون لدينا منهجية عمل DevOps تشبه المنهجية الموضحة في بداية مقالنا.
</p>

<h2 id="">
	خطوات إضافية لتحسين المنهجية
</h2>

<p>
	سنوضح فيما يلي بعض الخطوات الإضافية التي يمكن القيام بها لتحسين منهجية عمل DevOps الخاصة بنا.
</p>

<h3 id="containers">
	استخدام الحاويات Containers
</h3>

<p>
	ذكرنا سابقًا أن بإمكاننا استضافة خادم التطبيقات على آلة افتراضية أو خادم، كما يمكن استخدام <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-r641/" rel="">الحاويات containers</a> أيضًا.
</p>

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

<p>
	توجد خيارات مختلفة للحاويات، لكن <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AF%D9%88%D9%83%D8%B1-docker-r607/" rel="">دوكر Docker</a> وكوبرنيتس Kubernetes هما الأكثر شيوعًا.
</p>

<table>
	<thead>
		<tr>
			<th>
				الأداة
			</th>
			<th>
				الرخصة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<a href="https://www.docker.com/" rel="external nofollow">Docker</a>
			</td>
			<td>
				Apache 2.0
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://kubernetes.io/" rel="external nofollow">Kubernetes</a>
			</td>
			<td>
				Apache 2.0
			</td>
		</tr>
	</tbody>
</table>

<p style="text-align: center;">
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="487" id="ips_uid_9479_5" referrerpolicy="strict-origin-when-cross-origin" src="https://academy.hsoub.com/applications/core/interface/index.html" title="ما هو كوبرنيتس Kubernetes" width="866" data-embed-src="https://www.youtube.com/embed/OQVZ_jyZwh4"></iframe>
</p>

<h3 id="middlewareautomationtools">
	أدوات الأتمتة الوسيطة Middleware automation tools
</h3>

<p>
	تركز منهجية عمل DevOps التي نفّذناه على إنشاء تطبيق ونشره بصورة تعاونية، ولكن هناك أمور أخرى عدّة يمكننا تنفيذها باستخدام أدوات DevOps، وأحدها هو الاستفادة من أدوات إدارة البنية التحتية باستخدام الشيفرة Infrastructure as Code واختصارًا IaC، والتي تُعرف أيضًا باسم أدوات الأتمتة الوسيطة.
</p>

<p>
	تساعد هذه الأدوات في أتمتة عمليات التثبيت والإدارة ومهام البرامج الوسيطة، حيث يمكن لأداة الأتمتة مثلًا أن تسحب الاعدادات المناسبة للتطبيقات، مثل خادم تطبيقات الويب وقواعد البيانات وأدوات المراقبة، ونشرها على خادم التطبيقات.
</p>

<p>
	وفيما يلي بعض أدوات الأتمتة الوسيطة مفتوحة المصدر:
</p>

<table>
	<thead>
		<tr>
			<th>
				الأداة
			</th>
			<th>
				الرخصة
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				<a href="https://www.saltstack.com/" rel="external nofollow">SaltStack</a>
			</td>
			<td>
				Apache 2.0
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://www.chef.io/" rel="external nofollow">Chef</a>
			</td>
			<td>
				Apache 2.0
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://www.ansible.com/" rel="external nofollow">Ansible</a>
			</td>
			<td>
				GNU Public
			</td>
		</tr>
		<tr>
			<td>
				<a href="https://puppet.com/" rel="external nofollow">Puppet</a>
			</td>
			<td>
				Apache أو GPL
			</td>
		</tr>
	</tbody>
</table>

<h2 id="-1">
	الخلاصة
</h2>

<p>
	تعلّمنا في هذا المقال كيفية إنشاء منهجية عمل لإنتاج البرمجيات بأسلوب DevOps باستخدام أدوات مفتوحة المصدر، وتعرفنا على أشهر أدوات التكامل والنشر المستمر CI/CD مفتوحة المصدر وأدوات إدارة الشيفرة المصدرية واختبارها، ويمكنك من الآن بدء تجربة إنشاء منهجية DevOps لمشروعك البرمجي الحقيقي.
</p>

<p>
	ترجمة، وبتصرّف، للمقال <a href="https://opensource.com/article/19/4/devops-pipeline" rel="external nofollow">A beginner's guide to building DevOps pipelines with open source tools</a> لكاتبه Bryant Son.
</p>

<h2 id="-2">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/general/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%81%D9%8A-%D9%85%D8%AC%D8%A7%D9%84-devops-%D9%88%D8%A3%D9%87%D9%85-%D9%85%D9%85%D8%A7%D8%B1%D8%B3%D8%A7%D8%AA%D9%87-%D9%88%D8%A3%D8%AF%D9%88%D8%A7%D8%AA%D9%87-r844/" rel="">دليل المبتدئين في مجال DevOps وأهم ممارساته وأدواته</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/general/%D8%AA%D8%B9%D9%84%D9%85-devops/" rel="">المدخل الشامل لتعلم DevOps</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/general/%D9%85%D8%A7-%D9%87%D9%8A-%D8%A7%D9%84%D8%BA%D8%A7%D9%8A%D8%A9-%D9%85%D9%86-devops%D8%9F-r414/" rel="">ما هي الغاية من DevOps؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/general/%D8%A3%D9%81%D8%B6%D9%84-5-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D9%80-devops-r416/" rel="">أفضل 5 لغات برمجة لـ DevOps</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">846</guid><pubDate>Fri, 27 Jun 2025 16:04:02 +0000</pubDate></item><item><title>&#x625;&#x646;&#x634;&#x627;&#x621; &#x62D;&#x627;&#x648;&#x64A;&#x629; &#x62F;&#x648;&#x643;&#x631; &#x644;&#x62A;&#x637;&#x628;&#x64A;&#x642; &#x631;&#x64A;&#x622;&#x643;&#x62A; &#x648;&#x631;&#x641;&#x639;&#x647;&#x627; &#x625;&#x644;&#x649; &#x633;&#x62C;&#x644; &#x627;&#x644;&#x62D;&#x627;&#x648;&#x64A;&#x627;&#x62A; &#x641;&#x64A; DigitalOcean</title><link>https://academy.hsoub.com/devops/cloud-computing/docker/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-%D8%AF%D9%88%D9%83%D8%B1-%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%B1%D9%8A%D8%A2%D9%83%D8%AA-%D9%88%D8%B1%D9%81%D8%B9%D9%87%D8%A7-%D8%A5%D9%84%D9%89-%D8%B3%D8%AC%D9%84-%D8%A7%D9%84%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-digitalocean-r845/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_05/DigitalOcean.png.3d0c52fe8a8f73328885cc7b9d14a0f5.png" /></p>
<p>
	أَحدَثَت مشاريع الحوسبة السحابية الأصيلة Cloud-native Computing تغييرًا كبيرًا في طريقة بناء التطبيقات ونشرها، ووفرت وسائل متنوعة تُسَهِّل العمل، بدايةً من أدوات التكامل Integrating وتحزيم Packaging شيفرة التطبيق تمهيدًا لنشره، ووصولًا إلى أدوات التوسعة Scaling وغير ذلك.
</p>

<p>
	تندرج هذه الأعمال جميعها تحت مظلة DevOps وتُعدّ الحاويات في عصرنا الحالي أشهر مفاهيم DevOps الحديث وأكثرها تأثيرًا، وسنعرض في مقالنا هذا مثالًا عمليًّا عن نشر تطبيق ريآكت React بمساعدة الحاويات؛ إذ سننشئ صورة دوكر Docker للتطبيق ثم نرفعها إلى سجل الحاويات Container Registry وننشرها باستخدام خادم Droplet من منصة DigitalOcean.
</p>

<h2 id="">
	متطلبات العمل
</h2>

<p>
	من أجل إنشاء حاوية دوكر لتطبيق <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%A7-%D9%87%D9%8A-react%D8%9F-r773/" rel="">ريآكت</a> ورفعها إلى سجل الحاويات في DigitalOcean، سنحتاج إلى المتطلبات التالية:
</p>

<ul>
	<li>
		إنشاء حساب على منصة <a href="https://cloud.digitalocean.com/registrations/new" rel="external nofollow">DigitalOcean</a>.
	</li>
	<li>
		تثبيت كل من <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AF%D9%88%D9%83%D8%B1-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D8%B9%D9%84%D9%89-%D8%AF%D8%A8%D9%8A%D8%A7%D9%86-r465/" rel="">دوكر Docker</a> و<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-%D9%81%D9%8A-nodejs-r1465/" rel="">مدير الحزم npm</a> على جهازننا
	</li>
	<li>
		تشغيل خادم Droplet على DigitalOcean
	</li>
	<li>
		إعداد <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%B3%D8%AC%D9%84-%D8%AF%D9%88%D9%83%D8%B1-docker-registry-%D8%AE%D8%A7%D8%B5-%D8%B9%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-2204-r814/" rel="">سجل الحاويات</a> على DigitalOcean
	</li>
</ul>

<h3 id="docker">
	تثبيت Docker
</h3>

<p>
	تساعدنا <a href="https://docs.docker.com/get-docker/" rel="external nofollow">توثيقات دوكر</a> و<a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%AF%D9%88%D9%83%D8%B1-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87-%D8%B9%D9%84%D9%89-%D8%AF%D8%A8%D9%8A%D8%A7%D9%86-r465/" rel="">هذا الدليل</a> على تثبيت دوكر على الجهاز. وعند إتمام التثبيت، سنستخدم الأمر <code>docker --version</code> للاستعلام عن إصدار دوكر والتحقق من نجاح تثبيته، وذلك وفق التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_12" style=""><span class="pln">docker </span><span class="pun">--</span><span class="pln">version
</span><span class="typ">Docker</span><span class="pln"> version </span><span class="lit">26.0</span><span class="pun">.</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> build </span><span class="lit">2ae903e</span></pre>

<h2 id="-1">
	إنشاء تطبيق ريآكت
</h2>

<p>
	خطوتنا التالية هي تشغيل <a href="https://academy.hsoub.com/programming/javascript/react/" rel="">تطبيق ريآكت React</a>، فإذا كان التطبيق الذي نخطط لاستخدامه موجودًا على الجهاز المحلي، فلا بد من تشغيله مباشرةً. وإذا كان موجودًا على <a href="https://academy.hsoub.com/programming/workflow/git/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%B7%D9%84%D8%A8-%D8%B3%D8%AD%D8%A8-%D8%B9%D9%84%D9%89-github-r1581/" rel="">GitHub</a>، فيمكن استنساخ مستودع التطبيق إلى الجهاز مباشرةً؛ أما في حال لم نكن نمتلك تطبيقًا، فلا بد من إنشاء واحد باستخدام <a href="https://vitejs.dev/" rel="external nofollow">Vite</a> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_10" style=""><span class="pln">npm create vite@latest react</span><span class="pun">-</span><span class="pln">app </span><span class="pun">--</span><span class="pln"> </span><span class="pun">--</span><span class="pln">template react</span></pre>

<p>
	سيُنشئ هذا الأمر تطبيقًا تجريبيًّا ضمن المجلد <code>react-app</code>، وبعدها يمكن الانتقال للمجلد وتشغيل التطبيق بواسطة الأوامر التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_16" style=""><span class="pln">cd react</span><span class="pun">-</span><span class="pln">app
npm install
npm run dev</span></pre>

<p>
	وعند الانتهاء من التشغيل سنحصل على رسالة شبيهة بالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_18" style=""><span class="typ">OutputVITE</span><span class="pln"> v5</span><span class="pun">.</span><span class="lit">2.11</span><span class="pln">  ready in </span><span class="lit">712</span><span class="pln"> ms
</span><span class="pun">➜</span><span class="pln">  </span><span class="typ">Local</span><span class="pun">:</span><span class="pln">   http</span><span class="pun">:</span><span class="com">//localhost:5173/</span><span class="pln">
</span><span class="pun">➜</span><span class="pln">  </span><span class="typ">Network</span><span class="pun">:</span><span class="pln"> use </span><span class="pun">--</span><span class="pln">host to expose
</span><span class="pun">➜</span><span class="pln">  press h </span><span class="pun">+</span><span class="pln"> enter to show help</span></pre>

<p>
	يمكن الآن فتح التطبيق من المتصفح بكتابة العنوان <code>localhost:5173</code> كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172396" href="https://academy.hsoub.com/uploads/monthly_2025_05/img01-vite-react-app.png.056d19484085845db300f3e07c9b3461.png" rel=""><img alt="img01 vite react app" class="ipsImage ipsImage_thumbnailed" data-fileid="172396" data-unique="014kp20g4" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img01-vite-react-app.thumb.png.4377e109ad786cbe69d2d70c82280d2e.png"> </a>
</p>

<p>
	بهذا يكون تطبيقنا قد أصبح جاهزًا الآن، ونستطيع الانتقال للخطوة التالية لنبدأ بتحزيمه ضمن صورة Docker، ثم نشره في بيئة الإنتاج.
</p>

<h2 id="dockerfile">
	إنشاء الملف Dockerfile
</h2>

<p>
	يساعدنا دوكر Docker على تغليف شيفرة تطبيقنا ضمن حاوية وتشغيله، وذلك باستخدام صور دوكر وحاويات دوكر.
</p>

<p>
	تُنشَئ صور دوكر باستخدام ملف خاص يدعى <code>Dockerfile</code> يتضمن التعليمات البرمجية اللازمة لبناء الصورة؛ إذ تتكون الصورة image من عدة طبقات layers للقراءة فقط. وعند إضافة أي تعليمة إلى الملف <code>Dockerfile</code>، تُضَاف طبقة جديدة إلى الصورة.
</p>

<p>
	تُخَزَّن هذه الطبقات بهيئة قيم مُعَمَّاة hash من نوع <code>SHA-256</code>، ويمكن معرفة المزيد عن بنية صور الحاويات بمطالعة مقال <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%85%D8%A7-%D9%87%D9%8A-%D8%B5%D9%88%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-container-image%D8%9F-r587/" rel="">ما هي صورة الحاوية container image؟</a>
</p>

<p>
	إذًا تُبنى صور دوكر بإنشاء الملف <code>Dockerfile</code> ويشمل ذلك خطوتين أساسيتين:
</p>

<ul>
	<li>
		بناء التطبيق
	</li>
	<li>
		إعداد خادم <code>nginx</code> وتشغيله لخدمة التطبيق
	</li>
</ul>

<p>
	<strong>تنويه</strong>: ستُكتَب جميع التعليمات التي ننفذها في هاتين الخطوتين ضمن الملف <code>Dockerfile</code>.
</p>

<h3 id="1">
	1. بناء التطبيق
</h3>

<p>
	سنوجه Docker في البداية ليستخدم أحدث نسخة من <a href="https://academy.hsoub.com/programming/javascript/nodejs/" rel="">Node.js</a> لتكون الركيزة التي سيبني عليها تطبيق React وفق التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_20" style=""><span class="pln">FROM node</span><span class="pun">:</span><span class="pln">latest AS builder</span></pre>

<p>
	بعد ذلك سنحَدِّد مجلد العمل <code>WORKDIR</code> الذي ستُنَفَّذ فيه جميع التعليمات اللاحقة، والذي سيكتمل فيه تطبيقنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_22" style=""><span class="pln">WORKDIR </span><span class="pun">/</span><span class="pln">app</span></pre>

<p>
	سننسخ بعدها الملف <code>package.json</code> إلى الحاوية <code>builder</code> ونشَغِّل <code>npm install</code> لتثبيت جميع الاعتماديات المطلوبة في الملف <code>package.json</code> وبذلك نضمن أن <a href="https://academy.hsoub.com/programming/javascript/react/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-%D8%A8%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D8%AE%D8%A7%D8%AF%D9%85-graphql-r1210/" rel="">تطبيق React</a> يمتلك كل ما يحتاجه ليعمل بصورة سليمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_26" style=""><span class="pln">COPY package</span><span class="pun">.</span><span class="pln">json </span><span class="pun">.</span><span class="pln">
RUN npm install</span></pre>

<p>
	والآن. يمكن نسخ بقية ملفات المشروع إلى الحاوية وتشغيل <code>npm run build</code> لتصريف compile التطبيق وفق الأمر التالي الذي سيُحَوِّل الشيفرة المصدرية لتطبيقنا إلى حزمة bundle جاهزة لبيئة الإنتاج ومُخَزَّنة في المجلد <code>/dist</code> ومُضبوطة لتعمل بكفاءة وأداء جيد:
</p>

<pre class="ipsCode">COPY . ./
RUN npm run build
</pre>

<h3 id="2nginx">
	2. إعداد خادم <code>nginx</code> للتطبيق وتشغيله
</h3>

<p>
	يشتهر <a href="https://academy.hsoub.com/devops/servers/web/nginx/" rel="">NGINX</a> بالكفاءة والسرعة، لذا يُعدّ خيارًا مناسبًا للاستخدام كخادم ويب لتطبيقنا حتى يصله بالمستخدمين، وسنعتمد لبنائه أحدث صورة <code>nginx</code> متوفرة؛ ثم نستبدل ملف الإعدادات الافتراضي الذي يأتي مع الصورة <code>default.conf</code> بملف إعدادات مشروعنا ليتمكن خادم <code>nginx</code> من تلبية طلبات المستخدمين الواردة إلى التطبيق.
</p>

<p>
	وذلك وفق الأوامر التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_28" style=""><span class="pln">FROM nginx</span><span class="pun">:</span><span class="pln">latest
RUN rm </span><span class="pun">-</span><span class="pln">rf </span><span class="pun">/</span><span class="pln">etc</span><span class="pun">/</span><span class="pln">nginx</span><span class="pun">/</span><span class="pln">conf</span><span class="pun">.</span><span class="pln">d</span><span class="pun">/</span><span class="kwd">default</span><span class="pun">.</span><span class="pln">conf
COPY </span><span class="pun">./</span><span class="pln">nginx</span><span class="pun">/</span><span class="kwd">default</span><span class="pun">.</span><span class="pln">conf </span><span class="pun">/</span><span class="pln">etc</span><span class="pun">/</span><span class="pln">nginx</span><span class="pun">/</span><span class="pln">conf</span><span class="pun">.</span><span class="pln">d</span><span class="pun">/</span><span class="kwd">default</span><span class="pun">.</span><span class="pln">conf</span></pre>

<p>
	سننسخ بعد ذلك تطبيق ريآكت الذي حوّلناه إلى حزمة في الخطوة السابقة، وذلك من المجلد <code>dist</code> في الحاوية <code>builder</code> إلى المجلد الذي يتوقع <code>nginx</code> أن يجد ملفات تطبيقنا فيه، ليستطيع خدمة المستخدمين، وذلك وفق الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_30" style=""><span class="pln">COPY </span><span class="pun">--</span><span class="pln">from</span><span class="pun">=</span><span class="pln">builder </span><span class="pun">/</span><span class="pln">app</span><span class="pun">/</span><span class="pln">dist </span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="pln">share</span><span class="pun">/</span><span class="pln">nginx</span><span class="pun">/</span><span class="pln">html</span></pre>

<p>
	سنضبط بعد ذلك قيمة مجلد العمل <code>WORKDIR</code> داخل حاوية <code>nginx</code> ليشير إلى مجلد التطبيق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_32" style=""><span class="pln">WORKDIR </span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="pln">share</span><span class="pun">/</span><span class="pln">nginx</span><span class="pun">/</span><span class="pln">html</span></pre>

<p>
	ثم نشَغِّل خادم <code>nginx</code> بواسطة الأمر التالي، الذي يوجه Docker لبدء تشغيله والحفاظ عليه في حالة عمل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_35" style=""><span class="pln">CMD </span><span class="pun">[</span><span class="str">"/bin/bash"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"-c"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"nginx -g "</span><span class="pln">daemon off</span><span class="pun">;</span><span class="str">""</span><span class="pun">]</span></pre>

<p>
	إذا جمعنا كل التعليمات السابقة معًا، فستشكل الملف <code>Dockerfile</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_37" style=""><span class="pun">#</span><span class="pln"> </span><span class="pun">------------------------</span><span class="pln">
</span><span class="pun">#</span><span class="pln"> </span><span class="typ">Step</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Build</span><span class="pln"> react app
</span><span class="pun">#</span><span class="pln"> </span><span class="pun">------------------------</span><span class="pln">

</span><span class="pun">#</span><span class="pln"> </span><span class="typ">Use</span><span class="pln"> node</span><span class="pun">:</span><span class="pln">latest as the builder image
FROM node</span><span class="pun">:</span><span class="pln">latest AS builder

</span><span class="pun">#</span><span class="pln"> </span><span class="typ">Set</span><span class="pln"> the working directory
WORKDIR </span><span class="pun">/</span><span class="pln">app

</span><span class="pun">#</span><span class="pln"> </span><span class="typ">Copy</span><span class="pln"> package</span><span class="pun">.</span><span class="pln">json and install app dependencies
COPY package</span><span class="pun">.</span><span class="pln">json </span><span class="pun">.</span><span class="pln">
RUN npm install

</span><span class="pun">#</span><span class="pln"> </span><span class="typ">Copy</span><span class="pln"> other project files and build
COPY </span><span class="pun">.</span><span class="pln"> </span><span class="pun">./</span><span class="pln">
RUN npm run build

</span><span class="pun">#</span><span class="pln"> </span><span class="pun">--------------------------------------</span><span class="pln">
</span><span class="pun">#</span><span class="pln"> </span><span class="typ">Step</span><span class="pln"> </span><span class="lit">2</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Set</span><span class="pln"> up nginx to serve the app
</span><span class="pun">#</span><span class="pln"> </span><span class="pun">--------------------------------------</span><span class="pln">
</span><span class="pun">#</span><span class="pln"> </span><span class="typ">Use</span><span class="pln"> nginx</span><span class="pun">:</span><span class="pln">latest as the base image
FROM nginx</span><span class="pun">:</span><span class="pln">latest

</span><span class="pun">#</span><span class="pln"> </span><span class="typ">Overwriting</span><span class="pln"> nginx config </span><span class="kwd">with</span><span class="pln"> our own config file
RUN rm </span><span class="pun">-</span><span class="pln">rf </span><span class="pun">/</span><span class="pln">etc</span><span class="pun">/</span><span class="pln">nginx</span><span class="pun">/</span><span class="pln">conf</span><span class="pun">.</span><span class="pln">d</span><span class="pun">/</span><span class="kwd">default</span><span class="pun">.</span><span class="pln">conf
COPY </span><span class="pun">./</span><span class="pln">nginx</span><span class="pun">/</span><span class="kwd">default</span><span class="pun">.</span><span class="pln">conf </span><span class="pun">/</span><span class="pln">etc</span><span class="pun">/</span><span class="pln">nginx</span><span class="pun">/</span><span class="pln">conf</span><span class="pun">.</span><span class="pln">d</span><span class="pun">/</span><span class="kwd">default</span><span class="pun">.</span><span class="pln">conf

</span><span class="pun">#</span><span class="pln"> </span><span class="typ">Copy</span><span class="pln"> over the build created in the </span><span class="typ">Step</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
COPY </span><span class="pun">--</span><span class="pln">from</span><span class="pun">=</span><span class="pln">builder </span><span class="pun">/</span><span class="pln">app</span><span class="pun">/</span><span class="pln">dist </span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="pln">share</span><span class="pun">/</span><span class="pln">nginx</span><span class="pun">/</span><span class="pln">html

</span><span class="pun">#</span><span class="pln"> </span><span class="typ">Set</span><span class="pln"> the working directory
WORKDIR </span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="pln">share</span><span class="pun">/</span><span class="pln">nginx</span><span class="pun">/</span><span class="pln">html

</span><span class="pun">#</span><span class="pln"> </span><span class="typ">Start</span><span class="pln"> nginx server
CMD </span><span class="pun">[</span><span class="str">"/bin/bash"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"-c"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"nginx -g \"daemon off;\""</span><span class="pun">]</span></pre>

<p>
	لذا سننشئ ملفًا نصيًّا باسم <code>Dockerfile</code> ضمن المجلد الجذر لتطبيقنا كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_39" style=""><span class="pln">touch </span><span class="typ">Dockerfile</span></pre>

<p>
	مع نسخ التعليمات السابقة ضمنه.
</p>

<h2 id="nginx">
	إنشاء ملف إعدادات NGINX
</h2>

<p>
	ذكرنا في الخطوة السابقة الخاصة بإعداد <a href="https://academy.hsoub.com/devops/servers/web/nginx/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-nginx-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r434/" rel="">خادم NGINX</a> أننا سنستبدل ملف الإعدادات الافتراضي للخادم بملف إعدادات مشروعنا، فكيف ننشئ ملف إعدادات المشروع؟
</p>

<p>
	سنتوجه في البداية إلى مجلد الجذر لتطبيقنا ثم ننشئ بداخله مجلدًا جديدًا باسم <code>nginx</code>، وننشئ بداخله ملفًا نصيًّا يدعى <code>default.conf</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_41" style=""><span class="pln">mkdir nginx
cd nginx </span><span class="pun">&amp;&amp;</span><span class="pln"> touch </span><span class="kwd">default</span><span class="pun">.</span><span class="pln">conf</span></pre>

<p>
	ثم ننسخ الإعدادات التالية والصقها ضمنه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_43" style=""><span class="pln">server </span><span class="pun">{</span><span class="pln">
  listen </span><span class="lit">80</span><span class="pun">;</span><span class="pln">
  add_header </span><span class="typ">Cache</span><span class="pun">-</span><span class="typ">Control</span><span class="pln"> no</span><span class="pun">-</span><span class="pln">cache</span><span class="pun">;</span><span class="pln">
  location </span><span class="pun">/</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    root   </span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="pln">share</span><span class="pun">/</span><span class="pln">nginx</span><span class="pun">/</span><span class="pln">html</span><span class="pun">;</span><span class="pln">
    index  index</span><span class="pun">.</span><span class="pln">html index</span><span class="pun">.</span><span class="pln">htm</span><span class="pun">;</span><span class="pln">
    try_files $uri $uri</span><span class="pun">/</span><span class="pln"> </span><span class="pun">/</span><span class="pln">index</span><span class="pun">.</span><span class="pln">html</span><span class="pun">;</span><span class="pln">
    expires </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  error_page   </span><span class="lit">500</span><span class="pln"> </span><span class="lit">502</span><span class="pln"> </span><span class="lit">503</span><span class="pln"> </span><span class="lit">504</span><span class="pln">  </span><span class="pun">/</span><span class="lit">50x</span><span class="pun">.</span><span class="pln">html</span><span class="pun">;</span><span class="pln">
  location </span><span class="pun">=</span><span class="pln"> </span><span class="pun">/</span><span class="lit">50x</span><span class="pun">.</span><span class="pln">html </span><span class="pun">{</span><span class="pln">
    root   </span><span class="pun">/</span><span class="pln">usr</span><span class="pun">/</span><span class="pln">share</span><span class="pun">/</span><span class="pln">nginx</span><span class="pun">/</span><span class="pln">html</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُحَدِّد هذه الإعدادات البوابة التي سيستقبل خادم <code>nginx</code> الطلبات عبرها، وهي البوابة <code>80</code>، وأيضًا الملفات التي سيتعامل معها، بالإضافة إلى الأخطاء الافتراضية التي قد تعترضه مصنفةً حسب رمز الخطأ. وفي هذا المجال، يُنصح بالاطلاع على مقال <a href="https://academy.hsoub.com/devops/servers/web/nginx/%D9%81%D9%87%D9%85-%D8%A8%D9%86%D9%8A%D8%A9-%D9%85%D9%84%D9%81-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-nginx-%D9%88%D8%B3%D9%8A%D8%A7%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D8%A7%D8%AA-r112/" rel="">فهم بنية ملف إعدادات nginx وسياقات الإعدادات</a>، كما توفر DigitalOcean لمستخدميها <a href="https://www.digitalocean.com/community/tools/nginx?domains.0.php.php=false&amp;domains.0.routing.index=index.html&amp;domains.0.routing.fallbackHtml=true" rel="external nofollow">أداة رسومية</a> تساعدهم في إنشاء ملف إعدادات nginx.
</p>

<h2 id="image">
	بناء صورة التطبيق image
</h2>

<p>
	سنستخدم هننا تعليمة <code>cd</code> للعودة إلى مجلد الجذر لتطبيقنا حيث يوجد الملف <code>Dockerfile</code>، ثم ننفِّذ الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_45" style=""><span class="pln">docker image build </span><span class="pun">-</span><span class="pln">t react</span><span class="pun">-</span><span class="pln">app</span><span class="pun">:</span><span class="pln">v1</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> </span><span class="pun">.</span><span class="pln"> </span><span class="pun">--</span><span class="pln">platform linux</span><span class="pun">/</span><span class="pln">amd64</span></pre>

<p>
	تتميز كل صورة دوكر عن غيرها باسم name ووسم tag خاصين بها يُكتَبان بعد الراية <code>t-</code>؛ فالصورة في مثالنا تدعى <code>react-app</code>، ولها الوسم <code>v1.0</code>؛ أما الراية <code>platform--</code> فتساعدنا على تحديد المنصة التي تُبنى الصور من أجلها، وتُعَدُّ خيارًا مهمًا في الحالات التي نحتاج فيها لبناء صورة متعددة المنصات cross-platform حتى تُحَِّدد الأنظمة التي ستتوافق معها.
</p>

<p>
	سنحصل على نتيجة تشبه التالي بعد الانتهاء من بناء الصورة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_47" style=""><span class="pun">[+]</span><span class="pln"> </span><span class="typ">Building</span><span class="pln"> </span><span class="lit">113.9s</span><span class="pln"> </span><span class="pun">(</span><span class="lit">17</span><span class="pun">/</span><span class="lit">17</span><span class="pun">)</span><span class="pln"> FINISHED
</span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">[</span><span class="pln">internal</span><span class="pun">]</span><span class="pln"> load build definition from </span><span class="typ">Dockerfile</span><span class="pln">
</span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> transferring dockerfile</span><span class="pun">:</span><span class="pln"> </span><span class="lit">982B</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> writing image sha256</span><span class="pun">:</span><span class="pln">d125b102b094224c82ddb69f9ece98c7161c8660fe72fd5aec57e41a6d72cf2f
</span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> naming to docker</span><span class="pun">.</span><span class="pln">io</span><span class="pun">/</span><span class="pln">library</span><span class="pun">/</span><span class="pln">react</span><span class="pun">-</span><span class="pln">app</span><span class="pun">:</span><span class="pln">v1</span><span class="pun">.</span><span class="lit">0</span></pre>

<p>
	يمكن استخدام الأمر التالي الذي يعرض صور دوكر الموجودة لتتحقق من صحة بناء الصورة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_49" style=""><span class="pln">docker image list</span></pre>

<p>
	وسنحصل على النتيجة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_51" style=""><span class="typ">OutputREPOSITORY</span><span class="pln">   TAG   IMAGE ID       CREATED         SIZE
react</span><span class="pun">-</span><span class="pln">app    v1</span><span class="pun">.</span><span class="lit">0</span><span class="pln">  d125b102b094   </span><span class="lit">6</span><span class="pln"> minutes ago   </span><span class="lit">188MB</span></pre>

<p>
	وكما نلاحظ، فصورة التطبيق التي بنيناها ضمن قائمة الصور الموجودة.
</p>

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

<p>
	لننتقل الآن إلى الخطوة التالية وهي تخزين الصورة في مكان مركزي يُسَهِّل توزيعها ونشرها، وهنا يأتي دور سجل الحاويات Container Registry.
</p>

<h2 id="-2">
	رفع صورة التطبيق إلى سجل الحاويات
</h2>

<p>
	سجل الحاويات Container Registry هو مخزون مركزي من صور الحاويات، يمكن رفع صورنا إليه وسحبها منه عندما نحتاجها من أي مكان وفي أي وقت، وذلك بعدة طرق. إما محليًّا، أو عبر عنقود <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%85%D8%A7-%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D8%AF%D9%88%D9%83%D8%B1-docker-%D9%88%D9%83%D9%88%D8%A8%D9%8A%D8%B1%D9%86%D9%8A%D8%AA%D9%8A%D8%B3-kubernetes%D8%9F-r612/" rel="">Kubernetes</a>، أو عبر خطوط أنابيب <a href="https://academy.hsoub.com/devops/deployment/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D9%83%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-%D9%88%D8%A7%D9%84%D9%86%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-cicd-r644/" rel="">CI/CD</a>، أو غير ذلك.
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="603" id="ips_uid_7517_5" referrerpolicy="strict-origin-when-cross-origin" src="https://academy.hsoub.com/applications/core/interface/index.html" title="ما هو التكامل المستمر والنشر المستمر CI/CD" width="930" data-embed-src="https://www.youtube.com/embed/hFzSG9qNWWs"></iframe>
</p>

<p>
	لنبدأ الآن خطوات رفع push الصورة <code>react-app</code> إلى السجل.
</p>

<p>
	لرفع صورنا إلى السجل ينبغي امتلاك سجل خاص أو أن نستخدم سجلًا عامًا مثل Docker Hub أو خدمة أخرى نحو <a href="https://www.digitalocean.com/products/container-registry" rel="external nofollow">سجل الحاويات لمنصة ديجيتال أوشن</a> كما فعلنا في هذا المثال، والذي يمكننا استخدامه بالمجان للمشاريع الصغيرة؛ إذ يمكننا حجز مستودع واحد بمساحة تخزينية لا تتجاو 500 ميجا بايت، وهذا يناسب في الواقع يناسب الصورة الوحيدة التي سنرفعها إلى السجل.
</p>

<p>
	يمكننا البدء إذًا بإعداد سجل الحاويات المجاني free-tier registry، واختيار أحد مواقع مراكز البيانات في المنطقة، مع تسمية السجل باسم مناسب مثل <code>my-container-registry</code>، كما هو الحال في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172391" href="https://academy.hsoub.com/uploads/monthly_2025_05/img02-docker-registry.png.a3924d6850528298710f0b9156f9c3ae.png" rel=""><img alt="img02 docker registry" class="ipsImage ipsImage_thumbnailed" data-fileid="172391" data-unique="7sg7omq6v" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img02-docker-registry.thumb.png.b79a88ebf37d59b16f0f6625850c7142.png"> </a>
</p>

<p>
	<strong>تنويه</strong>: لا بد من توفر حساب خاص على منصة ديجيتال أوشن لاستخدام سجل الحاويات، ويمكن الاسترشاد <a href="https://docs.digitalocean.com/products/container-registry/getting-started/quickstart/#create-a-registry" rel="external nofollow">بدليل فتح الحساب</a> لمزيدٍ من المعلومات عن هذه الخدمة.
</p>

<p>
	سنحتاج الآن لمفتاح <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%8A-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-api%D8%9F-r1512/" rel="">الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> للاتصال بالسجل، لذا سنتوجه إلى تبويب <strong>tokens</strong> من الصفحة الرئيسية لحسابنا على المنصة وننشئ مفتاحًا جديدًا خاصًا بنا كما هو الحال في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172395" href="https://academy.hsoub.com/uploads/monthly_2025_05/img03-api-token-doker-registry.png.cd4fa337aace24307ce3964717c29133.png" rel=""><img alt="img03 api token doker registry" class="ipsImage ipsImage_thumbnailed" data-fileid="172395" data-unique="rssxb6in9" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img03-api-token-doker-registry.thumb.png.08c26d36a2b369e7ff3f9dff780f91da.png"> </a>
</p>

<p>
	سنملأ الآن النموذج الظاهر أمامنا بعد الضغط على زر <strong>إنشاء رمز جديد Generate New Token</strong> ونفَعِّل خيار التحكم الكامل Full Access طالما أنه لن يكون هناك أكثر من مستخدم وحيد للرمز، إذ لا توجد أي مخاطر أمنية.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172397" href="https://academy.hsoub.com/uploads/monthly_2025_05/img04-gnerate-token.png.5939a3bf2589362741f6194c10a5e739.png" rel=""><img alt="img04 gnerate token" class="ipsImage ipsImage_thumbnailed" data-fileid="172397" data-unique="6g5lnigqz" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img04-gnerate-token.thumb.png.c84d12f53dcc9d245d7d134acdb06195.png"> </a>
</p>

<p>
	سينشأ الآن الرمز الجديد، وما علينا سوى نسخه والاحتفاظ به في مكانٍ آمن؛ إذ سنحتاجه لاحقًا عند تسجيل الدخول إلى السجل.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172394" href="https://academy.hsoub.com/uploads/monthly_2025_05/img05-new-token.png.40e456cc622c62f70e7751a35345413a.png" rel=""><img alt="img05 new token" class="ipsImage ipsImage_thumbnailed" data-fileid="172394" data-unique="gca3rmgfi" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img05-new-token.thumb.png.e4cca9c7bb9a7c7a793df8ed9d6a6bf5.png"> </a>
</p>

<p>
	والآن يمكننا تسجيل الدخول إلى سجل الحاويات باستخدام بيانات حسابنا، وذلك عبر كتابة الأمر التالي في الطرفية terminal:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_54" style=""><span class="pln">docker login registry</span><span class="pun">.</span><span class="pln">digitalocean</span><span class="pun">.</span><span class="pln">com</span></pre>

<p>
	سيُطلب منا عندها إدخال اسم المستخدم وكلمة المرور:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_56" style=""><span class="typ">OutputUsername</span><span class="pun">:</span><span class="pln"> ravish</span><span class="pun">.</span><span class="pln">foo@bar</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Password</span><span class="pun">:</span><span class="pln">
</span><span class="typ">Login</span><span class="pln"> </span><span class="typ">Succeeded</span></pre>

<p>
	وبإدخالهما تكتمل عملية تسجيل الدخول ويمكن رفع صورة تطبيقنا إلى السجل؛ ويجري ذلك بخطوتين، أولاهما عبر وسم <code>tag</code> الصورة بوسم خاص للرفع كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_58" style=""><span class="pln">docker tag react</span><span class="pun">-</span><span class="pln">app</span><span class="pun">:</span><span class="pln">v1</span><span class="pun">.</span><span class="lit">0</span><span class="pln"> registry</span><span class="pun">.</span><span class="pln">digitalocean</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="pln">my</span><span class="pun">-</span><span class="pln">container</span><span class="pun">-</span><span class="pln">registry</span><span class="pun">/</span><span class="pln">react</span><span class="pun">-</span><span class="pln">app</span></pre>

<p>
	وثانيهما رفع الصورة إلى السجل كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_62" style=""><span class="pln">docker push registry</span><span class="pun">.</span><span class="pln">digitalocean</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="pln">my</span><span class="pun">-</span><span class="pln">container</span><span class="pun">-</span><span class="pln">registry</span><span class="pun">/</span><span class="pln">react</span><span class="pun">-</span><span class="pln">app</span></pre>

<p>
	وسنحصل على خرج يشبه التالي يؤكد لنا نجاح العملية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_60" style=""><span class="typ">OutputUsing</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> tag</span><span class="pun">:</span><span class="pln"> latest
</span><span class="typ">The</span><span class="pln"> push refers to repository </span><span class="pun">[</span><span class="pln">registry</span><span class="pun">.</span><span class="pln">digitalocean</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="pln">my</span><span class="pun">-</span><span class="pln">container</span><span class="pun">-</span><span class="pln">registry</span><span class="pun">/</span><span class="pln">react</span><span class="pun">-</span><span class="pln">app</span><span class="pun">]</span><span class="pln">
</span><span class="lit">5f70bf18a086</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pushed</span><span class="pln">
fafde3127bc5</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pushed</span><span class="pln">
</span><span class="lit">155c640ab606</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pushed</span><span class="pln">
</span><span class="lit">993afd9f06ad</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pushed</span><span class="pln">
</span><span class="lit">9fd54926bcae</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pushed</span><span class="pln">
</span><span class="lit">175aa66db4cc</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pushed</span><span class="pln">
e6380a7057a5</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pushed</span><span class="pln">
</span><span class="lit">1db2242fc1fa</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pushed</span><span class="pln">
b09347a1aec6</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pushed</span><span class="pln">
bbde741e108b</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pushed</span><span class="pln">
</span><span class="lit">52ec5a4316fa</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pushed</span><span class="pln">
latest</span><span class="pun">:</span><span class="pln"> digest</span><span class="pun">:</span><span class="pln"> sha256</span><span class="pun">:</span><span class="lit">227a8c3ede3fc5c81b281228600bec84939f8a4a0cc770fc6e5527b3261e77f4</span><span class="pln"> size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2608</span></pre>

<p>
	بهذا تكون صورة التطبيق أصبحت قد صارت محفوظةً الآن ضمن سجل الحاويات الخاص بنا، وهي متاحة للسحب، وسنراها مباشرةً من لوحة تحكم حسابنا:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172389" href="https://academy.hsoub.com/uploads/monthly_2025_05/img06-container-registry.png.5bbc0df42f163c99e3df6919327a7e9c.png" rel=""><img alt="img06 container registry" class="ipsImage ipsImage_thumbnailed" data-fileid="172389" data-unique="nitqw64d8" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img06-container-registry.thumb.png.6ac284860b6fc56e638b90afc3b702d5.png"> </a>
</p>

<p>
	يمكننا ملاحظة أن الصورة <code>react-app</code> التي رفعناها إلى السجل تحمل الوسم الافتراضي <code>latest</code> لأننا لم نعطها وسمًا خاصًا عند الرفع، فأي صورة تُرفع للسجل دون وسم تُمنح افتراضيًا الوسم <code>latest</code>، ويمكن بالتأكيد تغيير هذا الوسم إلى أي وسم آخر تريده بواسطة خاصية الإصدار Versioning.
</p>

<p>
	فعلى سبيل المثال، يمكن وسم صورة الإصدار الأول من تطبيقنا بالوسم <code>v1.0</code> وعند إجراء تغيرات لاحقة على شيفرة التطبيق نستطيع إعطاء الصورة الجديدة الوسم <code>v1.1</code> ثم <code>v1.4</code> أو <code>v2.0</code>، وهكذا. فننشئ بذلك سجلًا زمنيًا خاصًا بإصدارات تطبيقنا يساعدنا على توثيق تطور التطبيق ويُسَهِّل علينا تتبع إصداراته وإدارتها وخصوصًا على المدى الطويل.
</p>

<p>
	بهذا نكون انتهينا من بناء صورة التطبيق وأصبحت جاهزة على سجل الحاويات، كل ما علينا الآن تشغيل خادم Droplet لنشر deploy الحاوية باستخدام هذه الصورة.
</p>

<h2 id="droplet">
	تشغيل خادم Droplet
</h2>

<p>
	<strong>تنويه</strong>: لقد استخدمنا في هذا المثال خادمًا من ديجيتال أوشن لكن يمكن اختيار الاستضافة المناسبة لتأمين الخادم، كما يمكن الاستعانة بمقال <a href="https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%A3%D9%88%D9%84%D9%8A%D8%A9-%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r431/" rel="">التهيئة الأولية لخادم أوبونتو</a> لتجهيزه.
</p>

<p>
	عمليًّا، ستكفينا الخطة ذات 6$ لبناء خادم Droplet مناسب لنشر الحاوية، وسيساعدنا <a href="https://docs.digitalocean.com/products/droplets/how-to/create/" rel="external nofollow">هذا الدليل</a> على إتمام العملية؛ إذ سنختار في البداية نظام التشغيل وليكن <a href="https://academy.hsoub.com/tags/%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88/" rel="">أوبنتو</a>، وعند اكتمال تشغيل الخادم سيظهر أمامنا في لوحة تحكم حسابنا كما في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172392" href="https://academy.hsoub.com/uploads/monthly_2025_05/img07-droplet-server.png.b66564e8f0925abbee60c1ef50b3b4b6.png" rel=""><img alt="img07 droplet server" class="ipsImage ipsImage_thumbnailed" data-fileid="172392" data-unique="n62c43tmq" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img07-droplet-server.thumb.png.047f2e6c2f929e2c034040f6cd825598.png"> </a>
</p>

<p>
	ننقر على الخادم لرؤية تفاصيله، وننسخ <a href="https://academy.hsoub.com/devops/networking/%D8%B4%D8%A8%D9%83%D8%A9-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%B1%D9%88%D8%AA%D9%88%D9%83%D9%88%D9%84-ip-r497/" rel="">عنوان IP</a> الخاص به، والذي نجده تحت الاسم الخادم كما في الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172390" href="https://academy.hsoub.com/uploads/monthly_2025_05/img08-droplet-ip.png.0c2c7715ad2c355d5bae37625ba453c9.png" rel=""><img alt="img08 droplet ip" class="ipsImage ipsImage_thumbnailed" data-fileid="172390" data-unique="22itfn8tf" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img08-droplet-ip.thumb.png.d479c3aba2def1e6b50dc78f979bc93c.png"> </a>
</p>

<p>
	يمكن الآن فتح <a href="https://academy.hsoub.com/devops/security/ssh/" rel="">جلسة اتصال <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr></a> مع الخادم كما يلي، وذلك باستخدام عنوان IP المخصص له وكلمة المرور لحساب الجذر <code>root</code> التي تُطلب منا أثناء إعداد الخادم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_64" style=""><span class="pln">ssh root@143</span><span class="pun">.</span><span class="lit">244.130</span><span class="pun">.</span><span class="lit">234</span><span class="pln">
root@143</span><span class="pun">.</span><span class="lit">244.130</span><span class="pun">.</span><span class="lit">234</span><span class="str">'s password:</span></pre>

<p>
	يمكن أيضًا اختيار عدم المطالبة بكلمة مرور عند الاتصال مع الخادم ببروتوكول <code><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><a href="https://academy.hsoub.com/devops/security/ssh/%D9%85%D8%A7-%D9%87%D9%8A-%D8%AA%D9%82%D9%86%D9%8A%D8%A9-ssh%D8%9F-r793/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></a></abbr></code> إذا أردنا ذلك.
</p>

<p>
	ستظهر أمامنا رسالة الترحيب التالية بمجرد نجاح الاتصال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_66" style=""><span class="typ">Welcome</span><span class="pln"> to </span><span class="typ">Ubuntu</span><span class="pln"> </span><span class="lit">23.10</span><span class="pln"> </span><span class="pun">(</span><span class="pln">GNU</span><span class="pun">/</span><span class="typ">Linux</span><span class="pln"> </span><span class="lit">6.5</span><span class="pun">.</span><span class="lit">0</span><span class="pun">-</span><span class="lit">9</span><span class="pun">-</span><span class="pln">generic x86_64</span><span class="pun">)</span><span class="pln">

 </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Documentation</span><span class="pun">:</span><span class="pln">  https</span><span class="pun">:</span><span class="com">//help.ubuntu.com</span><span class="pln">
 </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Management</span><span class="pun">:</span><span class="pln">     https</span><span class="pun">:</span><span class="com">//landscape.canonical.com</span><span class="pln">
 </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Support</span><span class="pun">:</span><span class="pln">        https</span><span class="pun">:</span><span class="com">//ubuntu.com/advantage</span><span class="pln">

  </span><span class="typ">System</span><span class="pln"> information as </span><span class="kwd">of</span><span class="pln"> </span><span class="typ">Mon</span><span class="pln"> </span><span class="typ">Apr</span><span class="pln"> </span><span class="lit">29</span><span class="pln"> </span><span class="lit">08</span><span class="pun">:</span><span class="lit">07</span><span class="pun">:</span><span class="lit">53</span><span class="pln"> UTC </span><span class="lit">2024</span><span class="pln">

  </span><span class="typ">System</span><span class="pln"> load</span><span class="pun">:</span><span class="pln">  </span><span class="lit">0.32</span><span class="pln">              </span><span class="typ">Processes</span><span class="pun">:</span><span class="pln">             </span><span class="lit">106</span><span class="pln">
  </span><span class="typ">Usage</span><span class="pln"> </span><span class="kwd">of</span><span class="pln"> </span><span class="pun">/:</span><span class="pln">   </span><span class="lit">9.3</span><span class="pun">%</span><span class="pln"> </span><span class="kwd">of</span><span class="pln"> </span><span class="lit">23.17GB</span><span class="pln">   </span><span class="typ">Users</span><span class="pln"> logged in</span><span class="pun">:</span><span class="pln">       </span><span class="lit">0</span><span class="pln">
  </span><span class="typ">Memory</span><span class="pln"> usage</span><span class="pun">:</span><span class="pln"> </span><span class="lit">40</span><span class="pun">%</span><span class="pln">               </span><span class="typ">IPv4</span><span class="pln"> address </span><span class="kwd">for</span><span class="pln"> eth0</span><span class="pun">:</span><span class="pln"> </span><span class="lit">143.244</span><span class="pun">.</span><span class="lit">130.234</span><span class="pln">
  </span><span class="typ">Swap</span><span class="pln"> usage</span><span class="pun">:</span><span class="pln">   </span><span class="lit">0</span><span class="pun">%</span><span class="pln">                </span><span class="typ">IPv4</span><span class="pln"> address </span><span class="kwd">for</span><span class="pln"> eth0</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10.47</span><span class="pun">.</span><span class="lit">0.5</span><span class="pln">

</span><span class="lit">47</span><span class="pln"> updates can be applied immediately</span><span class="pun">.</span><span class="pln">
</span><span class="typ">To</span><span class="pln"> see these additional updates run</span><span class="pun">:</span><span class="pln"> apt list </span><span class="pun">--</span><span class="pln">upgradable

</span><span class="pun">***</span><span class="pln"> </span><span class="typ">System</span><span class="pln"> restart required </span><span class="pun">***</span><span class="pln">
</span><span class="typ">Last</span><span class="pln"> login</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Mon</span><span class="pln"> </span><span class="typ">Apr</span><span class="pln"> </span><span class="lit">29</span><span class="pln"> </span><span class="lit">08</span><span class="pun">:</span><span class="lit">07</span><span class="pun">:</span><span class="lit">55</span><span class="pln"> </span><span class="lit">2024</span><span class="pln"> from </span><span class="lit">198.211</span><span class="pun">.</span><span class="lit">111.194</span><span class="pln">
root@ubuntu</span><span class="pun">-</span><span class="pln">s</span><span class="pun">-</span><span class="lit">1vcpu</span><span class="pun">-</span><span class="lit">1gb</span><span class="pun">-</span><span class="pln">blr1</span><span class="pun">-</span><span class="lit">01</span><span class="pun">:~#</span></pre>

<p>
	وبهذا صرنا متصلين <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">بالخادم</a> ويمكننا تنفيذ التعليمات عليه.
</p>

<h2 id="-3">
	سحب صورة التطبيق من سجل الحاويات
</h2>

<p>
	من المهم في البداية التأكد من وجود <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-docker-r3/" rel="">Docker</a> ضمن الخادم، وذلك بكتابة الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_68" style=""><span class="pln">root@ubuntu</span><span class="pun">-</span><span class="pln">s</span><span class="pun">-</span><span class="lit">1vcpu</span><span class="pun">-</span><span class="lit">1gb</span><span class="pun">-</span><span class="pln">blr1</span><span class="pun">-</span><span class="lit">01</span><span class="pun">:~#</span><span class="pln"> docker </span><span class="pun">--</span><span class="pln">version
</span><span class="typ">Docker</span><span class="pln"> version </span><span class="lit">24.0</span><span class="pun">.</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> build ced0996</span></pre>

<p>
	وإذا لم يكن موجودًا يمكننا تثبيته باستخدام الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_70" style=""><span class="pln">root@ubuntu</span><span class="pun">-</span><span class="pln">s</span><span class="pun">-</span><span class="lit">1vcpu</span><span class="pun">-</span><span class="lit">1gb</span><span class="pun">-</span><span class="pln">blr1</span><span class="pun">-</span><span class="lit">01</span><span class="pun">:~#</span><span class="pln"> snap install docker</span></pre>

<p>
	ثم نسجل الدخول إلى سجل الحاوية بالخطوات نفسها التي اتبعناها قبل قليل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_72" style=""><span class="pln">root@ubuntu</span><span class="pun">-</span><span class="pln">s</span><span class="pun">-</span><span class="lit">1vcpu</span><span class="pun">-</span><span class="lit">1gb</span><span class="pun">-</span><span class="pln">blr1</span><span class="pun">-</span><span class="lit">01</span><span class="pun">:~#</span><span class="pln"> docker login registry</span><span class="pun">.</span><span class="pln">digitalocean</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Username</span><span class="pun">:</span><span class="pln"> ravish</span><span class="pun">.</span><span class="pln">foo@bar</span><span class="pun">.</span><span class="pln">com
</span><span class="typ">Password</span><span class="pun">:</span><span class="pln">
</span><span class="typ">Login</span><span class="pln"> </span><span class="typ">Succeeded</span></pre>

<p>
	بعد ذلك نستطيع سحب pull صورة التطبيق من السجل كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_74" style=""><span class="pln">root@ubuntu</span><span class="pun">-</span><span class="pln">s</span><span class="pun">-</span><span class="lit">1vcpu</span><span class="pun">-</span><span class="lit">1gb</span><span class="pun">-</span><span class="pln">blr1</span><span class="pun">-</span><span class="lit">01</span><span class="pun">:~#</span><span class="pln"> docker pull registry</span><span class="pun">.</span><span class="pln">digitalocean</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="pln">my</span><span class="pun">-</span><span class="pln">container</span><span class="pun">-</span><span class="pln">registry</span><span class="pun">/</span><span class="pln">react</span><span class="pun">-</span><span class="pln">app</span></pre>

<p>
	وسنحصل على خرج يشبه التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_76" style=""><span class="typ">OutputUsing</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> tag</span><span class="pun">:</span><span class="pln"> latest
latest</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pulling</span><span class="pln"> from my</span><span class="pun">-</span><span class="pln">container</span><span class="pun">-</span><span class="pln">registry</span><span class="pun">/</span><span class="pln">react</span><span class="pun">-</span><span class="pln">app
b0a0cf830b12</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Already</span><span class="pln"> exists
</span><span class="lit">8ddb1e6cdf34</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Already</span><span class="pln"> exists
</span><span class="lit">5252b206aac2</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Already</span><span class="pln"> exists
</span><span class="lit">988b92d96970</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Already</span><span class="pln"> exists
</span><span class="lit">7102627a7a6e</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Already</span><span class="pln"> exists
</span><span class="lit">93295add984d</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Already</span><span class="pln"> exists
ebde0aa1d1aa</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Already</span><span class="pln"> exists
</span><span class="lit">6156e0586e61</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Already</span><span class="pln"> exists
</span><span class="lit">88bdf71f3911</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pull</span><span class="pln"> complete
f5524cce8c8a</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pull</span><span class="pln"> complete
</span><span class="lit">4f4fb700ef54</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Pull</span><span class="pln"> complete
</span><span class="typ">Digest</span><span class="pun">:</span><span class="pln"> sha256</span><span class="pun">:</span><span class="lit">227a8c3ede3fc5c81b281228600bec84939f8a4a0cc770fc6e5527b3261e77f4</span><span class="pln">
</span><span class="typ">Status</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Downloaded</span><span class="pln"> newer image </span><span class="kwd">for</span><span class="pln"> registry</span><span class="pun">.</span><span class="pln">digitalocean</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="pln">my</span><span class="pun">-</span><span class="pln">container</span><span class="pun">-</span><span class="pln">registry</span><span class="pun">/</span><span class="pln">react</span><span class="pun">-</span><span class="pln">app</span><span class="pun">:</span><span class="pln">latest
registry</span><span class="pun">.</span><span class="pln">digitalocean</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="pln">my</span><span class="pun">-</span><span class="pln">container</span><span class="pun">-</span><span class="pln">registry</span><span class="pun">/</span><span class="pln">react</span><span class="pun">-</span><span class="pln">app</span><span class="pun">:</span><span class="pln">latest</span></pre>

<p>
	وكما نلاحظ، فالأمر السابق قد سَحَبَ الصورة ذات الوسم <code>latest</code> أي الصورة الأحدث، لأننا لم نُحَدِّد أي وسم آخر في أمر السحب، وفي مثل هذه الحالة يسحب <code>docker</code> الصورة <code>latest</code> تلقائيًا.
</p>

<h2 id="-4">
	تشغيل الحاوية
</h2>

<p>
	لدينا الآن صورة عن أحدث إصدار من التطبيق تشمل كل الاعتماديات اللازمة لعمله، ويمكننا تشغيل الحاوية بواسطتها وفق الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_78" style=""><span class="pln">docker run </span><span class="pun">-</span><span class="pln">dp </span><span class="lit">80</span><span class="pun">:</span><span class="lit">80</span><span class="pln"> registry</span><span class="pun">.</span><span class="pln">digitalocean</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="pln">my</span><span class="pun">-</span><span class="pln">container</span><span class="pun">-</span><span class="pln">registry</span><span class="pun">/</span><span class="pln">react</span><span class="pun">-</span><span class="pln">app</span></pre>

<p>
	وكتوضيح للشيفرة أعلاه، تجعل الراية <code>d-</code> الحاوية تعمل في الخلفية، وتصل الراية <code>p 80:80</code> البوابة <code>80</code> في الخادم المضيف بالبوابة <code>80</code> في الحاوية لتتجه حركة مرور البيانات الواردة للخادم إلى بوابة الحاوية.
</p>

<p>
	البوابة <code>80</code> هي البوابة المعيارية لحركة البيانات وفق <a href="https://academy.hsoub.com/devops/servers/web/" rel="">البروتوكول HTTP</a> وقد ضبطنا الخادم <code>nginx</code> ليستقبل الطلبات عبرها.
</p>

<p>
	سننفِّذ الآن الأمر التالي للتأكد من أن الحاوية قيد التشغيل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_80" style=""><span class="pln">root@ubuntu</span><span class="pun">-</span><span class="pln">s</span><span class="pun">-</span><span class="lit">1vcpu</span><span class="pun">-</span><span class="lit">1gb</span><span class="pun">-</span><span class="pln">blr1</span><span class="pun">-</span><span class="lit">01</span><span class="pun">:~#</span><span class="pln"> docker ps </span><span class="pun">-</span><span class="pln">a</span></pre>

<p>
	يَعرِضُ هذا الأمر الحاويات المُشَغّلة حاليًا على الخادم، سنجد حاويتنا بينها مع عبارة مثل "UP 2 seconds" تبين الزمن الذي أمضته الحاوية في وضع التشغيل. وحالما نتأكد من عمل الحاوية سيكون تطبيقنا متاحًا على الإنترنت للراغبين باستخدامه.
</p>

<p>
	يمكننا تجربة ذلك عبر كتابة عنوان IP لخادم Droplet في متصفحنا وستفتح أمامنا واجهة تطبيق React:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="172393" href="https://academy.hsoub.com/uploads/monthly_2025_05/img09-react-app-web-page.png.2179e5051db27031191db98e0665bfc9.png" rel=""><img alt="img09 react app web page" class="ipsImage ipsImage_thumbnailed" data-fileid="172393" data-unique="9lul1ua14" style="width: 750px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_05/img09-react-app-web-page.thumb.png.e9e6d72de7bdef5cc32ee58a4c067a6b.png"> </a>
</p>

<p>
	يمكننا إيقاف تشغيل الحاوية بواسطة الأمر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_82" style=""><span class="pln">docker stop </span><span class="pun">&lt;</span><span class="pln">container_id</span><span class="pun">&gt;</span></pre>

<p>
	وإذا أردنا نستطيع حذفها بعد إيقافها بكتابة التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6962_84" style=""><span class="pln">docker rm </span><span class="pun">&lt;</span><span class="pln">container_id</span><span class="pun">&gt;</span></pre>

<h2 id="-5">
	الخلاصة
</h2>

<p>
	يمكن الانطلاق مما تعلمناه هنا وتطوير آلية نشر تطبيقاتنا عبر أتمتة عمليات بناء النسخ ورفعها إلى سجل الحاويات وسحبها منه؛ حيث يمكن مثلًا كتابة <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D8%AF%D9%81%D8%A9-%D8%A8%D8%A7%D8%B4-bash-r606/" rel="">سكريبت Bash</a> يتكامل مع GitHub فيبني تلقائيًا صورة جديدة لتطبيقنا ويرفعها إلى سجل الحاويات مع كل عملية إيداع commit تنفذها على التطبيق، كما نستطيع إنشاء خط أنابيب <a href="https://academy.hsoub.com/devops/deployment/%D8%A3%D9%81%D8%B6%D9%84-%D9%85%D9%85%D8%A7%D8%B1%D8%B3%D8%A7%D8%AA-%D9%85%D9%86%D9%87%D8%AC-%D8%A7%D9%84%D8%AA%D9%83%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-%D9%88%D8%A7%D9%84%D8%AA%D8%B3%D9%84%D9%8A%D9%85-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-cicd-r687/" rel="">للنشر المستمر والتسليم المستمر CI/CD</a> يؤتمت عملية سحب الصور من السجل ونشرها على السحابة، فضلًا عن إمكانية استخدام عناقيد <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%85%D8%A7-%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D8%AF%D9%88%D9%83%D8%B1-docker-%D9%88%D9%83%D9%88%D8%A8%D9%8A%D8%B1%D9%86%D9%8A%D8%AA%D9%8A%D8%B3-kubernetes%D8%9F-r612/" rel="">Kubernetes</a> في عمليات النشر واسعة النطاق، والتي تتيح لنا إمكانية إدارة مجموعة كاملة من الحاويات بدلًا واحدة.
</p>

<p>
	ترجمة -وبتصرف- لمقال <a href="https://www.digitalocean.com/community/developer-center/how-to-deploy-your-react-app-using-container-registry" rel="external nofollow">How to deploy your React app using Container Registry</a> لصاحبيه Ravish Ahmad Khan و Anish Singh Walia.
</p>

<h2 id="-6">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-r641/" rel="">مدخل إلى الحاويات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AF%D9%88%D9%83%D8%B1-docker-r607/" rel="">مدخل إلى دوكر Docker</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-docker-r310/" rel="">التعامل مع حاويات Docker</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%AA%D9%86%D8%B3%D9%8A%D9%82-%D8%A7%D9%84%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-r643/" rel="">أساسيات تنسيق الحاويات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">845</guid><pubDate>Mon, 23 Jun 2025 16:00:02 +0000</pubDate></item><item><title>&#x62F;&#x644;&#x64A;&#x644; &#x627;&#x644;&#x645;&#x628;&#x62A;&#x62F;&#x626;&#x64A;&#x646; &#x641;&#x64A; &#x645;&#x62C;&#x627;&#x644; DevOps &#x648;&#x623;&#x647;&#x645; &#x645;&#x645;&#x627;&#x631;&#x633;&#x627;&#x62A;&#x647; &#x648;&#x623;&#x62F;&#x648;&#x627;&#x62A;&#x647;</title><link>https://academy.hsoub.com/devops/general/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%81%D9%8A-%D9%85%D8%AC%D8%A7%D9%84-devops-%D9%88%D8%A3%D9%87%D9%85-%D9%85%D9%85%D8%A7%D8%B1%D8%B3%D8%A7%D8%AA%D9%87-%D9%88%D8%A3%D8%AF%D9%88%D8%A7%D8%AA%D9%87-r844/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_05/DevOps.png.7fcac3f8eb69af06e18df3b5dfc40eaa.png" /></p>
<p>
	طرأت تطورات كثيرة منذ أن أصبح مصطلح DevOps شائعًا في عالم تقنية المعلومات. ونظرًا لكثرة المصادر المفتوحة في هذا المجال، فمن المفيد الاطلاع على نشأة هذا المصطلح وأهميته في المهن التقنية.
</p>

<p>
	سنلقي الضوء في مقالنا هذا على مفهوم تخصص DevOps وأهميته، ونستعرض أساليبه، وأطر عمله، وأدواته.
</p>

<h2 id="-devops">
	ما هو تخصص DevOps
</h2>

<p>
	لا يوجد تعريف واحد لمصطلح <a href="https://academy.hsoub.com/devops/general/%D8%AA%D8%B9%D9%84%D9%85-devops/" rel="">DevOps</a>، لكن يمكن أن نعُدّه إطار عمل يضمن التعاون بين فرق التطوير Development المسؤولة عن كتابة الشيفرة البرمجية للتطبيق، وفرق العمليات Operations المسؤولة عن نشر الشيفرات والبرامج في بيئات الإنتاج بسرعة وبطريقة مؤتمتة Automated وقابلة للتكرار أي يمكن تشغيلها عدة مرات دون مشكلات.
</p>

<p>
	ومصطلح DevOps هو دمج لكلمتي تطوير Development وعمليات Operations. وهو <a href="https://academy.hsoub.com/devops/general/%D9%84%D9%85%D8%A7%D8%B0%D8%A7-%D8%AA%D8%B9%D8%AF-devops-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B1%D8%A7%D8%AA%D9%8A%D8%AC%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%82%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%A3%D9%83%D8%AB%D8%B1-%D8%A3%D9%87%D9%85%D9%8A%D8%A9-%D8%A7%D9%84%D9%8A%D9%88%D9%85-r421/" rel="">تقنية مهمة</a> جدًا تساعد على زيادة سرعة تسليم التطبيقات والخدمات، وتتيح للمؤسسات خدمة عملائها بكفاءة أعلى، وتزيد قدرتها على المنافسة في السوق.
</p>

<p>
	يركّز منهج DevOps على نشر ثقافة التوافق بين فرق التطوير وفرق تقنية المعلومات وتحسين التواصل والتعاون بينهما، ويولد تطبيق ممارسات DevOps في المؤسسة قيمة مستمرة للعملاء، ويعتمد على ثلاث ركائز أساسية هي الأشخاص People، والعمليات Processes، والأدوات Tools. فالأشخاص هم الذين يقومون بالعمل، والعمليات هي الطريقة التي ينجز بها العمل، أما الأدوات فهي البرامج التي تساعد على تنفيذ العمل بشكل أسرع وأسهل من خلال الأتمتة والتي سنوضحها بمزيد من التفصيل لاحقًا.
</p>

<p>
	يزيد تطبيق ممارسات DevOps من قدرة المؤسسات على تقديم حلول برمجية عالية الجودة بوتيرة سريعة، وذلك من خلال أتمتة جميع العمليات الخاصة بتطبيق أو منتج ما، بدءًا من إنشائه، وحتى نشره إلى المستخدمين.
</p>

<h2 id="-">
	التحديات التي يواجهها فريق التطوير
</h2>

<p>
	يكون المطورون متحمسين عادةً ومستعدين لتبني أساليب وتقنيات جديدة لحل المشكلات، لكنهم قد يواجهون التحديات التالية:
</p>

<ul>
	<li>
		يخلق السوق التنافسي ضغطًا كبيرًا على المطورين لتسليم المشروع في الوقت المحدد
	</li>
	<li>
		يجب على المطورين الالتزام بجعل الشيفرة البرمجية جاهزة ومناسبة لبيئات الإنتاج وتطبيق المميزات الجديدة
	</li>
	<li>
		يتعين على فريق التطوير وضع عدة توقعات قبل نشر التطبيق بسبب طول دورة إصدار التطبيقات، لذا يستغرق حل المشكلات التي تحدث أثناء النشر مزيدًا من الوقت في بيئات الإنتاج أو في مرحلة التهيئة الأخيرة قبل النشر Staging Environment.
	</li>
</ul>

<h2 id="-">
	التحديات التي يواجهها فريق العمليات
</h2>

<p>
	فريق العمليات هو المسؤول عن تشغيل التطبيقات بشكل مستقر وآمن على الخوادم أو الخدمات السحابية، وضمان أن تعمل التطبيقات كما ينبغي بعد نشرها للمستخدمين النهائيين، ويركّز هذا الفريق على استقرار وموثوقية الخدمات التقنية. ولذلك نجدهم يهتمون بإجراء تغييرات على الموارد أو التقنيات أو الأساليب المتبعة لتحقيق استقرار التطبيقات، وقد يواجهون التحديات التالية:
</p>

<ul>
	<li>
		إدارة التنافس على الموارد، وتوفير موارد كافية عند زيادة الطلب عليها
	</li>
	<li>
		التعامل مع عمليات إعادة تصميم البنية التحتية للتطبيق وإجراء التعديلات المطلوبة لتنفيذ التطبيق في بيئة الإنتاج
	</li>
	<li>
		تشخيص المشكلات التي قد تظهر في بيئة الإنتاج، وحلّها بصورة منفصلة بعد نشر التطبيق
	</li>
</ul>

<h2 id="-devops-">
	كيف تعالج منهجية DevOps تحديات فريقي التطوير والعمليات
</h2>

<p>
	بدلًا من إطلاق ميزات كثيرة للتطبيق في وقت واحد، تحاول الشركات طرح عدد صغير من الميزات لعملائها من خلال سلسلة من تكرارات الإصدار Release Iterations، ولهذا الأمر عدة مزايا، مثل تحقيق جودة أعلى للبرامج، والحصول على تقييمات العملاء بصورة أسرع وهذا يضمن رضاهم.
</p>

<p>
	ولتحقيق هذه الأهداف يجب على الشركات تحقيق التالي:
</p>

<ul>
	<li>
		تقليل احتمالية فشل الإصدارات الجديدة بجعل كل تحديث أصغر وأسهل في الاختبار
	</li>
	<li>
		زيادة وتيرة النشر عن طريق إطلاق نسخ محدثة بوتيرة أسرع وبشكل منتظم
	</li>
	<li>
		تقصير وقت التعافي MTTR في حال حدوث خلل بعد نشر إصدار جديد، لتعود الخدمة لطبيعتها بسرعة
	</li>
	<li>
		تسريع وقت التسليم Lead Time، أي تقليل المدة بين اكتشاف المشكلة وإطلاق الإصلاح الخاص بها
	</li>
</ul>

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

<p>
	يهتم نهج DevOps بمعالجة مجموعة متنوعة من المشكلات التي تنتج عن المنهجيات السابقة، بما في ذلك:
</p>

<ul>
	<li>
		مشكلة عمل فرق التطوير والتشغيل بصورة منفصلة عن بعضهم
	</li>
	<li>
		مشكلة إنجاز مرحلتي الاختبار والنشر كلّ على حدة بعد انتهاء مرحلتي التصميم والبناء، وذلك يتطلب وقتًا أطول من دورات البناء
	</li>
	<li>
		مشكلة قضاء أعضاء الفريق وقتًا طويلًا في الاختبار والنشر والتصميم بدلًا من التركيز على إنشاء خدمات للأعمال
	</li>
	<li>
		مشكلة حدوث أخطاء الإنتاج عند النشر اليدوي للكود
	</li>
	<li>
		مشكلة عمل فرق التطوير والعمليات وفق جداول زمنية منفصلة وغير متزامنة، مما يسبب تأخيرات إضافية في العمل
	</li>
</ul>

<h2 id="-agile-devops-it-">
	مقارنة بين منهجية أجايل Agile و DevOps وتقنية Waterfall التقليدية
</h2>

<p>
	تُقارَن DevOps مع المنهجيات الأخرى في مجال تقنية المعلومات، ولا سيّما منهجيّتي أجايل Agile و<a href="https://academy.hsoub.com/entrepreneurship/business/%D9%83%D9%84-%D9%85%D8%A7-%D8%AA%D8%AD%D8%AA%D8%A7%D8%AC-%D9%84%D9%85%D8%B9%D8%B1%D9%81%D8%AA%D9%87-%D8%B9%D9%86-%D9%85%D9%86%D9%87%D8%AC%D9%8A%D8%A9-%D8%AA%D8%AF%D9%81%D9%82-%D8%A7%D9%84%D9%85%D9%8A%D8%A7%D9%87-waterfall-%D9%84%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-r1107/" rel="">الشلال Waterfall</a>.
</p>

<p>
	تتكون <a href="https://academy.hsoub.com/entrepreneurship/business/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%84%D9%85%D9%86%D9%87%D8%AC%D9%8A%D8%A9-%D8%A3%D8%AC%D8%A7%D9%8A%D9%84-agile-r1047/" rel="">أجايل Agile </a> من مجموعة من المبادئ والقيم والأساليب المُتّبعة لإنتاج البرمجيات. فمثلًا، إن أردنا تحويل فكرة إلى برنامج، فيمكننا الاستفادة من مبادئ وقيم أجايل، لكن هذا البرنامج قد يعمل فقط في بيئة التطوير أو الاختبار، لذا سنحتاج إلى طريقة لنقل البرنامج بسرعة وتكرار إلى بيئة الإنتاج بطريقة بسيطة وآمنة، والطريقة هي استخدام أدوات وتقنيات DevOps؛ إذ تركّز منهجية أجايل لتطوير البرمجيات على عمليات التطوير، عكس منهجية DevOps المسؤولة عن التطوير والنشر بطريقة أكثر أمانًا وموثوقية.
</p>

<p>
	والآن لنقارن بين منهجية الشلال أو تدفق المياه <a href="https://academy.hsoub.com/entrepreneurship/business/%D9%83%D9%84-%D9%85%D8%A7-%D8%AA%D8%AD%D8%AA%D8%A7%D8%AC-%D9%84%D9%85%D8%B9%D8%B1%D9%81%D8%AA%D9%87-%D8%B9%D9%86-%D9%85%D9%86%D9%87%D8%AC%D9%8A%D8%A9-%D8%AA%D8%AF%D9%81%D9%82-%D8%A7%D9%84%D9%85%D9%8A%D8%A7%D9%87-waterfall-%D9%84%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-r1107/" rel="">Waterfall</a> التقليدية ومنهجية DevOps لفهم الفوائد التي تقدمها DevOps. سنفترض أن لدينا السيناريو التالي:
</p>

<p>
	لدينا تطبيق سيُشغَّل بعد أربعة أسابيع، واكتملت عملية كتابة الشيفرة بنسبة 85%، وبدأت عملية شراء الخوادم التي ستُرفع الشيفرة البرمجية عليها.
</p>

<p>
	يوضّح الجدول التالي الفروقات بين تطبيق المنهجيتين في هذه الحالة:
</p>

<table>
	<thead>
		<tr>
			<th>
				النهج التقليدي
			</th>
			<th>
				نهج DevOps
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				يعمل فريق التطوير على الاختبار بعد تقديم طلب لشراء الخوادم الجديدة، ويعمل فريق العمليات على تجهيز الأعمال الورقية اللازم لنشر البنية التحتية
			</td>
			<td>
				بعد تقديم طلب شراء الخوادم، يعمل فريق التطوير والعمليات معًا على العمليات والأوراق اللازمة لإعداد الخوادم الجديدة. ويؤدي هذا إلى فهم أفضل لمتطلبات البنية التحتية
			</td>
		</tr>
		<tr>
			<td>
				لا تكون المعلومات المتعّلقة بتجاوز الفشل والتكرار ومواقع مراكز البيانات، ومتطلبات التخزين دقيقة بسبب عدم توفر مدخلات من فريق التطوير الذي لديه معرفة عميقة بالتطبيق
			</td>
			<td>
				المعلومات المتعّلقة بتجاوز الفشل والتكرار والتعافي من الكوارث ومواقع مراكز البيانات ومتطلبات التخزين معروفة وصحيحة بسبب مدخلات فريق التطوير
			</td>
		</tr>
		<tr>
			<td>
				لا يعلم فريق العمليات بالتقدم الذي أحرزه فريق التطوير، إذ يضع فريق العمليات خطة مراقبة بناءً على فهمهم
			</td>
			<td>
				يطّلع فريق العمليات على تقدم فريق التطوير، ويتعاون الفريقان في وضع خطة مراقبة تلبي احتياجات تقنية المعلومات والأعمال. ويستخدمون أدوات مراقبة أداء التطبيقيات APM Tools
			</td>
		</tr>
		<tr>
			<td>
				يؤدي اختبار التحميل Load Test قبل إتاحة التطبيق للعموم إلى تعطل التطبيق، مما يؤدي إلى تأخير إصداره
			</td>
			<td>
				يؤدي اختبار التحميل قبل إتاحة التطبيق إلى بطء التطبيق. يُصلَح فريق التطوير الاختناقات بسرعة، ويُصدَر التطبيق في الوقت المحدد
			</td>
		</tr>
	</tbody>
</table>

<h2 id="-devops">
	دورة حياة المشاريع في DevOps
</h2>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2025_05/001devopsprocesses.png.273934ba841695f2b01cf3cd62a6aeb4.png" data-fileid="171626" data-fileext="png" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="171626" data-ratio="53.22" data-unique="zp5615z96" width="900" alt="001 devopsprocesses.png" src="https://academy.hsoub.com/uploads/monthly_2025_05/001devopsprocesses.thumb.png.54c0fa158b00f73a70a45d6ceec2c711.png"></a>
</p>

<p>
	سنستعرض فيما يلي بعض الممارسات الشائعة في منهجية DevOps.
</p>

<h3 id="-continuous-planning">
	التخطيط المستمر Continuous Planning
</h3>

<p>
	يعتمد التخطيط المستمر على <a href="https://academy.hsoub.com/entrepreneurship/business/%D9%85%D8%A7-%D9%87%D9%88-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B1%D9%86-lean-software-development%D8%9F-r1094/" rel="">مبادئ تطوير البرمجيات المرن lean</a> للعمل بخطوات صغيرة، وذلك من خلال تحديد الموارد والنتائج اللازمة لاختبار قيم العمل أو رؤيته، والتكيف باستمرار، وقياس التقدم، والتعلم من احتياجات العملاء، وتعديل العمل بسرعة وفقًا للحاجة وتحديث خطة العمل.
</p>

<h3 id="-collaborative-development">
	التطوير التعاوني Collaborative Development
</h3>

<p>
	تتيح عملية التطوير التعاوني Collaborative Development إمكانية التعاون بين الفريق التجاري وفريق التطوير وفريق الاختبار المنتشرين في مناطق مختلفة لتقديم برامج ذات جودة عالية باستمرار، ويتضمن ذلك تطوير منصات متعددة، ودعم البرمجة متعددة اللغات، وإنشاء قصص المستخدمين، ووضع الأفكار، وإدارة دورة حياة المشروع.
</p>

<p>
	يشتمل التطوير التعاوني على عملية التكامل المستمر، مما يعزز دمج الشيفرة البرمجية Code Integration بتكرار، ويؤدي ذلك إلى تحديد مشكلات التكامل في وقت مبكر من دورة الحياة التطبيق حيث يكون إصلاحها أسهل، ويُخفّض جهد التكامل الإجمالي من خلال الآراء والتقييمات المستمرة، لأن المشروع يُظهر تقدمًا مستمرًا ومثبتًا.
</p>

<h3 id="-continuous-testing">
	الاختبار المستمر Continuous Testing
</h3>

<p>
	يؤدي الاختبار المستمر إلى تقليل تكلفة الاختبار ومساعد فرق التطوير على تحقيق التوازن بين سرعة الإنجاز والجودة، كما يزيل اختناقات الاختبار Testing Bottlenecks من خلال الخدمات الافتراضية ويبسط إنشاء بيئات اختبار افتراضية يمكن مشاركتها ونشرها وتحديثها بسهولة عند تغيير الأنظمة.
</p>

<p>
	تخفّض هذه الميزات تكلفة توفير بيئات الاختبار وصيانتها، وتقصّر المدة اللازمة لإتمام دورات الاختبار من خلال إتاحة اختبار التكامل في وقت مبكر من دورة حياة المشروع.
</p>

<h3 id="-continuous-release-continuous-deployment">
	الإصدار المستمر Continuous Release والنشر المستمر Continuous Deployment
</h3>

<p>
	يوفر الإصدار والنشر المستمران خطوط إنتاج مستمرة تعمل على أتمتة العمليات الرئيسية، ويقللان من العمليات اليدوية وأوقات انتظار إتاحة الموارد وكمية العمل، وذلك من خلال تفعيل خاصيّة النشر بضغطة زر، مما يتيح زيادة عدد الإصدارات وتقليل الأخطاء.
</p>

<p>
	تلعب الأتمتة دورًا رئيسيًا في ضمان إصدار البرنامج بتكرار وموثوقية؛ إذ إن أحد الأهداف الرئيسية هو أتمتة العمليات اليدوية مثل عملية بناء التطبيقات والانحدار Regression والنشر وتوفير البنية التحتية.
</p>

<p>
	ويتطلب هذا إدارة إصدارات الشيفرة المصدرية، مثل استخدام البرامج النصية لأتمتة عمليات الاختبار والنشر، وأتمتة إعدادات التطبيقات والبنية التحتية، والمكتبات والحزم التي يعتمد عليها التطبيق؛ إذ تُعدّ القدرة على الاستعلام عن حالة جميع البيئات عاملًا مهمًا.
</p>

<h3 id="-continuous-monitoring">
	المراقبة المستمرة Continuous Monitoring
</h3>

<p>
	توفر المراقبة المستمرة إمكانية إعداد التقارير على مستوى المؤسسة، ويساعد ذلك فرق التطوير على الإطلاع على أداء التطبيقات ومدى توفرها في بيئة الإنتاج، قبل نشرها.
</p>

<p>
	تُعَدّ التقييمات والآراء Feedback التي توفرها المراقبة المستمرة في مراحل مبكّرة ذات أهمية كبيرة في خفض الأخطاء وتوجيه المشاريع في الاتجاه الصحيح. وغالبًا ما تتضمن هذه المرحلة أدوات المراقبة التي تكشف عن المقاييس المتعلقة بأداء التطبيق.
</p>

<h3 id="-continuous-feedback-optimization">
	المراجعة والتقييم والتحسين Optimization
</h3>

<p>
	توفر التقييمات والآراء المستمرة Continuous Feedback من العملاء وسائل مساعدة لتحليل تجربتهم للتطبيق وتحديد النقاط التي يجب تحسينها. ويمكن إتاحتهما قبل الإنتاج وبعده لزيادة الاستفادة إلى أقصى حد وضمان إتمام مزيد من المعاملات بنجاح إذ يُتيح ذلك معرفة السبب الجذري للمشكلات التي يواجهها العملاء، والتي تؤثر على السلوك من جهة، وعلى العمل من جهة أخرى.
</p>

<h2 id="-devops-">
	فوائد تطبيق منهجية DevOps في بيئة العمل
</h2>

<p>
	يؤمّن تطبيق منهجية DevOps بيئة عمل يتعاون فيها فريق التطوير وفريق إدارة البرمجيات ويعملان كفريق واحد لتحقيق أهداف مشتركة. ويتميز ذلك بتنفيذ التكامل المستمر والتسليم المستمر CI/CD، مما يتيح إصدار البرامج بسرعة وأخطاء أقل ويعود علينا بجملة من الفوائد تشمل:
</p>

<ul>
	<li>
		<strong>القدرة على التنبؤ بنتائج الإصدارات الجديدة</strong>: عند تطبيق ممارسات DevOps في بيئة العمل، سينخفض معدل فشل الإصدارات الجديدة للتطبيقات كثيرًا
	</li>
	<li>
		<strong>قابلية الصيانة</strong>: سنتمكن من استرداد التطبيق بسهولة في حالة عدم عمل الإصدار الجديد أو تعطل التطبيق
	</li>
	<li>
		<strong>إمكانية التكرار</strong>: فبفضل أدوات إدارة الإصدارات، سنتمكن من تتبع التغييرات بدقة والرجوع إلى إصدارات سابقة بسهولة عند الحاجة
	</li>
	<li>
		<strong>جودة أعلى للتطبيقات</strong>: تؤدي إدارة مشكلات البنية التحتية لتحسين جودة التطبيقات
	</li>
	<li>
		<strong>تقصير وقت إطلاق التطبيق</strong>: يؤدي تسليم البرامج المبسط إلى تقليل الوقت اللازم لتسليمه وتوفره في السوق بنسبة 50%
	</li>
	<li>
		<strong>تقليل المخاطر</strong>: وذلك عن طريق تعزيز الأمان في دورة حياة البرنامج، مما يؤدي إلى تقليل الأخطاء والعيوب التي قد تظهر
	</li>
	<li>
		<strong>فعالية التكلفة</strong>: تؤدي ممارسات DevOps لتخفيض تكلفة تطوير البرمجيات
	</li>
	<li>
		<strong>المرونة</strong>: يكون نظام البرمجيات أكثر استقرارًا وأمانًا، مع إمكانية تتبع التغييرات وتدقيقها بسهولة
	</li>
	<li>
		<strong>تقسيم قاعدة الشيفرات البرمجية الكبيرة لأجزاء أصغر</strong>: تتكامل ممارسات DevOps مع منهجيات أجايل، مما يساعد على تقسيم الكود لأجزاء صغيرة يسهل إدارتها واختبارها ونشرها
	</li>
</ul>

<h2 id="-devops">
	مبادئ DevOps
</h2>

<p>
	مبادئ DevOps كثيرة، وهي تتطور مع الوقت حسب احتياجات كل شركة. فبعض الشركات تضيف تغييرات خاصة بها، لكن في النهاية، تشكل هذه المبادئ طريقة عمل شاملة تساعد الفرق على التعاون بشكل أفضل، وتسليم البرامج بسرعة وجودة.
</p>

<p>
	ومن أهم هذه المبادئ نذكر:
</p>

<h3 id="1-">
	1. التطوير والاختبار في بيئة شبيهة ببيئة الإنتاج
</h3>

<p>
	الهدف من هذا المبدأ هو السماح لفرق التطوير وفرق ضمان الجودة Quality Assurance -أو اختصارًا QA- بتطوير البرمجيات واختبارها في بيئات قريبة من بيئات الإنتاج Production Systems، كي يعاينوا أداء التطبيقات قبل أن تصبح جاهزة للنشر.
</p>

<p>
	يجب أن يتعرض التطبيق لأنظمة شبيهة بالإنتاج في وقت مبكر قدر الإمكان من دورة حياته لمعالجة الأمور التالية:
</p>

<ul>
	<li>
		اختبار التطبيق في بيئة قريبة من البيئة الفعلية
	</li>
	<li>
		السماح باختبار عمليات تسليم التطبيق والتحقق من صحته مقدمًا
	</li>
	<li>
		تمكين فريق العمليات من اختبار أداء البيئة عند نشر التطبيق في مرحلة مبكرة من دورة حياته وتعديل البيئة بما يناسب التطبيق
	</li>
</ul>

<h3>
	2. النشر باستخدام عمليات موثوقة وقابلة للتكرار
</h3>

<p>
	يسمح هذا المبدأ لفرق التطوير والعمليات بتنفيذ منهجية أجايل المرنة في تطوير البرمجيات طوال دورة حياة المشروع، إذ تُعدّ الأتمتة مهمةً لإنشاء عمليات متكررة وموثوقة  ولذلك يجب على المؤسسات إنشاء خط تسليم Delivery Pipeline يتيح إمكانية النشر والاختبار المستمر والمؤتمت، مما يضمن أن كل تحديث جرى اختباره قبل أن نشره بشكل فعلي، وبالتالي تقليل مخاطر فشل النشر أثناء الإصدار الفعلي للتطبيق.
</p>

<h3 id="3-operational-quality">
	3. مراقبة الجودة التشغيلية Operational Quality
</h3>

<p>
	تُجيد المؤسسات مراقبة أداء التطبيقات في بيئة الإنتاج لأن لديها أدوات لمراقبة المقاييس ومؤشرات الأداء الرئيسية KPIs في الوقت الفعلي. ويُطبَق هذا المبدأ في وقت مبكر من دورة حياة التطبيق، مما يضمن أن الاختبارات الآلية تراقب السمات الوظيفية وغير الوظيفية للتطبيق في وقت مبكر.
</p>

<p>
	يجب جمع مقاييس <a href="https://academy.hsoub.com/entrepreneurship/business/%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84-%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%82%D8%A8%D9%88%D9%84-%D9%84%D8%B6%D9%85%D8%A7%D9%86-%D8%AC%D9%88%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D9%86%D8%AA%D8%AC-r1114/" rel="">الجودة</a> وتحليلها عند اختبار تطبيق ما ونشره، إذ يضمن ذلك كشف مشكلات التشغيل التي قد تحدث أثناء الإنتاج. ويجب جمع هذه المقاييس وصياغتها بطريقة يمكن لجميع المشاركين في المشروع ومالكي الأسهم فهمها.
</p>

<h3 id="4-feedback-">
	4. جمع تقييمات وآراء موسعة حول التطبيق
</h3>

<p>
	إن أحد أهداف عمليات DevOps هو تمكين المؤسسات من التفاعل وإجراء التغييرات بسرعة أكبر. يتطلب تحقيق هذا الهدف في مجال تسليم البرمجيات جمع تقييمات وآراء Feedback مبكرة، ومن ثم التعلم بسرعة من كل إجراء.
</p>

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

<h2 id="-devops">
	قائمة بأهم أدوات DevOps
</h2>

<p>
	من أهم أدوات DevOps مفتوحة المصدر في مجال تطوير البرمجيات وإدارة العمليات نذكر الآتي:
</p>

<h3 id="-development">
	أهم أدوات عملية التطوير Development
</h3>

<ul>
	<li>
		<strong>مرحلة التخطيط</strong>: أداة Kanboard وWekan و<a href="https://ana.hsoub.com" rel="external">أنا</a> و<a href="https://gitlab.com/gitlab-org/gitlab" rel="external nofollow">GitLab</a> و<a href="https://github.com/Enalean/tuleap" rel="external nofollow">Tuleap</a> و <a href="https://www.redmine.org/" rel="external nofollow">Redmine</a> وبدائل جيرا JIRA مثل Mattermost و Roit.im و IRC.
	</li>
	<li>
		<strong>مرحلة كتابة الشيفرة</strong>: <a href="https://git-scm.com/" rel="external nofollow">غيت Git</a> و <a href="https://gerrit.googlesource.com/gerrit/" rel="external nofollow">Gerrit</a> و <a href="https://github.com/bugzilla/bugzilla" rel="external nofollow">Bugzilla</a> و <a href="https://github.com/jenkinsci/jenkins" rel="external nofollow">جينكينز Jenkins</a>.
	</li>
	<li>
		<strong>مرحلة بناء التطبيق</strong>: <a href="https://maven.apache.org/scm.html" rel="external nofollow">Apache Maven</a> و <a href="https://github.com/gradle/gradle" rel="external nofollow">Gradle</a> و <a href="https://ant.apache.org/" rel="external nofollow">Apache Ant</a> و <a href="https://packer.io/" rel="external nofollow">Packer</a>
	</li>
	<li>
		<strong>مرحلة الاختبار</strong>: <a href="https://github.com/junit-team/junit4" rel="external nofollow">JUnit</a> و <a href="https://cucumber.io/tools/cucumber-open/" rel="external nofollow">Cucumbe</a> و <a href="https://opensource.com/article/19/4/devops-pipeline-acceptance-testing" rel="external nofollow">Selenium</a> و <a href="https://jmeter.apache.org/usermanual/get-started.html" rel="external nofollow">Apache JMeter</a>
	</li>
</ul>

<h3 id="-op-eration-s-">
	أهم أدوات عملية إدارة العمليات Operations
</h3>

<ul>
	<li>
		<strong>مرحلة الإصدار والنشر والتشغيل</strong>: <a href="https://kubernetes.io/docs/tutorials/kubernetes-basics/deploy-app/deploy-intro/" rel="external nofollow">Kubernetes</a> و <a href="https://www.hashicorp.com/products/nomad/" rel="external nofollow">Nomad</a> و <a href="https://github.com/jenkinsci/jenkins" rel="external nofollow">جينكينز Jenkins</a> و Zuul و Spinnaker و <a href="https://www.ansible.com/use-cases/application-deployment" rel="external nofollow">Ansible</a> و <a href="https://zookeeper.apache.org/" rel="external nofollow">Apache ZooKeeper</a> و <a href="https://github.com/etcd-io/etcd" rel="external nofollow">etcd</a> و <a href="https://github.com/Netflix/archaius/wiki/Overview" rel="external nofollow">Netflix Archaius</a> و <a href="https://www.terraform.io/" rel="external nofollow">Terrafo</a>
	</li>
	<li>
		<strong>مرحلة المراقبة</strong>: <a href="https://prometheus.io/" rel="external nofollow">Prometheus</a> و <a href="https://github.com/NagiosEnterprises/nagioscore" rel="external nofollow">Nagios</a> و <a href="https://github.com/influxdata/influxdb" rel="external nofollow">InfluxDB</a> و <a href="https://www.fluentd.org/" rel="external nofollow">Fluentd</a> وكما هو موضح، فقد دمجنا مرحلة التشغيل الأخيرة مع مرحلتي الإصدار والنشر بسبب تشابه أدواتها.
	</li>
</ul>

<h2 id="-">
	الخاتمة
</h2>

<p>
	بهذا نصل لختام مقالنا الذي وضحنا فيه منهجية DevOps التي تهدف لربط فريق تطوير التطبيقات وفريق العمليات في وحدة متماسكة، وهي منهجية ذات شعبية متزايدة تختلف عن الطرق القديمة والتقليدية المستخدمة في إدارة الأنظمة وتطوير البرمجيات حيث كانت الفرق تعمل بمعزل عن بعضها، وتشبه كما وضحنا منهجية أجايل في بعض النقاط ولكنها ليست مثلها تمامًا.
</p>

<p>
	ترجمة، وبتصرّف، للمقال <a href="https://opensource.com/article/20/2/devops-beginners" rel="external nofollow">A beginner's guide to everything DevOps</a> لكاتبه Sameer S Paradkar.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/general/%D8%AA%D8%B9%D9%84%D9%85-devops/" rel="">المدخل الشامل لتعلم DevOps</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/general/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AD%D9%82%D9%8A%D9%82-%D9%86%D9%87%D8%AC-devsecops-%D8%A8%D9%86%D8%AC%D8%A7%D8%AD-r524/" rel="">كيفية تحقيق نهج DevSecOps بنجاح</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/general/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%B9%D9%86-%D8%A7%D9%84%D9%86%D8%B4%D8%B1-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-continuous-delivery-r806/" rel="">دليلك الشامل عن النشر المستمر Continuous Delivery</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/entrepreneurship/business/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-%D9%84%D9%85%D9%86%D9%87%D8%AC%D9%8A%D8%A9-%D8%A3%D8%AC%D8%A7%D9%8A%D9%84-agile-r1047/" rel="">دليل المبتدئين لمنهجية أجايل Agile</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/entrepreneurship/business/%D9%85%D8%A7-%D9%87%D9%88-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B1%D9%86-lean-software-development%D8%9F-r1094/" rel="">ما هو إطار عمل تطوير البرمجيات المرن Lean Software Development</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">844</guid><pubDate>Sun, 04 May 2025 09:08:01 +0000</pubDate></item><item><title>&#x627;&#x644;&#x641;&#x631;&#x642; &#x628;&#x64A;&#x646; &#x627;&#x644;&#x62E;&#x62F;&#x645;&#x627;&#x62A; &#x627;&#x644;&#x633;&#x62D;&#x627;&#x628;&#x64A;&#x629; SaaS &#x648; PaaS &#x648; Iaas</title><link>https://academy.hsoub.com/devops/cloud-computing/%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%B3%D8%AD%D8%A7%D8%A8%D9%8A%D8%A9-saas-%D9%88-paas-%D9%88-iaas-r843/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_04/SaaSPaaSIaas.png.0fd3b83aeaa14fed747c01f2cf91f7ce.png" /></p>
<p>
	تعتمد الأعمال الرقمية حاليًا على الخدمات السحابية لتسهيل التفاعل مع العملاء، ويتطلب الأمر تجميع وتخزين ومعالجة كميات هائلة من البيانات قبل تقديمها إلى المستخدم النهائي. وهنا يأتي دور تطبيقات الويب السحابية. فعندما نتكلم عن الخدمات السحابية نتذكر مباشرة النماذج التالية:
</p>

<ul>
	<li>
		البرمجيات كخدمات سحابية Software as a services واختصارًا SaaS
	</li>
	<li>
		منصات العمل كخدمات سحابية Platform as a Service واختصارًا PaaS
	</li>
	<li>
		البُنى التحتية كخدمات سحابية Infrastructure as Service واختصارًا IaaS
	</li>
</ul>

<p>
	سنناقش في مقالنا نماذج الأعمال الثلاث السابقة بالتفصيل لتكوين فكرة واضحة عن فائدة الخدمات السحابية لأعمالنا. لكننا سنلقي نظرة أولًا على مفهوم الحوسبة السحابية قبل أن نخوض في خدماتها.
</p>

<h2 id="">
	ما هي الحوسبة السحابية
</h2>

<p>
	<a href="https://academy.hsoub.com/devops/cloud-computing/" rel="">الحوسبة السحابية</a> هي طريقة حديثة في الوصول إلى البيانات والمعلومات عبر شبكة الإنترنت بدلًا من الأقراص الصلبة، وهي وسيلة سريعة وآمنة وأكثر فعالية من أنظمة التخزين التقليدية. وقد ازداد استخدام الحوسبة السحابية حاليًا وفي مختلف القطاعات لكونها توفر حلًا ناجحًا للأعمال النامية أو التي أسست حديثًا نظرًا لحرية التوسع عند الحاجة.
</p>

<p>
	عند استخدام الخدمات السحابية لن نعتمد على عتاد أجهزتنا المحلية، إذ يمكننا الوصول إلى بياناتنا افتراضيًا ومن أي مكان، وطالما أنها متاحة على الشبكة، سنتمكن من الوصول إليها في أي وقت، فلن نضطر إلى استثمار الكثير على العتاد الصلب عند إطلاق أو توسيع أعمالنا بفضل الحوسبة السحابية، وكل ما علينا حجز مساحات إضافية عندما نحتاج لتوسيع العمل.
</p>

<h2 id="-1">
	أنواع الخدمات السحابية
</h2>

<p>
	فيما يلي مقارنة سريعة بين البرمجيات والمنصات والبنى التحتية كخدمات:
</p>

<table>
	<thead>
		<tr>
			<th style="text-align:right;">
				 
			</th>
			<th style="text-align:right;">
				<strong>IaaS</strong>
			</th>
			<th style="text-align:right;">
				<strong>PaaS</strong>
			</th>
			<th style="text-align:right;">
				<strong>SaaS</strong>
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td style="text-align:right;">
				طبيعة الخدمة
			</td>
			<td style="text-align:right;">
				تقدم أساسًا لإنشاء البنى التحتية للخدمات السحابية وتؤمن نموذج الدفع وفقًا للاستخدام
			</td>
			<td style="text-align:right;">
				أطراف خارجية تؤمن أدوات أو تطبيقات للمستخدمين عبر الإنترنت من خلال خدمات البنية التحتية الخاصة بهم
			</td>
			<td style="text-align:right;">
				تؤمن وصولًا إلى تطبيقات الويب عبر نموذج الدفع وفقًا للاستخدام
			</td>
		</tr>
		<tr>
			<td style="text-align:right;">
				الإيجابيات
			</td>
			<td style="text-align:right;">
				مقبولة التكلفة ومرنة وقابلة للاسترجاع عند حدوث المشاكل. وكذلك سهلة الوصول ويمكن الاعتماد عليه
			</td>
			<td style="text-align:right;">
				مدروسة التكاليف وانتاجية متزايدة. متجاوبة ورشيقة. سهلة الوصول وقابلة للتوسع بسهولة.
			</td>
			<td style="text-align:right;">
				قابلة للتوسع وسهل الوصول ومقبولة التكلفة. كما أنها سهلة الترقية والنشر
			</td>
		</tr>
		<tr>
			<td style="text-align:right;">
				السلبيات
			</td>
			<td style="text-align:right;">
				صعوبة التحكم بها وتعاني بعض المشاكل الأمنية
			</td>
			<td style="text-align:right;">
				تعاني مشاكل في التوافق وتغييرات في موزعي الخدمة
			</td>
			<td style="text-align:right;">
				أمان غير كاف للبيانات وتحكم أقل
			</td>
		</tr>
		<tr>
			<td style="text-align:right;">
				مزودو الخدمة
			</td>
			<td style="text-align:right;">
				خدمات أمازون ويب AWS و محرك حوسبة جوجل GCE وديجتال أوشن DigitalOcean
			</td>
			<td style="text-align:right;">
				AWS ElasticBeanstalk و Apache و OpenShift و Heroku
			</td>
			<td style="text-align:right;">
				Google Workspace و Salesforce و Cisco و WebEx و Dropbox
			</td>
		</tr>
	</tbody>
</table>

<h3 id="iaas">
	البُنى التحتية كخدمات سحابية IaaS
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="170911" href="https://academy.hsoub.com/uploads/monthly_2025_04/01_Iaas.jpg.6e055ad1c183c342fa61c192a1d3d540.jpg" rel=""><img alt="01 iaas" class="ipsImage ipsImage_thumbnailed" data-fileid="170911" data-unique="1e4t3zogf" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/01_Iaas.thumb.jpg.dfe883cd45a5918d95067c025ab452b9.jpg"> </a>
</p>

<p>
	تؤمن خدمة البنية التحتية السحابية وحدات البناء الأساسية للبنية التحتية لأي سحابة وتقدم موارد حاسوبية مثل المعالجة والآلات الافتراضية والشبكات وأكثر. وتسهل IaaS دعم الأعمال الصغيرة والمنظمات التي تستهدف حلولًا سحابية غير مكلفة وتعمل وفق نموذج الدفع وفقًا للاستخدام pay as you go وبالتالي سيدفع المستخدم تكلفة الخدمات التي يحتاجها فقط دون أية تكاليف إضافية، وهي متاحة لتوزيع الخدمات والموارد بشكل عام أو خاص أو هجين.
</p>

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

<p>
	تساعد IaaS في توفير الوقت والتكلفة لأن مزود الخدمة هو من يهتم بإدارة العتاد الصلب. وطالما أن استخدام تلك الموارد هو فقط عند الحاجة، لن يكون هنالك وجود لمصادر مهملة، وستدفع فقط على ما تستخدمه فعليًا.
</p>

<p>
	من الأمثلة عليها نجد: خدمة أمازون ويب AWS، ومحرك حوسبة جوجل GCE، ومايكروسوفت آزور، وديجتال أوشن DigitalOcean.
</p>

<h4 id="iaas-1">
	متى نستخدم نموذج IaaS
</h4>

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

<h4 id="-2">
	الإيجابيات
</h4>

<ul>
	<li>
		غير مكلفة
	</li>
	<li>
		مرنة
	</li>
	<li>
		إمكانية استعادة البيانات عند حدوث كوارث
	</li>
	<li>
		سهل الوصول
	</li>
	<li>
		موثوقة ويمكن الاعتماد عليها
	</li>
</ul>

<h4>
	السلبيات
</h4>

<ul>
	<li>
		صعوبة التحكم
	</li>
	<li>
		مشاكل في أمان البيانات
	</li>
</ul>

<h3 id="paas">
	منصات العمل كخدمات سحابية PaaS
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="170912" href="https://academy.hsoub.com/uploads/monthly_2025_04/02_Paas_a.jpg.8543292ef780c5a960a4223355d02789.jpg" rel=""><img alt="02 paas a" class="ipsImage ipsImage_thumbnailed" data-fileid="170912" data-unique="wbwfrgmmn" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/02_Paas_a.thumb.jpg.ed7c5afe153da3d0ce4ed8d7ec654b36.jpg"> </a>
</p>

<p>
	نموذج PaaS هو نموذج خدمات سحابية توفر فيه أطراف خارجية أدوات عبر الإنترنت للمطورين، معتمدة على بنية تحتية خاصة بها لتطوير التطبيقات. حيث يمكن للمطورين من خلال PaaS إنشاء تطبيقات قابلة للتوسع دون الحاجة لإعداد وإدارة قواعد البيانات والخوادم والشبكات والبنى التحتية لتخزين البيانات.
</p>

<p>
	ويستفيد المطورون الجدد من PaaS في تسهيل تطوير التطبيقات، وسيتمكن أي مطور من استخدام متصفحه فقط لتطوير التطبيق، كما تتحمل PaaS مسؤولية تحديث البنية التحتية الخاصة بنا وبالتالي لن نقلق بشأن صيانة تطبيقاتنا. كما يستفيد مطورو الأعمال من PaaS لكونها تؤمن بيئة عمل جماعية للمطورين الذين يعملون على المشروع ذاته، وتقدم أسلوبًا سريعًا في إنشاء التطبيقات نظرًا لسهولة توسيعها ومرونتها.
</p>

<p>
	من الأمثلة عليها نجد: AWS Elastic Beanstalk و Apache Stratos و Force.com و OpenShift و Heroku
</p>

<h4 id="paas-1">
	متى نستخدم نموذج PaaS
</h4>

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

<h4 id="-3">
	الإيجابيات
</h4>

<ul>
	<li>
		تكلفة مناسبة
	</li>
	<li>
		إنتاجية متزايدة
	</li>
	<li>
		رشيقة ومتجاوبة
	</li>
	<li>
		سهلة التوسع
	</li>
	<li>
		سهلة الوصول
	</li>
</ul>

<h4>
	السلبيات
</h4>

<ul>
	<li>
		مشاكل في التوافقية
	</li>
	<li>
		تغيّر مقدمي الخدمات
	</li>
</ul>

<h3 id="saas">
	البرمجيات كخدمات سحابية SaaS
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="170913" href="https://academy.hsoub.com/uploads/monthly_2025_04/03_Saas.jpg.92bc3857558841d6449903f9357a4e71.jpg" rel=""><img alt="03 saas" class="ipsImage ipsImage_thumbnailed" data-fileid="170913" data-unique="0q26v7sj3" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/03_Saas.thumb.jpg.2489aecf46bdf8267b3328946e743b65.jpg"> </a>
</p>

<p>
	توفر لنا <a href="https://academy.hsoub.com/entrepreneurship/tips/%D9%85%D8%AD%D8%A7%D8%B3%D8%A8%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D9%8A%D8%A9-saas-r1089/" rel="">البرمجيات كخدمة SaaS </a>إمكانية الوصول إلى تطبيقات الويب عبر الإنترنت، ولا حاجة معها إلى تنزيل أية أدوات أو برمجيات، وقد تكون مجانية أو تعمل وفق مبدأ الدفع وفقًا للاستخدام. ويمكن للمستخدمين الوصول إلى التطبيقات عبر أي جهاز بصرف النظر عن البنية التحتية لمقدم الخدمة أو صيانة التطبيقات أو أي شيء آخر، فهي أمور يديرها ويحميها مزوّد الخدمة السحابية.
</p>

<p>
	تفضل كثير من الأعمال استخدام خدمة SaaS نظرًا لانتشارها الواسع وعدم الحاجة إلى تكاليف خاصة أو تنزيل وتثبيت تلك البرمجيات. مع ذلك، تعتمد هذه الخدمة كليًا على موزعين خارجيين وليس للمستخدم القدرة على التحكم بالخدمة أو تغييرها.
</p>

<p>
	من الأمثلة عنها نجد: Google Workspace و Salesforce و Cisco WebEx و Dropbox.
</p>

<h4 id="saas-1">
	متى نستخدم نموذج SaaS
</h4>

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

<p>
	يمكن للأعمال الصغيرة الاستفادة من SaaS في حال لم نمتلك الميزانية الكافية أو كادر العمل لبناء تطبيقات خاصة بها. ويستخدم العديد من متخصصي تقانة المعلومات والمنظمات تطبيقات SaaS، وبإمكان مستخدمي B2B و B2C الاستفادة من تطبيقات SaaS على خلاف الخدمات السحابية الأخرى
</p>

<h4 id="-4">
	الإيجابيات
</h4>

<ul>
	<li>
		قابلة للتوسع وسهلة الوصول
	</li>
	<li>
		غير مكلفة
	</li>
	<li>
		سهلة التحديث
	</li>
	<li>
		سهلة التوزيع
	</li>
</ul>

<h4>
	السلبيات
</h4>

<ul>
	<li>
		لا تقدم حماية كافية للبيانات
	</li>
	<li>
		تحكم أقل
	</li>
</ul>

<h2 id="saaspaasiaas-1">
	الاختلافات بين الخدمات السحابية SaaS و PaaS و Iaas
</h2>

<p>
	عندما نقارن بين هذه الخدمات السحابية من ناحية المرونة تبرز خدمة IaaS. إذ تعتمد المرونة تمامًا على موزع الخدمة الذي نختاره، وكذلك الأمر من ناحية الأمان. وتُدفع تكاليف هذه الخدمة عادة بالساعة وفقًا للاستخدام وبالتالي قد ترتفع تكاليفها نظرًا لأسلوب الدفع الدقيق المرتبط بها.
</p>

<p>
	من ناحية أخرى، تعالج PaaS مشكلة البرمجة المتقدمة عالية المستوى بتسهيل وتبسيط العمليات، مما يجعل عملية تطوير التطبيقات أقل كلفة وزمنًا. وبالنسبة إلى التكلفة، فستزداد مع توسيع التطبيق ونموه. وبمجرد أن نلتزم مع موزع محدد فسنكون مقيدين ببيئة العمل والواجهة التي نختارها.
</p>

<p>
	أخيرًا، لخدمة SaaS سقف سعر فهي أرخص من كلتا الخدمتين السابقتين، وهي نعمة حقيقية للأشخاص والأعمال الصغيرة. لكن في المقابل سيكون تحكمنا في الخدمة محدودًا أو غير متاح، فمزود الخدمة هو من يدير معظم تفاصيلها.
</p>

<p>
	يمثل المخطط التالي الحجم السوقي للخدمات السحابية بين عامي 2018 و2024، وشعبية كل خدمة من الخدمات السابقة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="170909" href="https://academy.hsoub.com/uploads/monthly_2025_04/04_cloud-market-size.png.a79454640a6f262fb82f7963b53c66ab.png" rel=""><img alt="04 cloud market size" class="ipsImage ipsImage_thumbnailed" data-fileid="170909" data-unique="gt3q1btbt" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/04_cloud-market-size.png.a79454640a6f262fb82f7963b53c66ab.png"> </a>
</p>

<p style="text-align: center;">
	<a href="https://www.t4.ai/industry/cloud-computing-market-share" rel="external nofollow">المصدر</a>
</p>

<p>
	بالنسبة لحرية التحكم بالخدمة سنجد أن نموذج IaaS في المقدمة، فهو يسمح لنا بإدارة التطبيقات والبيانات والبرمجيات الوسيطة ونظام التشغيل. بينما يسمح لنا نموذج PaaS بإدارة البيانات والتطبيقات فقط، ويدير مقدم الخدمة معظم أو كل النواحي في SaaS.
</p>

<h2 id="-5">
	اختيار الخدمة السحابية المناسبة
</h2>

<p>
	علينا التفكير أولًا بحجم تبادل البيانات في موقعك أو حركة المرور إليه ونستغل قدرات المعالجة والتخزين التي تلائم حركة المرور تلك بأفضل ما يمكن، فقد نواجه مشكلات نحن بغنى عنها إن اخترنا خدمة سحابية غير ملائمة. وقد ينهار موقعنا إن لم تؤمن الخدمة التي اخترناها قدرات معالجة مناسبة وقد نضطر إلى دفع مبالغ إضافية على البنية التحتية السحابية حتى لو كانت حركة المرور إلى موقعنا منخفضة. وعلينا إضافة إلى ذلك أخذ عوامل مهمة أخرى بعين الاعتبار عند اختيار مزود الخدمة السحابية مثل أوقات توقف الخدمة downtime والترحيل migration لنقل التطبيقات إلى مكان آخر.
</p>

<h2 id="-6">
	خدمات حوسبة سحابية أخرى
</h2>

<p>
	إلى جانب الخدمات الثلاث التي تحدثنا عنها في الفقرات السابقة نجد خدمات أخرى مثل:
</p>

<h3 id="dbaas">
	الخدمة السحابية DBaas
</h3>

<p>
	تُعد قاعدة البيانات كخدمة سحابية DBaaS خدمة سحابية مدارة تستضيف قواعد البيانات وتسمح بالوصول إلى خدماتها دون إدارة أية برمجيات أخرى. وكغيرها من الخدمات لن نحتاج فيها إلى شراء أو إعداد عتادنا الصلب أو التعامل مع أية برمجيات لتثبيت قواعد بيانات. إذ تهتم معمارية هذه الخدمة مع الطرف المزود لها بكل شيء من النسخ الاحتياطي إلى التحديثات لضمان التوفر الدائم للخدمة ومعايير الأمان القوية.
</p>

<h4 id="-7">
	الإيجابيات
</h4>

<ul>
	<li>
		سهولة العمل والتكيف مع التغييرات.
	</li>
	<li>
		غير مكلفة
	</li>
	<li>
		موثوقة
	</li>
	<li>
		لن نحتاج لبناء منظومة قواعد بيانات أو توظيف مطوري قواعد بيانات
	</li>
	<li>
		أوقات توفرها ممتازة
	</li>
</ul>

<h4 id="-8">
	السلبيات
</h4>

<ul>
	<li>
		تحكم محدود
	</li>
	<li>
		مشاكل في خصوصية البيانات
	</li>
</ul>

<h3 id="daas">
	الخدمة السحابية Daas
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="170914" href="https://academy.hsoub.com/uploads/monthly_2025_04/05_Daas.jpg.7a817b67f3642e70db26718904aa8b97.jpg" rel=""><img alt="05 daas" class="ipsImage ipsImage_thumbnailed" data-fileid="170914" data-unique="wm892zx1e" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/05_Daas.thumb.jpg.f68041a981704a373091456993e781bf.jpg"> </a>
</p>

<p>
	تُعد البيانات كخدمة Daas نهجًا مشابهًا لخدمة SaaS، إذ تؤمن خدمات تخزين ومعالجة وتكامل البيانات سحابيًا إلى مستخدميها عبر الإنترنت، ولا تتطلب تثبيت أو إدارة أية برمجيات. وتساعد Daas المستخدمين في الحد من تضخم البيانات data sprawl والحاجة إلى مجمعات تخزين data silos وتحسّن التعاون بين فرق العمل عبر مشاركة البيانات بينهم.
</p>

<h4 id="-9">
	الإيجابيات
</h4>

<ul>
	<li>
		بيئة مقادة بالبيانات
	</li>
	<li>
		صيانة مؤتمتة
	</li>
	<li>
		تحسين نوعية البيانات
	</li>
</ul>

<h4 id="-10">
	السلبيات
</h4>

<ul>
	<li>
		مشكلات في الخصوصية
	</li>
	<li>
		تعقيد البيانات
	</li>
</ul>

<h3 id="faas">
	الخدمة السحابية Faas
</h3>

<p>
	تُعد الدوال كخدمة Functions as a Service واختصارًا FaaS خدمة سحابية ذات معمارية مبنية على الأحداث و<a href="https://academy.hsoub.com/devops/cloud-computing/%D8%B3%D8%A8%D8%B9-%D9%85%D9%86%D8%B5%D8%A7%D8%AA-%D9%85%D9%81%D8%AA%D9%88%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-%D9%84%D9%84%D8%A8%D8%AF%D8%A1-%D9%81%D9%8A-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AD%D9%88%D8%B3%D8%A8%D8%A9-%D8%AE%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%AF%D9%85-serverless-r408/" rel="">خفية الخوادم serverless.</a> تعمل هذه الخدمة على مبدأ كتابة دوال وتنفيذها كاستجابة لحدث ما، وتعتمد نموذج الدفع وفقًا للاستخدام ولن تكون هناك تكاليف إضافية.
</p>

<h4 id="-11">
	الإيجابيات
</h4>

<ul>
	<li>
		ندفع فقط مقابل ما نستخدم
	</li>
	<li>
		زيادة في إنتاجية المطور
	</li>
	<li>
		توسع تلقائي
	</li>
</ul>

<h4 id="-12">
	السلبيات
</h4>

<ul>
	<li>
		دعم محدود للعديد من التقنيات
	</li>
	<li>
		تحكم أقل بالمنظومة
	</li>
</ul>

<h2 id="-13">
	الخلاصة
</h2>

<p>
	لا بد من الاستثمار في الخدمات السحابية إن أردنا مواكبة المعايير التي تتغير باستمرار. ليس لأنها تقدم خدمة أفضل للمستخدم فقط، بل لأنها تساعد أعمالنا على النمو أيضًا. حيث تخفف الخدمات السحابية من سلبيات ومحدودية البنى التقليدية لتقانة المعلومات، وسيعتمد اختيارنا للخدمة المناسبة على طبيعة العمل والطريقة التي نريدها في تشغيل التطبيقات السحابية.
</p>

<p>
	ترجمة -وبتصرف- لمقال: <a href="https://www.cloudways.com/blog/iaas-vs-paas-vs-saas/" rel="external nofollow">IaaS vs. PaaS vs. Saas how are the differents</a> لصاحبه Sarim Javaid
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%B3%D8%AD%D8%A7%D8%A8%D8%A9-cloud-r624/" rel="">مفهوم السحابة Cloud</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D8%B3%D8%AD%D8%A7%D8%A8%D9%8A%D8%A9-r807/" rel="">مقدمة إلى الخوادم السحابية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B9%D8%A7%D9%85%D8%A9-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%AD%D9%88%D8%B3%D8%A8%D8%A9-%D8%A7%D9%84%D8%B3%D8%AD%D8%A7%D8%A8%D9%8A%D8%A9-r809/" rel="">نظرة عامة على الحوسبة السحابية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%B3%D8%AD%D8%A7%D8%A8%D9%8A%D8%A9-cloud-hosting-r812/" rel="">مقدمة إلى الاستضافة السحابية Cloud Hosting</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">843</guid><pubDate>Sun, 20 Apr 2025 13:09:01 +0000</pubDate></item><item><title>&#x62A;&#x646;&#x641;&#x64A;&#x630; &#x639;&#x645;&#x644;&#x64A;&#x627;&#x62A; CRUD &#x641;&#x64A; &#x642;&#x627;&#x639;&#x62F;&#x629; &#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; MongoDB</title><link>https://academy.hsoub.com/devops/servers/databases/mongodb/%D8%AA%D9%86%D9%81%D9%8A%D8%B0-%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-crud-%D9%81%D9%8A-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-mongodb-r842/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_04/--CRUD----MongoDB.png.7ba805bbce58cc8919061f68f2492cf8.png" /></p>
<p>
	نشرح في هذه المقالة طريقة التعامل مع نظام إدارة قواعد البيانات MongoDB الذي يمتاز بكونه نظام غير علاقي وموجه نحو المستندات document-oriented، أي أنه يخزن البيانات في هيئة مستندات، ومثله مثل بقية أنظمة إدارة قواعد البيانات فهو يتعامل مع البيانات عبر أربع عمليات أساسية نسميها اختصارًا عمليات CRUD تشمل:
</p>

<ul>
	<li>
		عمليات الإنشاء Create أي كتابة بيانات جديدة لقاعدة البيانات
	</li>
	<li>
		عمليات القراءة Read‎ وهي عمليات الاستعلام التي نحصل بواسطتها على البيانات المخزنة
	</li>
	<li>
		عمليات التحديث Update وتعني العمليات التي تسبب تغييرًا في البيانات الموجودة ضمن قاعدة البيانات
	</li>
	<li>
		عمليات الحذف Delete وهي العمليات التي تؤدي إلى إزالة البيانات نهائيًا من قاعدة البيانات
	</li>
</ul>

<p>
	لنوضح من خلال الأمثلة العملية طريقة تنفيذ هذه كل من هذه العمليات على قاعدة بيانات من نوع MongoDB.
</p>

<h2 id="">
	متطلبات العمل
</h2>

<p>
	لمتابعة سير العمل في هذا المقال يتوجب امتلاك معرفة أساسية بكيفية استخدام واجهة أوامر Mongodb المعروفة باسم Mongodb Shell وتنفيذ أوامر قاعدة البيانات.كما يجب توفّر خادم يعمل بنظام Ubuntu يحتوي على مستخدم عادي غير الجذر يمتلك صلاحيات sudo، مع تفعيل جدار الحماية الافتراضي UFW على الخادم، ويجب تثبيت <a href="https://academy.hsoub.com/devops/servers/databases/mongodb/" rel="">MongoDB</a> على الخادم، وتأمين قاعدة البيانات بإنشاء مستخدم إداري administrative user على قاعدة البيانات لديه صلاحية مسؤول وتفعيل المصادقة authentication في قاعدة البيانات لأنها تكون مُعطلة تلقائيًا. يمكن الاستعانة بمقال <a href="https://academy.hsoub.com/devops/servers/databases/mongodb/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D9%88%D8%AA%D8%A3%D9%85%D9%8A%D9%86-mongodb-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r435/" rel="">كيفية تثبيت وتأمين MongoDB على أوبونتو</a> فهو يتضمن كل الخطوات اللازمة.
</p>

<h2 id="1mongodb">
	الاتصال بخادم MongoDB
</h2>

<p>
	قبل البدء بتنفيذ عمليات CRUD على البيانات، علينا الاتصال بقاعدة بيانات MongoDB عبر الصدفة MongoDB Shell. فإذا كان خادم MongoDB الذي نعمل عليه مبني على خادم بعيد وليس على جهازنا المحلي، نفتح جلسة <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a> للاتصال به كما يلي:
</p>

<pre class="ipsCode">$ ssh sammy@your_server_ip
</pre>

<p>
	ولمطالعة المزيد من المعلومات حول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> ننصح بمشاهدة الفيديو التالي:<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="603" id="ips_uid_2456_6" referrerpolicy="strict-origin-when-cross-origin" src="https://academy.hsoub.com/applications/core/interface/index.html" title="ما هي تقنية SSH" width="930" data-embed-src="https://www.youtube.com/embed/CGDYjTTDOYg"></iframe>
</p>

<p>
	بعدها نتصل بتطبيق MongoDB المُثَبَّت على الخادم بواسطة مستخدم يتمتع بصلاحيات القراءة والكتابة على البيانات،  وسيكون أمر الاتصال بالصيغة التالية:
</p>

<pre class="ipsCode">$ mongo -u AdminSammy -p --authenticationDatabase admin
</pre>

<p>
	ستُطلب منا كلمة المرور لمستخدم قاعدة البيانات وبعد كتابتها ستفتح الصدفة Shell مباشرة، ويمكن التأكد من ذلك بتغيّر شكل الموجّه في النافذة الطرفية ليصبح على هذا الشكل <code>&lt;</code> عندها تكون الصدفة جاهزة لاستقبال الأوامر التي نريد توجيهها إلى الخادم
</p>

<p>
	<strong>ملاحظة</strong>: بمجرد إنشاء اتصال جديد مع خادم MongoDB سيصلنا تلقائيًا بقاعدة البيانات الاختبارية لنظام MongoDB تدعى <code>test</code>، ويمكن الانتقال إلى قاعدة بياناتنا الخاصة باستخدام الأمر <code>use</code> متبوعًا باسم قاعدة البيانات كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2456_9" style=""><span class="pln">use database_name</span></pre>

<p>
	والآن بعد أن جهزنا الاتصال بخادم MongoDB، يمكننا البدء بالعملية الأولى من عمليات CRUD وهي Create لإنشاء مستندات جديدة.
</p>

<h2 id="2">
	إنشاء المستندات
</h2>

<p>
	سننشئ مستندات MongoDB ونستخدامها لاحقًا في تطبيق الأمثلة على عمليات القراءة والتحديث والحذف. لنفترض أننا نريد إنشاء مستندات تخزن معلومات عامة عن بعض المعالم الأثرية مثل: اسم المعلم الأثري، والبلد الذي يوجد فيه، والمدينة، وإحداثيات الموقع الجغرافي.
</p>

<p>
	ستكون المستندات التي تتضمن معلومات عن أهرامات الجيزة <code>The Pyramids of Giza</code> مثلًا وفق الصيغة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2516_7" style=""><span class="pun">{</span><span class="pln">
    </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"The Pyramids of Giza"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"city"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Giza"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"country"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Egypt"</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"gps"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"lat"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">29.976480</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"lng"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">31.131302</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تُكتب مستندات MongoDB وتعالج بصيغة BSON، وهي تمثيل ثنائي لملفات <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-json-r604/" rel="">JSON</a> سهل القراءة والفهم ويوفر بعض الإضافات التي تسمح بتخزين أنواع بيانات إضافية لا تدعمها صيغة JSON. تُمثَّل البيانات الموجودة في هذه المستندات بهيئة أزواج حقل وقيمة <code>field: value</code>.
</p>

<p>
	يتضمن المستند الذي أنشأناه أربعة حقول يشير الحقل الأول إلى اسم المعلم الأثري، يليه المدينة في الحقل الثاني، والبلد في الحقل الثالث، وتحتوي هذه الحقول الثلاثة على سلاسل نصية strings، أما الحقل الأخير والمسمى <code>gps</code>، فهو مستند متداخل nested document أي مستند داخل مستند يعرض معلومات عن الموقع الجغرافي للمكان الأثري ويتضمن إحداثيات خطوط الطول والعرض يمثلها الحقلان <code>lat</code> و <code>lng</code> والقيم ضمنهما من نوع  عدد عشري.
</p>

<p>
	تنتمي المستندات في MongoDB دائمًا إلى مجموعات collections، وهي تكافئ الجداول tables في قواعد البيانات العلاقية مثل <a href="https://academy.hsoub.com/devops/servers/databases/mysql/" rel="">MySQL</a> أما المستندات في MongoDB فهي تشبه الصفوف rows ضمن كل جدول 
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="639" id="ips_uid_2456_7" referrerpolicy="strict-origin-when-cross-origin" src="https://academy.hsoub.com/applications/core/interface/index.html" title="مقارنة قواعد البيانات MySQL و MongoDB" width="930" data-embed-src="https://www.youtube.com/embed/UL-gPrgoRFo"></iframe>
</p>

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

<p>
	لنكتب إذًا أمر الإنشاء التالي في نافذة الصدفة:
</p>

<pre class="ipsCode">&gt; db.monuments.insertOne(
&gt; {
&gt;  "name": "The Pyramids of Giza",
&gt;  "city": "Giza",
&gt;  "country": "Egypt",
&gt;  "gps": {
&gt;  "lat": 29.976480,
&gt;  "lng": 31.131302
&gt;   }
&gt;  }
&gt; )
</pre>

<p>
	نلاحظ هنا أننا لم ننشئ المجموعة <code>monuments</code> مُسبقًا قبل إدراج المستندات ضمنها، فنظام MongoDB مرن من هذه الناحية فالمجموعة ستنشأ تلقائيًا مع إدراج أول مستند فيها، وبذلك فإن التابع <code>insertOne()‎</code> لم يُنشئ المستند فحسب بل أنشأ المجموعة أيضًا.
</p>

<p>
	سنحصل على الخرج التالي الذي يعلمنا بنجاح تنفيذ التابع <code>insertOne()‎</code> وإنشاء المستند المطلوب، بالإضافة إلى إرجاع مُعَرّف الكائن <code>ObjectId</code> الخاص به والذي ينشأ تلقائيًا لكل كائن أو مستند جديد:
</p>

<pre class="ipsCode">{
  "acknowledged" : true,
  "insertedId" : ObjectId("6105752352e6d1ebb7072647")
}
</pre>

<p>
	ينبغي أن يتضمن كل مستند في مجموعة MongoDB حقلًا خاصًا بالمُعَرِّف الفريد <code>id_</code> للمستند والذي كمفتاح رئيسي primary key، يمكنك إعطاء الحقل <code>id_</code> أي قيمة نختارها طالما أننا ملتزمون تمامًا بالحفاظ على قيمة فريدة له وعدم تكراره بين المستندات، وبكل الأحوال إذا أهملنا هذا الحقل عند إنشاء أي مستند جديد فإن MongoDB ستُنشئه نيابةً عنا ويكون بهيئة كائن <code>ObjectId</code> كما شاهدنا في الخرج السابق.
</p>

<p>
	للتأكد من صحة الإنشاء، يمكن تفقد عدد المستندات في المجموعة <code>monuments</code> باستخدام الأمر التالي:
</p>

<pre class="ipsCode">&gt; db.monuments.count()
</pre>

<p>
	سيُرجع التابع <code>count</code> القيمة <code>1</code> لأننا أدخلنا مستندًا واحدًا فقط في المجموعة:
</p>

<pre class="ipsCode">1
</pre>

<p>
	توفر MongoDB طريقةً سهلة لإدخال عدة مستندات إلى المجموعة بعملية واحدة عبر التابع <code>insertMany</code> فالإدخال الفردي قد يصبح مرهقًا إذا كانت المستندات معقدة وكثيرة العدد. على سبيل المثال ينشئ الأمر التالي ستة مستندات دفعة واحدة ضمن المجموعة <code>monuments</code>:
</p>

<pre class="ipsCode">&gt; db.monuments.insertMany([
&gt;    {"name": "The Valley of the Kings", "city": "Luxor", "country": "Egypt", "gps": { "lat": 25.746424, "lng": 32.605309 }},
&gt;    {"name": "Arc de Triomphe", "city": "Paris", "country": "France", "gps": { "lat": 48.873756, "lng": 2.294946 }},
&gt;    {"name": "The Eiffel Tower", "city": "Paris", "country": "France", "gps": { "lat": 48.858093, "lng": 2.294694 }},
&gt;    {"name": "Acropolis", "city": "Athens", "country": "Greece", "gps": { "lat": 37.970833, "lng": 23.726110 }},
&gt;    {"name": "The Great Wall of China", "city": "Huairou", "country": "China", "gps": { "lat": 40.431908, "lng": 116.570374 }},
&gt;    {"name": "The Statue of Liberty", "city": "New York", "country": "USA", "gps": { "lat": 40.689247, "lng": -74.044502 }}
&gt; ])
</pre>

<p>
	تُحيط الأقواس المربعة <code>[</code><code>]</code> بالمستندات الستة في المثال أعلاه، فهي تشكل مصفوفة من العناصر، ويفصل بين كل عنصر وآخر فاصلة <code>, </code>فعندما نريد تمرير عدة كائنات في MongoDB أيًّا كان نوعها نمررها بشكل مصفوفة بين قوسين مربعين.
</p>

<p>
	بمجرد تنفيذ أمر الإنشاء السابق سيظهر الخرج التالي ليعلمنا بنجاح العملية ويرجع المعرّفات الفريدة لكل مستند:
</p>

<pre class="ipsCode">{
  "acknowledged" : true,
  "insertedIds" : [
    ObjectId("6105770952e6d1ebb7072648"),
    ObjectId("6105770952e6d1ebb7072649"),
    ObjectId("6105770952e6d1ebb707264a"),
    ObjectId("6105770952e6d1ebb707264b"),
    ObjectId("6105770952e6d1ebb707264c"),
    ObjectId("6105770952e6d1ebb707264d")
  ]
}
</pre>

<p>
	لنتأكد من صحة أمر إنشاء المستندات الجديدة بالاستعلام عن عدد المستندات في المجموعة <code>monuments</code>:
</p>

<pre class="ipsCode">&gt; db.monuments.count()
</pre>

<p>
	سنحصل على الخرج التالي الذي يوضح أن عدد المستندات في المجموعة أصبح <code>7</code> بعد إضافة ستة مستندات جديدة إلى المستند الأول:
</p>

<pre class="ipsCode">7
</pre>

<p>
	بعد أن أنشانا المستندات، لننتقل للخطوة التالية التي تشرح كيفية قراءة هذه البيانات من هذه المستندات.
</p>

<h2 id="3">
	قراءة المستندات
</h2>

<p>
	أصبح لدينا مجموعة تتضمن سبعة مستندات، ويمكننا الاستعلام عنها من قاعدة البيانات، سنعرض في البداية طريقة الاستعلام عن جميع مستندات المجموعة من دون استثناء، ثم سنتعلم تضييق نطاق الاستعلام لنحصل على مجموعة أقل من المستندات.
</p>

<p>
	لنبدأ بقراءة المستندات السبعة من المجموعة <code>monuments</code> بعملية واحد باستخدام التابع <code>()find</code>:
</p>

<pre class="ipsCode">&gt; db.monuments.find()
</pre>

<p>
	يؤدي استخدام التابع <code>()find</code> بدون وسطاء إلى إرجاع جميع البيانات الموجودة في المجموعة المحددة وهي في حالتنا المجموعة <code>monuments</code> وسنحصل بالتالي على هذا الخرج:
</p>

<pre class="ipsCode">{ "_id" : ObjectId("6105752352e6d1ebb7072647"), "name" : "The Pyramids of Giza", "city" : "Giza", "country" : "Egypt", "gps" : { "lat" : 29.97648, "lng" : 31.131302 } }
{ "_id" : ObjectId("6105770952e6d1ebb7072648"), "name" : "The Valley of the Kings", "city" : "Luxor", "country" : "Egypt", "gps" : { "lat" : 25.746424, "lng" : 32.605309 } }
{ "_id" : ObjectId("6105770952e6d1ebb7072649"), "name" : "Arc de Triomphe", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.873756, "lng" : 2.294946 } }
{ "_id" : ObjectId("6105770952e6d1ebb707264a"), "name" : "The Eiffel Tower", "city" : "Paris", "country" : "France", "gps" : { "lat" : 48.858093, "lng" : 2.294694 } }
{ "_id" : ObjectId("6105770952e6d1ebb707264b"), "name" : "Acropolis", "city" : "Athens", "country" : "Greece", "gps" : { "lat" : 37.970833, "lng" : 23.72611 } }
{ "_id" : ObjectId("6105770952e6d1ebb707264c"), "name" : "The Great Wall of China", "city" : "Huairou", "country" : "China", "gps" : { "lat" : 40.431908, "lng" : 116.570374 } }
{ "_id" : ObjectId("6105770952e6d1ebb707264d"), "name" : "The Statue of Liberty", "city" : "New York", "country" : "USA", "gps" : { "lat" : 40.689247, "lng" : -74.044502 } }
</pre>

<p>
	تعرض الصدفة MongoDB Shell المستندات السبعة بكامل حقولها، ونلاحظ أن لكل حقل مُعَرّف خاص <code>id_</code> رغم أننا لم نُعَرِّفه عند إنشاء المستندات، وذلك لأن MongoDB كما ذكرنا تُنشئ المُعرفات تلقائيًا مع أوامر الإدخال وهي تعمل كمفاتيح رئيسية للمستندات الخاصة بها.
</p>

<p>
	نلاحظ أيضًا أن طريقة عرض البيانات ضمن الصدفة MongoDB Shell صعب القراءة نوعًا ما، وخاصة إذا كانت المستندات كثيرة الحقول أو تتضمن مستندات متداخلة فالمستند الواحد سيظهر بجميع بياناته بسطر واحد، ويمكن تحسين الإظهار وجعله أسهل في القراءة باستخدام الخاصية <code>()pretty</code> مع التابع <code>()find</code> كما يلي:
</p>

<pre class="ipsCode">&gt; db.monuments.find().pretty()
</pre>

<p>
	الآن ستعرض الصدفة MongoDB Shell الخرج بالطريقة التالية، حيث تفرد لكل مستند عدة أسطر يبدأ كل منها بمسافة بادئة indentation:
</p>

<pre class="ipsCode">{
  "_id" : ObjectId("6105752352e6d1ebb7072647"),
  "name" : "The Pyramids of Giza",
  "city" : "Giza",
  "country" : "Egypt",
  "gps" : {
    "lat" : 29.97648,
    "lng" : 31.131302
  }
}
{
  "_id" : ObjectId("6105770952e6d1ebb7072648"),
  "name" : "The Valley of the Kings",
  "city" : "Luxor",
  "country" : "Egypt",
  "gps" : {
    "lat" : 25.746424,
    "lng" : 32.605309
  }
}
. . .
</pre>

<p>
	لنستخدم بعض الوسطاء مع التابع <code>()find</code> لتضييق نتائج البحث فمثالنا السابق أرجع كافة مستندات المجموعة من دون أي تصفية.
</p>

<p>
	ذكرنا سابقًا أن MongoDB تعطي مُعَرِّفًا فريدًا لكل مستند، لنأخذ مثلًا المستند المُسمى "The Valley of the Kings" والذي يشير لأحد المعالم الأثرية في مجموعتنا وله المُعرِّف <code>‎ObjectId("6105770952e6d1ebb7072648")‎</code> الذي يميزه عن غيره، علمًا أن مُعرِّف الكائن <code>ObjectId</code> ليس من مجرد سلسلة ست عشرية فهو بحد ذاته كائن أيضًا وهو نوع خاص من البيانات تستخدمه MonogoDB لتخزين مُعَرِّفات الكائنات،  ويمكن مطالعة المزيد عن <a href=")" rel="">أنواع البيانات التي تتعامل معها MongoDB</a>.
</p>

<p>
	سُنمَرِّر الآن هذا المُعَرِّف كوسيط للتابع <code>()find</code> بهيئة مستند يسمى مستند تصفية الاستعلام query filter document، تتمتع مستندات تصفية الاستعلام بنفس بنية مستندات مجموعة البيانات أي بنية حقل وقيمة لكنها تستخدم لغرض خاص هو تصفية نتائج الاستعلام.
</p>

<p>
	يتضمن مستند تصفية الاستعلام في حالتنا الحقل <code>id_</code> والقيمة التي تمثل <code>Objectid</code> للمستند "The Valley of the Kings"، وسيأخذ الاستعلام عندها الصيغة التالية:
</p>

<pre class="ipsCode">&gt; db.monuments.find({"_id": ObjectId("6105770952e6d1ebb7072648")}).pretty()
</pre>

<p>
	يستخدم مستند تصفية الاستعلام في هذا المثال شرط المساواة لإرجاع النتائج، فستُرجع <code>()find</code> المستند الذي يطابق معرفه <code>id_</code> القيمة <code>‎ObjectId("6105770952e6d1ebb7072648")‎</code> فقط ولن تُرجع معه أي مستند آخر.
</p>

<p>
	سنحصل في هذه الحالة على الخرج التالي:
</p>

<pre class="ipsCode">{
  "_id" : ObjectId("6105770952e6d1ebb7072648"),
  "name" : "The Valley of the Kings",
  "city" : "Luxor",
  "country" : "Egypt",
  "gps" : {
    "lat" : 25.746424,
    "lng" : 32.605309
  }
}
</pre>

<p>
	لنجرب مثالًا آخر، سنستعلم هنا عن جميع المعالم الأثرية الموجودة في فرنسا أي التي تحقق الشرط التالي:
</p>

<pre class="ipsCode">&gt; db.monuments.find({"country": "France"}).pretty()
</pre>

<p>
	سيُرجع هذا الاستعلام نتيجتين كما يلي:
</p>

<pre class="ipsCode">{
  "_id" : ObjectId("6105770952e6d1ebb7072649"),
  "name" : "Arc de Triomphe",
  "city" : "Paris",
  "country" : "France",
  "gps" : {
    "lat" : 48.873756,
    "lng" : 2.294946
  }
}
{
  "_id" : ObjectId("6105770952e6d1ebb707264a"),
  "name" : "The Eiffel Tower",
  "city" : "Paris",
  "country" : "France",
  "gps" : {
    "lat" : 48.858093,
    "lng" : 2.294694
  }
}
</pre>

<h2 id="4">
	الخطوة 4: تحديث المستندات
</h2>

<p>
	يُقصد بتحديث المستندات Updating Documents في <a href="https://academy.hsoub.com/devops/servers/databases/mongodb/" rel="">قواعد بيانات MongoDB</a> إما تغيير بنية المستند نفسه بإضافة أو تعديل الحقول لتناسب متطلبات جديدة في التطبيق مثلًا، أو تعديل قيم البيانات المُخَزَّنة ضمن المستند، سنناقش أمثلةًعن كلا الحالتين إذ سنُعَدِّل بعض القيم في المستندات وسنضيف حقلًا جديدًا إلى مستندات المجموعة.
</p>

<p>
	توفر MongoDB طريقتين لتحديث المستندات إما فرديًا أو جماعيًا، في التحديث الجماعي للمستندات يتعين علينا تمرير القيم التي نريد تحديثها كوسيط لتابع التحديث، وفي التحديث الفردي نمرر مُعَرِّف المستند الذي نود تعديله إضافةً إلى القيم الجديدة.
</p>

<p>
	تستخدم MongoDB آلية مستند تصفية الاستعلام query filter document نفسها التي استخدمناها في الفقرة السابقة، وذلك للسماح للمستخدم بتحديد المستندات التي يود تحديثها.
</p>

<p>
	يبين المثال التالي طريقة تغيير اسم أحد المعالم الأثرية وليكن مثلًا قوس النصر <code>Arc de Triomphe</code> حيث سنغير اسمه إلى <code>Arc de Triomphe de l’Étoile</code>، باستخدام التابع <code>()updateOne</code> المخصص لتحديث مستند واحد فقط في كل عملية:
</p>

<pre class="ipsCode">&gt; db.monuments.updateOne(
&gt;   { "name": "Arc de Triomphe" },
&gt;   {
&gt;     $set: { "name": "Arc de Triomphe de l'Étoile" }
&gt;   }
&gt; )
</pre>

<p>
	سيكون اسم المستند الذي نرغب بتعديله هو الوسيط الأول للتابع <code>()updateOne</code> وهو في حالتنا <code>{ "name": "Arc de Triomphe" }</code> وهو مستند تصفية استعلام مع شرط المساواة، وبالتالي سيُرجع لنا المستند الذي يطابق اسمه تمامًا القيمة المذكورة، وسيكون هو المستند الذي ننفذ عليه التعديل.
</p>

<p>
	أما الوسيط الثاني فهو مستند التحديث update document الذي يتضمن التحديثات التي ينبغي أن تُطَبَّق عند تنفيذ التابع، يتكون مستند التحديث من عوامل تحديث operators تلعب دور المفاتيح keys، ومعاملات parameters مقابلة تمثل القيم الجديدة values. في حالتنا عامل التحديث هو <code>set$</code> وهو المسؤول عن إعطاء قيم جديدة لحقول المستند التي تُكتب بصيغة كائنات JSON مثل <code>{ "name": "Arc de Triomphe de l'Étoile" }</code> الذي يعني ضمنًا تعديل قيمة الحقل <code>name</code> لتصبح <code>Arc de Triomphe de l'Étoile</code>.
</p>

<p>
	يُرجع تابع التحديث <code>()updateOne</code> خرجًا يخبرنا بنتيجة عمله، فقد عثر على مستند واحد مطابق للشروط، وحَدَّث قيمته بنجاح، كما يلي:
</p>

<pre class="ipsCode">{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
</pre>

<p>
	<strong>تنويه</strong>: قد لا يكون مستند تصفية الاستعلام المستخدم كوسيط أول للتابع <code>()updateOne</code> مُحَددًا ودقيقًا بما يكفي لإعطائنا نتيجة واحدة فيُرجِع عدة مستندات تتوافق مع شروط الاستعلام بدلًا من واحد، في هذه الحالة سيأخذ التابع <code>()updateOne</code> أول مستند من هذه المستندات ويطَبِّق التعديلات عليه ويهمل البقية لأنه في نهاية الأمر مُخصص للعمل مع مستند واحد.
</p>

<p>
	لنتأكد الآن من نجاح عملية التحديث، سنطلب جميع المعالم الأثرية الموجودة في فرنسا <code>France</code> وفق الآتي:
</p>

<pre class="ipsCode">&gt; db.monuments.find({"country": "France"}).pretty()
</pre>

<p>
	يؤكد لنا الخرج التالي تغيير طريقة كتابة <code>Arc de Triomphe</code> وبالتالي نجاح عملية التحديث:
</p>

<pre class="ipsCode">{
  "_id" : ObjectId("6105770952e6d1ebb7072649"),
  "name" : "Arc de Triomphe de l'Étoile",
  "city" : "Paris",
  "country" : "France",
  "gps" : {
    "lat" : 48.873756,
    "lng" : 2.294946
  }
}
. . .
</pre>

<p>
	سنستعمل الآن التابع <code>()updateMany</code> لتعديل أكثر من مستند في نفس الوقت.
</p>

<p>
	لنفترض مثلًا أننا نريد إضافة حقل إلى كل مستند من مستندات المجموعة، يبين هذا الحقل اسم المُحرِّر <code>editor</code> الذي أدخل بيانات المعلم الأثري. طالما أننا سنُنَفِّذ التعديل على جميع المستندات في المجموعة، فسنستخدم إذًا مستند تصفية استعلام فارغ بلا أي شروط وبسببه سيؤثر <code>()updateMany</code> على جميع مستندات المجموعة <code>monuments</code> ويُضيف إلى كل واحد منها حقلًا جديدًا يدعى <code>editor</code> ويحمل القيمة <code>Sammy</code> وفق التالي:
</p>

<pre class="ipsCode">&gt; db.monuments.updateMany(
&gt;   { },
&gt;   {
&gt;    $set: { "editor": "Sammy" }
&gt;   }
&gt; )
</pre>

<p>
	سيعطي هذا التابع الخرج التالي الذي يعلمنا بنتيجة التنفيذ:
</p>

<pre class="ipsCode">{ "acknowledged" : true, "matchedCount" : 7, "modifiedCount" : 7 }
</pre>

<p>
	عثرنا على سبعة مستندات مطابقة للشروط، وعَدَّلنا المستندات السبعة بنجاح.
</p>

<p>
	يمكن التأكد من إضافة الحقل الجديد إلى مستندات المجموعة بواسطة الأمر التالي:
</p>

<pre class="ipsCode">&gt; db.monuments.find().pretty()
</pre>

<p>
	وسيكون الخرج كما يلي:
</p>

<pre class="ipsCode">{
  "_id" : ObjectId("6105752352e6d1ebb7072647"),
  "name" : "The Pyramids of Giza",
  "city" : "Giza",
  "country" : "Egypt",
  "gps" : {
    "lat" : 29.97648,
    "lng" : 31.131302
  },
  "editor" : "Sammy"
}
{
  "_id" : ObjectId("6105770952e6d1ebb7072648"),
  "name" : "The Valley of the Kings",
  "city" : "Luxor",
  "country" : "Egypt",
  "gps" : {
    "lat" : 25.746424,
    "lng" : 32.605309
  },
  "editor" : "Sammy"

}
. . .
</pre>

<p>
	كما نلاحظ فقد أُضيف حقل جديد إلى كل مستندات المجموعة، فإعطاء اسم حقل غير موجود للعامل <code>set$</code> في تابع التحديث يعني أننا نطلب منه إنشاء هذا الحقل ومنحه القيمة المحددة.
</p>

<p>
	يُعدّ <code>set$</code> عاملًا شائع الاستخدام وسيفيدنا في كثير من الحالات، مع ذلك توفر MongoDB قائمة طويلة من عوامل التعديل التي تساعدنا على إجراء تحديثات متنوعة ومعقدة على المستندات ويمكن معرفة المزيد عنها من <a href="https://www.mongodb.com/docs/manual/reference/operator/update/" rel="external nofollow">توثيقات MongoDB الرسمية</a>.
</p>

<h2 id="5">
	حذف المستندات
</h2>

<p>
	إذا احتجنا لحذف بعض البيانات القديمة من قاعدة بيانات MongoDB فيمكن استخدام تابعين للحذف الأول هو التابع <code>()deleteOne</code> لحذف مستند واحد من المستندات المطابقة لشروط مستند تصفية الاستعلام، والثاني هو التابع <code>()deleteMany</code> لحذف عدة مستندات دفعة واحدة.
</p>

<p>
	لنحاول حذف المستند الخاص بقوس النصر <code>Arc de Triomphe de l’Étoile</code> الذي عَدَّلنا اسمه قبل قليل، وذلك وفق الأمر التالي:
</p>

<pre class="ipsCode">&gt; db.monuments.deleteOne(
&gt;     { "name": "Arc de Triomphe de l'Étoile" }
&gt; )
</pre>

<p>
	نلاحظ أن مستند تصفية الاستعلام المستخدم هنا مشابه لما استخدمناه في الأمثلة السابقة عند القراءة والتحديث، ويمكن بالتأكيد استخدام أي استعلام مكتوب بطريقة صحية لتحديد المستند الذي نريد حذفه.
</p>

<p>
	نحصل بعد التنفيذ على خرج يشبه التالي:
</p>

<pre class="ipsCode">{ "acknowledged" : true, "deletedCount" : 1 }
</pre>

<p>
	يبين هذا الخرج عدد المستندات المحذوفة بموجب الأمر السابق.
</p>

<p>
	والآن لنتأكد من حذف المستند بالاستعلام عن الآثار الموجودة في فرنسا وفق التالي:
</p>

<pre class="ipsCode">&gt; db.monuments.find({"country": "France"}).pretty() 
</pre>

<p>
	ستتضمن نتيجة الاستعلام المستند الخاص ببرج إيفل <code>The Eiffel Tower</code> فقط لأننا حذفنا مستند قوس النصر <code>Arc de Triomphe de l’Étoile</code>:
</p>

<pre class="ipsCode">{
  "_id" : ObjectId("6105770952e6d1ebb707264a"),
  "name" : "The Eiffel Tower",
  "city" : "Paris",
  "country" : "France",
  "gps" : {
    "lat" : 48.858093,
    "lng" : 2.294694
  },
  "editor" : "Sammy"
}
</pre>

<p>
	لنشرح الآن طريقة حذف مستندات متعددة، ولنحذف مثلًا كافة المستندات التي أنشأها المحرر الذي اسمه <code>Sammy</code>، طبعًا سيؤدي هذا إلى حذف جميع مستندات المجموعة لأن <code>Sammy</code> هو المحرر الذي أضفناه لكافة مستندات المجموعة <code>monuments</code>:
</p>

<pre class="ipsCode">&gt; db.monuments.deleteMany(
&gt;  { "editor": "Sammy" }
&gt; )
</pre>

<p>
	سيعلمنا الخرج بحذف ستة مستندات:
</p>

<pre class="ipsCode">{ "acknowledged" : true, "deletedCount" : 6 }
</pre>

<p>
	ويمكن التأكد من النتيجة بالاستعلام عن عدد مستندات المجموعة <code>monuments</code> كما يلي:
</p>

<pre class="ipsCode">&gt; db.monuments.count()
</pre>

<p>
	سنحصل على الخرج التالي:
</p>

<pre class="ipsCode">0
</pre>

<p>
	تعني النتيجة <code>0</code> حذف جميع المستندات في المجموعة.
</p>

<h2 id="-1">
	الخاتمة
</h2>

<p>
	تعرفنا في هذا المقال على طريقة تطبيق عمليات CRUD على قاعدة بيانات MongoDB، إذ تُعَدّ هذه العمليات أساسية في إدارة البيانات ويمكن من خلالها إنشاء مستندات جديدة وتعديلها والاستعلام عنها وحذفها عند الضرورة.
</p>

<p>
	وننوه في الختام أننا عرضنا في هذا المقال طريقةً واحدة فقط من طرق تصفية الاستعلام، لكن MongoDB توفر طرقًا أخرى أقوى وأكثر تعقيدًا ويمكنكم تعلم المزيد عنها بمراجعة <a href="https://academy.hsoub.com/devops/servers/databases/mongodb/" rel="">قسم MongoDB</a> على أكاديمية حسوب و<a href="https://docs.mongodb.com/v4.4/mongo/" rel="external nofollow">توثيقات MongoDB الرسمية</a>.
</p>

<p>
	ترجمة -وبتصرف- لمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-perform-crud-operations-in-mongodb" rel="external nofollow">How To Perform CRUD Operations in MongoDB</a> لصاحبه Mateusz Papiernik.
</p>
]]></description><guid isPermaLink="false">842</guid><pubDate>Sun, 13 Apr 2025 15:00:00 +0000</pubDate></item><item><title>&#x62A;&#x648;&#x635;&#x64A;&#x627;&#x62A; &#x623;&#x633;&#x627;&#x633;&#x64A;&#x629; &#x644;&#x62D;&#x645;&#x627;&#x64A;&#x629; &#x627;&#x644;&#x62E;&#x648;&#x627;&#x62F;&#x645; &#x645;&#x646; &#x627;&#x644;&#x627;&#x62E;&#x62A;&#x631;&#x627;&#x642;</title><link>https://academy.hsoub.com/devops/servers/%D8%AA%D9%88%D8%B5%D9%8A%D8%A7%D8%AA-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-%D9%84%D8%AD%D9%85%D8%A7%D9%8A%D8%A9-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%AF%D9%85-%D9%85%D9%86-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%B1%D8%A7%D9%82-r841/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_04/-----.png.48eb87659befcda5bbeab639f902b3af.png" /></p>
<p>
	غالبًا ما نركز اهتمامنا على معرفة كيفية بناء التطبيق ونشره في البيئة السحابية، وقد نغفل دون قصد الإجراءات الأمنية الأساسية التي ينبغي علينا توفيرها على الخادم السحابي قبل نشر التطبيقات عليه وإتاحتها للعامة. سنشارك في هذا المقال بعض التوصيات التي يمكننا تنفيذها على الخادم لحمايته من الهجمات الإلكترونية وجعله بيئة آمنة للنشر. بالطبع لن تغني هذه التوصيات العامة عن التدابير المصممة خصيصًا للتطبيق والتي يمكن تنفيذها بعد النشر، لكنها تُعَدّ انطلاقة جيدة يمكن البناء عليها.
</p>

<h2 id="ssh">
	استخدام مفاتيح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>
</h2>

<p>
	<abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> هي اختصار لعبارة Secure Shell، وهو بروتوكول آمن ومُشفر للاتصال بالخوادم وإدارتها، وهو شائع الاستخدام فأي شخص يتعامل مع <a href="https://academy.hsoub.com/devops/servers/" rel="">الخوادم</a> سيقضي معظم وقته متصلًا بالخادم عبر جلسة اتصال طرفية terminal تستخدم <a href="https://academy.hsoub.com/devops/security/ssh/" rel="">برتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a>، أما مفاتيح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> أو <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> Keys فهي بديل آمن لعمليات تسجيل الدخول المستندة إلى كلمة مرور أي تلك التي تعتمد الاستيثاق بكلمة المرور، لأن هذه المفاتيح تستخدم <a href="https://academy.hsoub.com/apps/general/%D8%A7%D9%84%D8%AA%D8%B4%D9%81%D9%8A%D8%B1-%D9%88%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D8%A7%D8%AA%D9%87-%D9%81%D9%8A-%D8%A7%D9%84%D8%B9%D8%A7%D9%84%D9%85-%D8%A7%D9%84%D8%B1%D9%82%D9%85%D9%8A-r380/" rel="">التشفير</a> لتأمين تسجيل الدخول إلى الخادم، وتُعَدُّ إجراءً ضروريًا يوصى به لجميع مستخدمي <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>.
</p>

<p>
	لإجراء الاستيثاق authentication من خلال مفاتيح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> نحتاج لتوليد مفتاحين، مفتاح عام public key مكن مشاركته مع الطرف الآخر، ومفتاح خاص private key سري يحتفظ به المستخدم ولا يشاركه مع أحد، تُسمى هذه الآلية التشفير غير المتماثل asymmetric encryption وقد تصادفنا في حالات استخدام أخرى.
</p>

<p style="text-align: center;">
	<img alt="001 مفاتيح SSH" class="ipsImage ipsImage_thumbnailed" data-fileid="170414" data-ratio="37.43" data-unique="9ztex9pt6" style="width: 700px; height: auto;" width="806" src="https://academy.hsoub.com/uploads/monthly_2025_04/img01-ssh-key-auth.png.83a792dc369aa79961ef4a141e170218.png">
</p>

<p>
	بعد توليد مفتاح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> العام ينبغي لنا وضعه على الخادم ضمن المسار المخصص له وهو عادةً المجلد <code>‎~/.<abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr>/authorized_keys</code>، وبعدها يمكننا الاتصال بالخادم باستخدام مفاتيح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>، ومعرفة المزيد عن الاستيثاق باستخدام كلمة المرور والاستيثاق باستخدام المفاتيح ننصح بمشاهدة الفيديو التالي:
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="400" id="ips_uid_6520_6" referrerpolicy="strict-origin-when-cross-origin" src="https://academy.hsoub.com/applications/core/interface/index.html" title="الاتصال بخادم Linux عبر SSH" width="930" data-embed-src="https://www.youtube.com/embed/NqKdCONHNgE"></iframe>
</p>

<h3 id="ssh-1">
	كيف تعزز مفاتيح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> أمان الاتصال مع الخادم
</h3>

<p>
	إن جميع أنواع الاستيثاق في <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> مُشفرة بالكامل حتى تلك المعتمدة على كلمات المرور فقط، لكن بالرغم من التشفير لن يُعدّ الاستيثاق باستخدام كلمة المرور آمنًا بما فيه كفاية، فالمخترق سيحاول تخمين كلمة المرور مرارًا وتكرارًا باستخدام برمجيات متخصصة وقد ينجح في كسرها والوصول إلى الخادم وخاصةً إذا كان للخادم عنوان IP عام، وعلى الرغم من إمكانية تقليل عدد المحاولات المتاحة أمام المخترق بضبط إعدادات الخادم ليمنع تلقائيًا الاتصالات الواردة من أي عنوان IP يجري عددًا معينًا من محاولات الاتصال الفاشلة، إلّا أن ذلك يبقى غير آمن تمامًا فترك الخادم عرضة <a href="https://academy.hsoub.com/programming/php/wordpress/%D9%87%D8%AC%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D9%88%D8%A9-%D8%A7%D9%84%D8%BA%D8%A7%D8%B4%D9%85%D8%A9-%D8%B9%D9%84%D9%89-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D9%88%D8%B1%D8%AF%D8%A8%D8%B1%D9%8A%D8%B3-%D9%88%D8%A7%D9%84%D8%AD%D9%85%D8%A7%D9%8A%D8%A9-%D9%85%D9%86%D9%87%D8%A7-r1440/" rel="">لهجمات القوة الغاشمة brute force attacks</a> يُعدّ خطرًا أمنيًا لا يستهان به.
</p>

<p>
	وهنا تبرز أهمية الاستيثاق باستخدام مفاتيح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> فهو يسمح بتعطيل الاستيثاق المستند إلى كلمة المرور ويلغي المخاطر المرتبطة به، وحتى إذا عينا كلمة مرور لمفتاح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> الخاص بنا فإنها تُعدّ هدفًا صعب المنال في هجوم القوة الغاشمة بسبب عدد المحارف الكبير إذ يمكننا إنشاء <a href="https://academy.hsoub.com/programming/advanced/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D9%82%D8%B7%D9%8A%D8%B9-hash-functions-%D9%81%D9%8A-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1428/" rel="">تقطيع أو تجزئة hash</a> بطول يصل إلى 128 محرف لمفتاح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> ذو كلمة مرور بطول 12 محرف فقط،
</p>

<p>
	ورغم أن التشفير يُستخدم لحماية كلمات المرور، إلا أن بعض خوارزميات التشفير ليست آمنة بالكامل. فبعضها يمكن اختراقه باستخدام تقنيات الهندسة العكسية، وذلك من خلال تحليل <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-encoding%D8%8C-%D8%A7%D9%84%D8%AA%D8%B9%D9%85%D9%8A%D8%A9-encryption%D8%8C-%D8%A7%D9%84%D8%AA%D8%AC%D8%B2%D8%A6%D8%A9-hashing-%D9%88%D8%A7%D9%84%D8%AA%D8%B4%D9%88%D9%8A%D8%B4-obfuscation%D8%9F-r308/" rel="">تجزئات كلمات المرور password hashes</a> على أجهزة قوية لفترات طويلة حتى الوصول إلى كلمة مرور مطابقة. في المقابل، توجد خوارزميات أكثر أمانًا حتى الآن، مثل خوارزمية RSA الافتراضية التي تعتمدها تطبيقات <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> الحديثة.
</p>

<h3 id="ssh-2">
	تنفيذ الاستيثاق بمفاتيح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> على الخادم
</h3>

<p>
	يوصى باستخدام مفاتيح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> للاتصال عن بعد بأي <a href="https://academy.hsoub.com/devops/linux/" rel="">خادم لينكس</a>، ويمكنك تنفيذ ذلك بخطوتين: توليد زوج من مفاتيح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> على الحاسوب المحلي باستخدام الأمر <code><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr></code> وفق الخطوات الواردة في مقال <a href="https://academy.hsoub.com/devops/security/ssh/%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%AE%D9%88%D8%A7%D8%AF%D9%8A%D9%85-ssh-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D9%85%D9%81%D8%A7%D8%AA%D9%8A%D8%AD-r55/" rel="">العمل مع خواديم <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>: العملاء والمفاتيح</a>، ثم نقل المفتاح العام public key إلى الخادم البعيد.
</p>

<p>
	أما في أجزاء النظام التي تحتاج لاستخدام كلمة مرور أو التي تكون عرضةً لهجمات القوة الغاشمة brute force attacks فيمكن تنفيذ حلول مخصصة لذلك مثل استخدام Fail2ban للحد من محاولات تخمين كلمة المرور، ولمطالعة المزيد ننصح بمطالعة مقال <a href="https://academy.hsoub.com/devops/security/firewalls/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AD%D9%85%D8%A7%D9%8A%D8%A9-ssh-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-fail2ban-%D8%B9%D9%84%D9%89-ubuntu-r113/" rel="">كيفية حماية <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> باستخدام Fail2Ban على Ubuntu</a>.
</p>

<p>
	من ناحية أخرى يُعدّ عدم السماح لمستخدم الجذر <code>root</code> بتسجيل الدخول مباشرةً عبر <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> واحدًا من أفضل الممارسات الأمنية الموصى بها، وذلك طبقًا <a href="https://en.wikipedia.org/wiki/Principle_of_least_privilege" rel="external nofollow">لمبدأ إعطاء الامتيازات الأقل</a> فبدلاً من المستخدم <code>root</code> نسجل الدخول بمستخدم عادي لا يملك امتيازات عالية، ثم نرفع امتيازاته حسب الحاجة بواسطة أداة مثل <a href="https://academy.hsoub.com/devops/linux/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-sudo-%D9%84%D8%AA%D9%81%D9%88%D9%8A%D8%B6-%D8%A7%D9%84%D8%B5%D9%84%D8%A7%D8%AD%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-linux-r379/" rel=""><code>sudo</code></a>، ويمكن تطبيق ذلك كما يلي: بعد التأكد من الاتصال بالخادم وإنشاء حساب لا يتمتع بامتيازات عالية لكنه قادر على العمل مع <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>، يمكن تعطيل عمليات تسجيل الدخول بواسطة مستخدم الجذر عبر تعديل قيمة السطر الخاص بذلك ضمن الملف <code>‎/etc/<abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr>/sshd_config</code> على الخادم ليصبح <code>PermitRootLogin no</code> ثم نعيد تشغيل <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> باستخدام أمر مثل <code>Sudo systemctl Restart sshd</code> ليأخذ التعديل مفعوله.
</p>

<h2 id="firewalls">
	الجدران النارية Firewalls
</h2>

<p>
	الجدار الناري <a href="https://academy.hsoub.com/devops/security/firewalls/%D9%85%D8%A7-%D9%87%D9%88-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D8%B1-%D8%A7%D9%84%D9%86%D8%A7%D8%B1%D9%8A-%D9%88%D9%83%D9%8A%D9%81-%D9%8A%D8%B9%D9%85%D9%84%D8%9F-r114/" rel="">Firewall</a> هو تطبيق برمجي أو عتاد يتحكم بإتاحة الخدمات على الشبكة، وبأنواع حركة مرور البيانات المسموح لها بالدخول والخروج من وإلى خادم معين أو مجموعة خوادم معينة، وعند ضبطه بشكل صحيح، يعمل الجدار الناري على حماية النظام بفعالية، حيث يمنع أي اتصال وارد من خارج الشبكة ما لم يكن مصرحًا به، ولا يسمح إلا بالوصول إلى الخدمات المخصصة لذلك فقط.
</p>

<p style="text-align: center;">
	<img alt="002 الجدار الناري" class="ipsImage ipsImage_thumbnailed" data-fileid="170421" data-ratio="38.67" data-unique="cbtvlgy2o" style="width: 600px; height: auto;" width="742" src="https://academy.hsoub.com/uploads/monthly_2025_04/img02-firewall.png.8ffcfe76112681fe896b534452f23d1c.png">
</p>

<p>
	تُصَنَّف الخدمات الموجودة على أي خادم تقليدي إلى الفئات التالية:
</p>

<ul>
	<li>
		خدمات عامة Public متاحة لأي شخص عبر الإنترنت مثل خوادم الويب
	</li>
	<li>
		خدمات خاصة Private محصورة بالاتصالات من مواقع محددة أو حسابات مصرح لها بالوصول مثل لوحات تحكم قواعد البيانات
	</li>
	<li>
		خدمات داخلية Internal غير متاحة للعامة وتقبل الاتصالات المحلية فقط، مثل قواعد البيانات المحلية
	</li>
</ul>

<p>
	تتحكم <a href="https://academy.hsoub.com/devops/security/firewalls/" rel="">الجدران النارية</a> بالوصول إلى التطبيقات تبعًا للفئات المذكورة أعلاه، فتساعد على إتاحة الخدمات العامة للجميع عبر الإنترنت، وعلى تقييد الوصول إلى الخدمات الخاصة بناءً على معايير مختلفة مثل أنواع الاتصالات أو غيرها، بالإضافة إلى عزل الخدمات الداخلية تمامًا عن الإنترنت، وإغلاق المنافذ غير المستخدمة التي عادة ما تغلق في معظم إعدادات الحماية.
</p>

<h3 id="-1">
	كيف يحمي الجدار الناري التطبيقات
</h3>

<p>
	لا غنى عن استخدام الجدار الناري لحماية الأنظمة البرمجية، حتى وإن كانت التطبيقات نفسها مزوّدة بميزات أمنية أو تقيّد الوصول إلى واجهات معينة فقط. فالجدار الناري يشكّل طبقة حماية أساسية تعمل على تصفية الاتصالات الواردة والصادرة، وتمنع حركة المرور غير المصرح بها من الوصول إلى التطبيق أساسًا، قبل معالجتها من قبل النظام أو الخوادم المعنية.
</p>

<p>
	ويتحكم الجدار الناري المُعَدّ بطريقة سليمة بإمكانية الوصول إلى مكونات النظام الخاص بنا حسب طبيعة العمل، فلا يتيح الوصول لكل الخدمات والاتصالات على الخادم، بل يسمح بالوصول إلى خدمات معينة ويمنع الوصول إلى سواها، وذلك عبر فتح المنافذ ports الخاصة بها وإغلاق بقية المنافذ، مثلًا يمكن فتح المنفذ 22 لبروتوكول الاتصال <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>، والمنفذين 80 و 443 لخدمات الويب <a href="https://academy.hsoub.com/devops/servers/web/" rel="">HTTP/HTTPS</a> التي تعمل من المتصفح وغيرها، عمومًا كلما اختصرنا عدد المنافذ أو الخدمات المفتوحة كلما قللنا من الثغرات الأمنية التي يمكن أن يستغلها المخترق ومن احتمالية تعرضنا للهجمات الإلكترونية.
</p>

<h3 id="-2">
	تشغيل الجدار الناري على الخادم
</h3>

<p>
	يوفر نظام <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r799/" rel="">لينكس</a> أنواع متعددة من جدران الحماية تختلف عن بعضها في درجة التعقيد وطبيعة الحماية التي تؤمنها، وكل ما علينا هو فهمها وضبط إعداداتها بحسب خدماتنا ثم التعديل عليها مع أي تغيير تجريه على هذه الخدمات، ومن أشهر الخيارات المتاحة الجدار الناري غير المُعقَّد Uncomplicated Firewall -أو UFW اختصارًا- حيث يتوفر هذا النوع افتراضيًا على بعض توزيعات لينكس مثل أوبنتو، ويمكن إعداده بالرجوع إلى مقال <a href="https://academy.hsoub.com/devops/security/firewalls/%D9%83%D9%8A%D9%81-%D8%AA%D8%B6%D8%A8%D8%B7-%D8%AC%D8%AF%D8%A7%D8%B1%D8%A7-%D9%86%D8%A7%D8%B1%D9%8A%D8%A7-%D9%81%D9%8A-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-ufw-r120/" rel="">كيف تضبط جدارًا ناريًا في أوبنتو باستخدام UFW</a> ومقال <a href="https://academy.hsoub.com/devops/security/firewalls/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-ufw-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D9%88%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%84%D9%84%D8%AC%D8%AF%D8%A7%D8%B1-%D8%A7%D9%84%D9%86%D8%A7%D8%B1%D9%8A-r121/" rel="">أساسيات UFW: قواعد وأوامر شائعة للجدار الناري</a>.
</p>

<p>
	تحتفظ العديد من الجدران النارية البرمجية مثل UFW و <a href="https://academy.hsoub.com/devops/linux/%D8%AE%D8%B7%D9%88%D8%A7%D8%AA-%D8%A5%D8%B6%D8%A7%D9%81%D9%8A%D8%A9-%D9%84%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%AE%D9%88%D8%A7%D8%AF%D9%8A%D9%85-centos-7-%D8%A7%D9%84%D8%AC%D8%AF%D9%8A%D8%AF%D8%A9-r346/" rel="">firewalld</a> بقواعدها rules ضمن ملف خاص يدعى <code>iptables</code>، ويمكن مطالعة المزيد عن هذا الموضوع بقراءة مقال <a href="https://academy.hsoub.com/devops/security/firewalls/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%AC%D8%AF%D8%A7%D8%B1-%D9%86%D8%A7%D8%B1%D9%8A-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-iptables-%D8%B9%D9%84%D9%89-ubuntu-1404-r38/" rel="">كيفية إعداد جدار ناري باستخدام IPTables على Ubuntu 14.04</a>، ومقال <a href="https://academy.hsoub.com/devops/security/firewalls/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-iptables-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D9%88%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%84%D9%84%D8%AC%D8%AF%D8%A7%D8%B1-%D8%A7%D9%84%D9%86%D8%A7%D8%B1%D9%8A-r119/" rel="">أساسيات IPTables - قواعد وأوامر شائعة للجدار الناري</a>، إذ يساعد فهم <code>iptables</code> على التعامل مع حالات التعارض التي قد تنشأ من تسجيل بعض البرمجيات مثل <a href="https://academy.hsoub.com/devops/cloud-computing/docker/" rel="">Docker</a> قواعدها الأمنية الخاصة بالتحكم بمنافذ الخادم ضمن الملف <code>iptables</code> إلى جوانب قواعد UFW المُسجلَّة فيه أيضًا.
</p>

<p>
	<strong>ملاحظة</strong>:توفر العديد من شركات الاستضافة خدمة جدار ناري خارجي يمكن تفعيله أمام الخادم السحابي لحمايته، بدلًا من الاعتماد فقط على الجدار الناري الموجود داخل نظام التشغيل، وهذا النوع من الجدران النارية يُدار من خلال لوحة التحكم الخاصة بمزود الخدمة، وتختلف أدوات التحكم فيه وطريقة إعداده من مزود لآخر.
</p>

<p>
	أهم ما يجب الانتباه إليه هو أن تكون الإعدادات الافتراضية للجدار الناري قائمة على مبدأ الحظر، أي أن يتم منع أي حركة مرور مجهولة أو غير مصرح بها بشكل تلقائي، فهذا الإجراء يحمي الخدمات الجديدة من الظهور على الإنترنت عن طريق الخطأ أو من أن تكون متاحة لأطراف غير مخولين. وعندما نقرر إطلاق خدمة جديدة، سيكون علينا حينها إعداد قواعد واضحة ومحددة في الجدار الناري تسمح بالوصول إليها وهذا يدفعنا بشكل طبيعي إلى التفكير المسبق في طريقة تشغيل الخدمة، وكيفية الوصول إليها، والتأكد من جميع الجوانب الأمنية قبل طرحها.
</p>

<h2 id="vpcnetworks">
	شبكات السحابة الافتراضية الخاصة VPC Networks
</h2>

<p>
	شبكات السحابة الافتراضية الخاصة Virtual Private Cloud -أو VPC اختصارًا- ههي شبكات مخصصة لربط موارد البنية التحتية في السحابة، وتوفر اتصالًا آمنًا ومعزولًا عن الإنترنت العام.
</p>

<h3 id="vpc">
	كيف تعزز شبكات VPC أمان نظامنا
</h3>

<p>
	عند حجز استضافة سحابية، يقوم بعض مزودي الخدمة بتخصيص واجهتين للشبكة تلقائيًا: واحدة عامة public network interface للتواصل مع الإنترنت، وأخرى خاصة private network interface للتواصل داخل الشبكة السحابية فقط. يمكننا تعطيل الواجهة العامة لبعض الخوادم والسماح لها بالتواصل فقط عبر الواجهة الخاصة. بذلك نحمي البيانات المتبادلة بين هذه الخوادم لأن الحركة لا تمر عبر الإنترنت العام، مما يقلل من خطر تعرضها للاختراق أو الكشف
</p>

<p>
	يساعدنا الاستخدام المدروس لعدد بوابات الإنترنت أو بوابات الدخول ingress gateways التي تعد نقاط الاتصال الوحيدة بين موارد شبكتنا السحابية الخاصة VPC والإنترنت العام على مراقبة حركة المرور العامة التي تتصل مع الموارد والتحكم بها بأفضل طريقة ممكنة، وتتوافق بعض أنظمة تنسيق الحاويات الحديثة مثل <a href="https://academy.hsoub.com/devops/cloud-computing/%D9%86%D8%B8%D8%A7%D9%85-%D9%83%D9%88%D8%A8%D9%8A%D8%B1%D9%86%D8%AA%D8%B3-kubernetes-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%B9%D9%85%D9%84%D9%87-r598/" rel="">Kubernetes</a> جيدًا مع مفهوم بوابات الدخول فلها إعداداتها الخاصة في هذا المجال، إذ تنشئ العديد من واجهات الشبكة الخاصة افتراضيًا، ويعود القرار لنا بما نريد إتاحته أو الكشف عنه منها.
</p>

<h3 id="vpc-1">
	تطبيق شبكات VPC
</h3>

<p>
	يتيح العديد من مزودي <a href="https://academy.hsoub.com/devops/cloud-computing/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%AD%D9%88%D8%B3%D8%A8%D8%A9-%D8%A7%D9%84%D8%B3%D8%AD%D8%A7%D8%A8%D9%8A%D9%91%D8%A9-%D8%A7%D9%84%D9%85%D8%AA%D8%B7%D9%84%D8%A8%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D9%91%D8%A9%D8%8C-%D9%88%D9%83%D9%8A%D9%81-%D8%AA%D8%B5%D8%A8%D8%AD-%D9%85%D9%87%D9%86%D8%AF%D8%B3-%D8%AD%D9%88%D8%B3%D8%A8%D8%A9-%D8%B3%D8%AD%D8%A7%D8%A8%D9%8A%D9%91%D8%A9-r457/" rel="">الخدمات السحابية</a> المتخصصين بخدمات البنية التحتية إمكانية إنشاء مواردنا الخاصة وإضافتها إلى شبكة VPC حسب ما نريد لكن بالطبع على أنظمتهم الأساسية وداخل مراكز بياناتهم، أما بناء شبكتك الخاصة من VPC يدويًا من دون أي مزود خدمة فهو أمرٌ صعب وبتطلب إعدادات ومزايا متقدمة على الخادم ومعرفة عميقة بالشبكات، لذا يمكن استخدام أحد بدائل VPC الموجودة في المتناول وهو إعداد اتصال <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> بين الخوادم كما سنشرح في فقرتنا التالية.
</p>

<h2 id="vpn">
	شبكات <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> والشبكات الخاصة
</h2>

<p>
	الشبكة الافتراضية الخاصة virtual private network -أو  <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> اختصارًا- هي وسيلة لإنشاء اتصالات آمنة بين الحواسيب البعيدة تكافئ الاتصالات ضمن شبكة محلية خاصة local private network، يساعدك ذلك على إعداد خدماتك كما لو أنها في شبكة خاصة وأيضًا على الاتصال مع خوادمنا البعيدة بطريقة آمنة.
</p>

<p style="text-align: center;">
	<img alt="003 " class="ipsImage ipsImage_thumbnailed" data-fileid="170429" data-ratio="38.00" data-unique="p1162i0yd" style="width: 600px; height: auto;" width="780" src="https://academy.hsoub.com/uploads/monthly_2025_04/img03-vpn.png.e02557976ce8f9886651f769beb705dd.png">
</p>

<h3 id="vpn-1">
	كيف تحسِّن شبكات <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> أمان النظام
</h3>

<p>
	يُعَدُّ استخدام <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> طريقةً فعالة لبناء شبكة خاصة لا يراها إلَّا خوادمنا، وتتمتع الاتصالات ضمنها بالأمان والخصوصية التامة، يمكن تمرير حركة مرور بعض التطبيقات عبر الواجهة الافتراضية التي توفرها برمجيات <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr>، وستكون عندها الخدمات الوحيدة المتاحة عبر الشبكة العامة هي تلك التي يُفترض بالعملاء أن يستخدموها عبر الإنترنت العام.
</p>

<h3 id="vpn-2">
	تنفيذ شبكات <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> عمليًا
</h3>

<p>
	يعتمد إعداد الشبكة الخاصة private network على التخطيط وضبط الإعدادات المناسبة لمشروعنا على التطبيقات والواجهات الشبكية والجدران النارية التي نستخدمها، وذلك في بداية العمل عند نشر الخوادم لأول مرة، بالمقابل يتطلب <a href="https://academy.hsoub.com/devops/security/vpn/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-tinc-%D9%84%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%B4%D8%A8%D9%83%D8%A9-%D8%AE%D8%A7%D8%B5%D8%A9-%D8%A7%D9%81%D8%AA%D8%B1%D8%A7%D8%B6%D9%8A%D8%A9-vpn-%D8%B9%D9%84%D9%89-ubuntu-1404-r143/" rel="">إعداد شبكات <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr></a> ونشرها تثبيت أدوات إضافية على بنيتنا الحالية وبناء مسارات شبكة network routes إضافية، كما ينبغي أن يمتلك كل خادم في شبكة <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> الخاصة بنا البيانات والإعدادات التي تمَكِّنُه من تأسيس اتصال <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> مع مكونات الشبكة، وبعد إتمام كافة الإعدادات وتشغيل <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> بنجاح يمكن ضبط إعدادات تطبيقاتنا لتمرر كافة بياناتها عبر نفق <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> tunnel. ولمزيد من المعلومات ننصح بمطالعة المقالات الموجودة في قسم <a href="https://academy.hsoub.com/devops/security/vpn/" rel=""><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr></a>.
</p>

<p>
	هناك العديد من الأدوات مفتوحة المصدر التي تساعدنا على بناء شبكات <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> مثل <a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-wireguard-on-ubuntu-20-04" rel="external nofollow">وايرجارد Wireguard</a> وغيرها، وتتبع شبكات <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> عمومًا المبدأ نفسه الذي ناقشناه في فقرة VPC والخاص بتقييد الوصول إلى <a href="https://academy.hsoub.com/devops/cloud-computing/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D8%B3%D8%AD%D8%A7%D8%A8%D9%8A%D8%A9-r807/" rel="">الخوادم السحابية</a> بعدد محدود من المداخل ingress وذلك عبر تنفيذها سلسلة من واجهات الشبكة الخاصة خلف عدد قليل من نقاط الدخول entry points، ويمكننا تنفيذ التقنيتين معًا أي <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> ضمن بنية تحتية أساسها VPC من بناء شبكات <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية"><abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr></abbr> أكثر تخصيصًا.
</p>

<h2 id="serviceauditing">
	تدقيق الخدمات ومراجعتها
</h2>

<p>
	يُعدّ تحليل الأنظمة المستخدمة وفهم ثغراتها واحتمالات الهجمات التي قد تتعرض لها من أهم <a href="https://academy.hsoub.com/devops/security/7-%D8%AA%D8%AF%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A3%D9%85%D9%86%D9%8A%D8%A9-%D9%84%D8%AD%D9%85%D8%A7%D9%8A%D8%A9-%D8%AE%D9%88%D8%A7%D8%AF%D9%8A%D9%85%D9%83-r60/" rel="">تدابير الحماية</a> التي تساعدك على تأمين الخودام وبقية مكونات النظام بأفضل طريقة ممكنة.
</p>

<p style="text-align: center;">
	<img alt="004 قائمة مراجعة الخدمات" class="ipsImage ipsImage_thumbnailed" data-fileid="170435" data-ratio="90.50" data-unique="zqjgyx5vg" style="width: 200px; height: auto;" width="299" src="https://academy.hsoub.com/uploads/monthly_2025_04/img04-service-audit.png.f21958faac2b6bbb111a4416d064431f.png">
</p>

<p>
	ويُعدّ تدقيق الخدمات Service auditing ومراجعتها من أبرز أساليب هذا التحليل، فهو يوفر معلومات عن الخدمات العاملة على نظام معين، وأرقام المنافذ التي تستخدمها، والبروتوكولات التي تتخاطب بها وما إلى ذلك، وتساعدنا هذه المعلومات في ضبط إعدادات الخدمات الموجهة للعامة، وإعدادات الجدران النارية، وأيضًا في المراقبة، والإنذار بحدوث أي طارئ.
</p>

<h3 id="-3">
	كيف يساهم تدقيق الخدمات ومراجعتها بتعزيز أمان الأنظمة؟
</h3>

<p>
	تمثل كل خدمة داخلية أو عامة تعمل على نظام حاسوبي ثغرة أمنية محتملة ونقطة ضعف يمكن أن يستغلها المهاجمون، وبالتالي كلما ازداد عدد الخدمات الموجودة على خادمنا كلما ازدادت احتمالات اختراقه وما يتبعها من تأثيراتٍ سلبية على برمجياتنا.
</p>

<p>
	عند توفر فكرة أساسية عن الخدمات الشبكية العاملة على جهازنا يمكن البدء بتحليلها، وطرح الأسئلة التالية عن كل خدمة نود تدقيقها:
</p>

<ul>
	<li>
		هل من الضروري تشغيل هذه الخدمة
	</li>
	<li>
		هل تعمل هذه الخدمة على واجهات الشبكة الصحيحة أم على واجهات لا ينبغي لها العمل عليها
	</li>
	<li>
		هل ينبغي ربط هذه الخدمة بواجهة شبكة عامة أم خاصة
	</li>
	<li>
		هل قواعد الجدار الناري مضبوطة بطريقة صحيحة فتمرر حركة البيانات المسموحة فقط إلى هذه الخدمة وتمنع غير المسموحة
	</li>
	<li>
		هل النظام مجهز بآلية تنبيه تعلمنا بالتحذيرات الأمنية المرتبطة بنقاط ضعف كل خدمة من هذه الخدمات
	</li>
</ul>

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

<h3 id="-4">
	تنفيذ تدقيق الخدمات ومراجعتها
</h3>

<p>
	نستخدم الأمر <code>ss</code> لتدقيق خدمات الشبكة العاملة على نظامنا، إذ يعرض هذا الأمر كافة منافذ <a href="https://academy.hsoub.com/devops/networking/%D8%A8%D8%B1%D8%AA%D9%88%D9%83%D9%88%D9%84%D8%A7%D8%AA-%D8%AA%D8%AF%D9%81%D9%82-%D8%A7%D9%84%D8%A8%D8%A7%D9%8A%D8%AA%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%88%D8%AB%D9%88%D9%82%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8%D9%8A%D8%A9-%D8%A8%D8%B1%D9%88%D8%AA%D9%88%D9%83%D9%88%D9%84-tcp-%D9%85%D8%AB%D8%A7%D9%84%D8%A7-r515/" rel="">TCP</a> و UDP المستخدمة على الخادم. لنلقِ نظرة على المثال التالي المتضمن استخدام هذا الأمر مع الخيارات الخاصة بإظهار اسم البرنامج، ورقم العملية <abbr title="Process IDentifier | معرّف العملية أو البرنامج"><abbr title="Process IDentifier | معرّف العملية أو البرنامج">PID</abbr></abbr>، والعناوين الشبكية المستخدمة للاستماع أو لتلقي اتصالات TCP و UDP:
</p>

<pre class="ipsCode">$ sudo ss -plunt
</pre>

<p>
	وهذه دلالة كل واحد من الخيارات <code>p</code> و <code>l</code> و <code>u</code> و <code>n</code> و <code>t</code> المستخدمة أعلاه:
</p>

<ul>
	<li>
		الخيار <code>p</code> مسؤول عن إظهار رقم العملية <abbr title="Process IDentifier | معرّف العملية أو البرنامج"><abbr title="Process IDentifier | معرّف العملية أو البرنامج">PID</abbr></abbr> المقابل لكل مِقبس socket
	</li>
	<li>
		يعرض الخيار <code>l</code> المقابس الفعالة التي تستمع listening حاليًّا للاتصالات
	</li>
	<li>
		يعمل <code>u</code> على تضمين مقابس UDP في المعلومات المعروضة عن الخدمات بالإضافة إلى مقابس TCP
	</li>
	<li>
		يعرض <code>n</code> بيانات رقمية عن حركة مرور البيانات
	</li>
	<li>
		يعمل <code>t</code> على تضمين مقابس TCP في المعلومات المعروضة عن الخدمات بالإضافة إلى مقابس UDP
	</li>
</ul>

<p>
	وستحصل بعد تنفيذ الأمر على خرج يشبه التالي:
</p>

<pre class="ipsCode">Netid       State        Recv-Q       Send-Q             Local Address:Port             Peer Address:Port      Process
tcp         LISTEN       0            128                      0.0.0.0:22                    0.0.0.0:*         users:(("sshd",pid=812,fd=3))
tcp         LISTEN       0            511                      0.0.0.0:80                    0.0.0.0:*         users:(("nginx",pid=69226,fd=6),("nginx",pid=69225,fd=6))
tcp         LISTEN       0            128                         [::]:22                       [::]:*         users:(("sshd",pid=812,fd=4))
tcp         LISTEN       0            511                         [::]:80                       [::]:*         users:(("nginx",pid=69226,fd=7),("nginx",pid=69225,fd=7))
</pre>

<p>
	الأعمدة الرئيسية في الخرج السابق هي: <code>Netid</code> و <code>Local Address:Port</code> و <code>Process</code> وهي الأجدر بالاهتمام، فإذا كانت قيمة العمود <code>Local Address:Port</code> هي <code>0.0.0.0</code> فإن الخدمة ستستقبل الاتصالات الواردة إليها على جميع واجهات الشبكة IPv4، وإذا كانت قيمته <code>[::]</code> فستتلقى الخدمة الاتصالات على جميع واجهات الشبكة من نوع IPv6، إذا طبقنا ذلك على المثال أعلاه سنجد أن كل من <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> و <a href="https://academy.hsoub.com/devops/servers/web/nginx/" rel="">Nginx</a> يستقبلان الاتصالات على جميع الواجهات العامة وبكلا النوعين IPv4 و IPv6.
</p>

<p>
	إذًا يمكن الآن أن نقرر فيما إذا كنا نرغب بإبقاء <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> و NGINX تستقبلان الاتصالات على كلا الواجهتين أم تغيير ذلك إلى واجهة واحدة، وبالمثل لبقية الخدمات. هذه هي الفائدة الرئيسية من تدقيق الخدمات بالإضافة إلى قدرتنا على كشف الخدمات التي تعمل على واجهات غير مستخدمة وضرورة تعطيلها.
</p>

<h2 id="-5">
	التحديثات التلقائية غير المراقبة
</h2>

<p>
	يُعد التحديث المنتظم للخوادم وتطبيق الإصلاحات الأمنية بشكل دوري أمرًا أساسيًا للحفاظ على مستوى جيد من الأمان، حيث إن الخوادم غير المُحدَّثة أو الإصدارات القديمة من البرامج قد تحتوي على ثغرات أمنية معروفة للمهاجمين، مما يسهل عليهم استغلالها لاختراق الأنظمة. من خلال تفعيل التحديثات التلقائية أو التحديثات غير المراقبة Unattended Updates التي لا تتطلب تدخلاً بشريًا، يمكن ضمان تحديث معظم حزم النظام بشكل تلقائي وفوري، مما يساهم في معالجة الثغرات الأمنية وحماية النظام من مخاطرها.
</p>

<h3 id="-6">
	كيف تُحسّن التحديثات التلقائية أمان النظام
</h3>

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

<h3 id="-7">
	تنفيذ التحديثات غير التلقائية على الخادم
</h3>

<p>
	ننصح بقراءة مقال <a href="https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D8%AA%D8%AD%D8%AF%D9%8A%D8%AB-%D8%A7%D9%84%D8%AA%D9%84%D9%82%D8%A7%D8%A6%D9%8A-%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-r840/" rel="">التحديث التلقائي لخادم أوبنتو</a> فهو يعرفكم بأفضل استراتيجيات التحديثات التلقائية على نظام أوبنتو، سواء تحديثات الحزم أو نواة النظام kernel.
</p>

<h2 id="ssltls">
	البنية التحتية للمفتاح العام والتشفير بواسطة <abbr title="Secure Socket Layer | طبقة المنافذ الآمنة"><abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">SSL</abbr></abbr> أو <abbr title="Transport Layer Security | بروتوكول أمن طبقة النقل"><abbr title="Transport Layer Security | بروتوكول أمن طبقة النقل">TLS</abbr></abbr>
</h2>

<p>
	إن البنية التحتية للمفتاح العام Public key infrastructure -أو PKI اختصارًا- هي نظام مخصص لإنشاء وإدارة الشهادات certificates والتحقق من صحتها بهدف تحديد هويات الأفراد وتشفير الاتصالات بينهم. تُستخدم شهادات <abbr title="Secure Socket Layer | طبقة المنافذ الآمنة">SSL</abbr> أو <abbr title="Transport Layer Security | بروتوكول أمن طبقة النقل">TLS</abbr> في البداية لإجراء الاستيثاق authentication بين مكونات النظام، ثم تُستخدم بعد ذلك لإنشاء اتصالات مشفرة بين هذه المكونات لضمان الأمان وحماية البيانات المتبادلة.
</p>

<p style="text-align: center;">
	<img alt="005 خادم HTTPS" class="ipsImage ipsImage_thumbnailed" data-fileid="170432" data-ratio="39.83" data-unique="7wep2g1ob" style="width: 600px; height: auto;" width="722" src="https://academy.hsoub.com/uploads/monthly_2025_04/img05-ssl-tls-pki.png.4aa76d8db8d895d3b2cf510be6f56949.png">
</p>

<h3 id="pki">
	دور PKI في تحسين أمان الأنظمة
</h3>

<p>
	يساعد إنشاء سُلطَة تصديق الشهادات Certificate Authority -أو AC اختصارًا- في تحسين أمان النظام عن طريق السماح لكل خادم بالتحقق من هويات الخوادم الأخرى وتشفير البيانات المتبادلة معها. هذا يمنع هجمات الرجل في المنتصف man-in-the-middle حيث يحاول المهاجم انتحال شخصية خادم آخر لاعتراض البيانات. يُضبَط كل خادم ليثق في سُلطَة التصديق المركزية، وبالتالي يثق في الشهادات التي تصدرها هذه السُلطَة، مما يزيد الأمان ويحمي البيانات.
</p>

<h3 id="pki-1">
	إعداد بنية تحتية للمفتاح العام PKI
</h3>

<p>
	في الواقع يتطلب إعداد سلطة لتصديق الشهادات وبناء البنية التحتية للمفتاح العام PKI جهدًا كبيرًا، كما أن إدارة الشهادات مثل إصدارها أو إبطالها قد تضيف عبئًا إداريًا إضافيًا، هذا الجهد لن يكون مفيدًا جدًا للمستخدم العادي، ولا يستحق كل هذه التكاليف، إلا إذا كان النظام كبيرًا ويزداد تعقيده مع الوقت، مما يجعل من الضروري وجود بنية خاصة لتأمينه. أما بالنسبة لمعظم المستخدمين، فإن استخدام الشبكات الخاصة الافتراضية <abbr title="Virtual Private Network | الشبكة الخاصة الافتراضية">VPN</abbr> لتأمين الاتصال بين أجزاء النظام يُعد خيارًا مناسبًا ويوفر حماية كافية، إلى أن يكبر النظام ويصبح بحاجة فعلية لبنية مفاتيح عامة متقدمة.
</p>

<h2 id="-8">
	الخلاصة
</h2>

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

<p>
	ترجمة -وبتصرف- لمقال <a href="https://www.digitalocean.com/community/tutorials/recommended-security-measures-to-protect-your-servers" rel="external nofollow">Recommended Security Measures to Protect Your Servers</a> لصاحبيه Justin Ellingwood و Alex Garnett.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/networking/%D8%A7%D9%84%D9%87%D8%AC%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D9%85%D9%86%D9%8A%D8%A9-security-attacks-%D9%81%D9%8A-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8%D9%8A%D8%A9-r540/" rel="">الهجمات الأمنية Security Attacks في الشبكات الحاسوبية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/security/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%A3%D9%85%D9%8A%D9%86-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D8%B3%D8%AD%D8%A7%D8%A8%D9%8A%D8%A9-%D8%B6%D8%AF-%D9%87%D8%AC%D9%85%D8%A7%D8%AA-%D8%AD%D9%82%D9%86-sql-r98/" rel="">كيفية تأمين الخوادم السحابية ضد هجمات حقن SQL</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/security/%D9%85%D8%A7-%D9%87%D9%88-%D8%A7%D9%84%D8%A3%D9%85%D9%86-%D8%A7%D9%84%D8%B3%D9%8A%D8%A8%D8%B1%D8%A7%D9%86%D9%8A-%D9%88%D9%85%D8%A7-%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%9F-r664/" rel="">ما هو الأمن السيبراني وما أهميته؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/security/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%AA%D8%A3%D9%85%D9%8A%D9%86-%D8%AE%D8%A7%D8%AF%D9%88%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r51/" rel="">مقدمة في تأمين خادم لينكس</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">841</guid><pubDate>Sun, 06 Apr 2025 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x62D;&#x62F;&#x64A;&#x62B; &#x627;&#x644;&#x62A;&#x644;&#x642;&#x627;&#x626;&#x64A; &#x644;&#x62E;&#x627;&#x62F;&#x645; &#x623;&#x648;&#x628;&#x646;&#x62A;&#x648;</title><link>https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D8%AA%D8%AD%D8%AF%D9%8A%D8%AB-%D8%A7%D9%84%D8%AA%D9%84%D9%82%D8%A7%D8%A6%D9%8A-%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-r840/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_03/---.png.59c09d9da58b420f83ccf9c0bed806b8.png" /></p>
<p>
	نناقش اليوم عدة طرق لتحديث خوادم أوبنتو تلقائيًا والاستفادة من ذلك في حماية الخوادم من الأخطاء والثغرات الأمنية وضمان عملها بأمان وموثوقية، وسنوضح استخدام بعض الأدوات والإعدادات التي تسهّل علينا إدارة تحديثات الخوادم وتصحيحها وإعادة تشغيلها، فإجراء تلك الأمور يدويًا أمر مرهق لمدير النظام وقد يتسبب بوقوع أخطاء.
</p>

<p>
	يشمل مقالنا ثلاثة أقسام:
</p>

<ul>
	<li>
		<a href="#1" rel="">أفضل ممارسات إدارة التطبيقات لضمان إعادة تشغيلها بشكل صحيح بعد التحديث</a>
	</li>
	<li>
		<a href="#2" rel="">إعدادات الترقية التلقائية للمكتبات والحزم العاملة على الخادم</a>
	</li>
	<li>
		<a href="#3" rel="">التحديث والتصحيح المباشر لنواة النظام</a>
	</li>
</ul>

<h2 id="-1">
	متطلبات العمل
</h2>

<p>
	نحتاج خادم يعمل بنظام التشغيل <a href="https://academy.hsoub.com/apps/operating-systems/linux/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%B3%D8%B7%D8%AD-%D9%85%D9%83%D8%AA%D8%A8-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-2004-r779/" rel="">أوبونتو</a>، مع مستخدم عادي غير المستخدم الجذر root لكنه يتمتع بصلاحيات عالية <code>sudo</code> لتشغيل الأوامر الإدارية عند الحاجة، ويمكن تجهيز المطلوب باتباع خطوات مقال <a href="https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D8%AA%D9%87%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%A3%D9%88%D9%84%D9%8A%D8%A9-%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-1804-r431/" rel="">التهيئة الأولية لخادم أوبونتو</a>.
</p>

<h2 id="1">
	أفضل ممارسات إدارة التطبيقات
</h2>

<p>
	عند ترقية الخادم، ستراودنا مخاوف أساسية تتعلق باستمرارية عمل التطبيقات بعد التحديث وإعادة التشغيل مثل كيف ستعمل تطبيقاتنا بعد ترقية الخادم؟ وهل سيعود الخادم لسلوكه الطبيعي بعد إعادة التشغيل اللاحقة للترقية؟ تشكل هذه الأسئلة هاجسًا لأي مدير النظام يسعى لتحديث خادمه وقد تجعله يتخلى عن قرار الترقية.
</p>

<p>
	كما سنحتاج للإجابة على هذه التساؤلات عند ضبط إعدادات الترقية التلقائية automatic upgrades على الخوادم ونتأكد من إعادة تشغيل جميع التطبيقات العاملة على الخادم بصورة سليمة بعد أي إعادة تشغيل أو توقف مفاجئ، علمًا أن أدوات <a href="https://academy.hsoub.com/devops/linux/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-apt-%D8%8Cyum-%D8%8Cdnf-%D8%8Cpkg-r231/" rel="">إدارة حزم لينكس</a> مصممة في الأساس لتعمل في الخلفية دون عرقلة أو أعباء صيانة إضافية على الخادم.
</p>

<p>
	نظريًّا ينبغي أن تُدار جميع التطبيقات العاملة على الخادم باستخدام نظام التمهيد init system ما أمكن ذلك، و <a href="https://academy.hsoub.com/devops/linux/%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A7%D8%AA-%D8%B9%D9%84%D9%89-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A8%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D9%85%D9%87%D9%8A%D8%AF-systemd-r284/" rel="">systemd</a> هي أداة التمهيد الرئيسية في أوبنتو وفي معظم <a href="https://academy.hsoub.com/devops/linux/%D8%B9%D8%B1%D8%B6-%D9%85%D9%88%D8%AC%D8%B2-%D9%84%D8%A3%D8%B4%D9%87%D8%B1-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A7%D8%AA-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r631/" rel="">توزيعات لينكس</a> الأخرى حيث تتيح التفاعل مع الخدمات العاملة على الخادم وإعادة تشغيلها تلقائيًا حسب الحاجة باستخدام الأمر <code>systemctl</code>، بناءً على ذلك فإن أي برنامج مثبت على الخادم ويعمل في الخلفية سيأتي تلقائيًا مع خدمة systemd لإدارته. بالإضافة إلى ذلك، سينشأ ملف إعدادات خاص يسمى <a href="https://academy.hsoub.com/devops/linux/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-systemd-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A7%D8%AA%D8%8C-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-units%D8%8C-%D9%88%D8%A7%D9%84%D9%8A%D9%88%D9%85%D9%8A%D8%A7%D8%AA-journal-r130/" rel="">ملف إعدادت الوحدة unit file</a> يحتوي على تعليمات تحدد كيفية تشغيل الخدمة وإيقافها وإدارتها بواسطة systemd.
</p>

<p>
	أما إذا كنا نشغل تطبيقًا خاصًا طورناه بنفسنا أو تطبيقًا ثبَّناه من <a href="https://academy.hsoub.com/programming/workflow/git/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9%D9%8A-%D9%84%D9%84%D8%B9%D9%85%D9%84-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D8%BA%D9%8A%D8%AA-git-r1587/" rel="">مستودعات Git</a> لا يتضمن ملف تمهيد للتكامل مع systemd، وسيصعب كتابة الملف يدويًّا ولن تكون نتائجه مضمونة، لذا يلجأ مدراء الأنظمة لاستخدام أدوات خاصة بإدارة التطبيقات مثل مدير العمليات <a href="https://academy.hsoub.com/devops/servers/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D9%88%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-supervisor-%D8%B9%D9%84%D9%89-%D8%AE%D9%88%D8%A7%D8%AF%D9%8A%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-%D9%88%D8%AF%D8%A8%D9%8A%D8%A7%D9%86-r279/" rel="">supervisor</a> لتوفير الوقت والجهد والاستغناء عن كتابة ملفات التمهيد يدويًا، كما يمكننا أيضًا استخدام جدولة المهام بواسطة cron مع الصيغة <code>reboot@</code> لأتمتة العملية.
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="400" id="ips_uid_135_6" referrerpolicy="strict-origin-when-cross-origin" src="https://academy.hsoub.com/applications/core/interface/index.html" title="جدولة المهام عبر cron" width="930" data-embed-src="https://www.youtube.com/embed/zcsGNNYkDgU"></iframe>
</p>

<p>
	ويمكن إعادة التشغيل باستخدام الأمر <code>Sudo Shutdown now -r</code> الذي يوقف جميع العمليات العاملة ويعيد تشغيل النظام مباشرةً، كما يمكننا جدولة إعادة التشغيل لتجري في وقت لاحق في ساعة ودقيقة معينة <code>hh:mm</code> بدل إعادة التشغيل الفوري، علمًا أن جميع الخدمات ونقاط الوصول endpoints الضرورية في بيئة الإنتاج ينبغي أن تعود للعمل بعد أي انقطاع طارئ.
</p>

<p>
	بعد أن عرضنا الطرق التي يمكن استخدامها لضمان إعادة تشغيل سليمة لجميع الخدمات أثناء عمليات الصيانة، لننتقل لشرح طريقة جدولة التحديثات التلقائية.
</p>

<h2 id="2">
	إعدادات الترقية التلقائية
</h2>

<p>
	يبدأ مدير حزم أوبنتو <code>apt</code> عمله بتحديث حزم النظام باستخدام <code>apt update</code> ثم ترقيتها بواسطة <code>apt Upgrade</code> التي ستعمل على ترقية جميع الحزم دون استثناء لأننا لم نحدد حزمة بعينها، تستخدم صيغة الأوامر هذه في الحالة المثالية لكنها ستختلف قليلًا وتتطلب إعدادات أكثر إذا كنا نستخدم حزمًا من مستودعات طرف ثالث غير مستودعات أوبنتو ونخشى من تعارضها مع حزم <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%B3%D8%AA%D9%88%D8%AF%D8%B9%D8%A7%D8%AA-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-r629/" rel="">مستودعات أوبنتو</a>، أو إذا كنا نحتفظ عمدًا بإصدارات محددة من بعض الحزم ولا نود ترقيتها لأنها تتوافق مع تطبيقاتنا مثلًا.
</p>

<p>
	يوفر أوبنتو أداة فريدة لتثبيت التصحيحات الأمنية patches والترقيات الأخرى غير المُراقبة التي لا تحتاج إلى تدخل بشري لإنجازها، تسمى<code> unattended-upgrades</code> وهي مثبتة تلقائيًا في معظم خوادم أوبنتو، ويمكن تثبيتها يدويًا باستخدام أوامر <a href="https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%83%D8%A7%D9%85%D9%84-%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-apt-%D9%81%D9%8A-%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r785/" rel="">apt</a> التالية:
</p>

<pre class="ipsCode">$ sudo apt update
$ sudo apt install unattended-upgrades
</pre>

<p>
	يمكن التأكد من عمل الخدمة <code>unattended-upgrades</code> باستخدام <code>systemctl</code> كما يلي:
</p>

<pre class="ipsCode">$ sudo systemctl status unattended-upgrades.service
</pre>

<p>
	إذا كانت الخدمة تعمل، سنحصل على خرج يشبه التالي:
</p>

<pre class="ipsCode">unattended-upgrades.service - Unattended Upgrades Shutdown
     Loaded: loaded (/lib/systemd/system/unattended-upgrades.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2022-02-14 17:51:49 UTC; 3h 4min ago
       Docs: man:unattended-upgrade(8)
   Main PID: 829 (unattended-upgr)
      Tasks: 2 (limit: 1137)
     Memory: 10.6M
     CGroup: /system.slice/unattended-upgrades.service
</pre>

<p>
	ستعمل الأداة <code>unattended-upgrades</code> بإعداداتها الافتراضية على تثبيت كافة الإصلاحات والتحديثات المتوفرة للحزم التابعة لمستودعات أوبنتو، أما إذا كنا نستخدم إصدارات أقدم من بعض الحزم وأردنا تجنب التعديلات على المستودع الأولي upstream changes أو كان خادمنا يستخدم مستودعات طرف ثالث تتبع لجهات خارجية إلى جانب مستودعات أوبنتو فسنحتاج عندها لتعديل إعدادات <code>unattended-upgrades</code> الافتراضية وضبطها بطريقة مختلفة.
</p>

<p>
	تُخَزَّن إعدادات هذه الأداة في الملف <code>‎/etc/apt/apt.conf.d/50unattended-upgrades</code> ويمكننا فتحه وتحريره بواسطة أي محرر نصوص مثل <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A%D9%85-vim-%D9%88%D9%86%D8%A7%D9%86%D9%88-nano-r1590/" rel="">نانو <code>nano</code></a> أو غيره كما يلي:
</p>

<pre class="ipsCode">$ sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
</pre>

<p>
	بمجرد فتح الملف، سنلاحظ أنه سهل القراءة ومشروح جيدًا، ويتضمن مجموعة من التعليقات التي تشرح التعليمات البرمجية ووظائفها، تتضمن كتلة التعليمات الأولى في الملف أسماء الحزم التي ستُحَدِّثها الأداة تلقائيًا ولها القالب العام لأسماء مستودعات حزم أوبنتو، وهي افتراضيًا ملفات المستودع المركزي core repository وملفات مستودع الحماية <code>security-</code>، أما مستودعات <code>updates-</code> و <code>proposition-</code> و <code>backports-</code> فتحديثها مُعطّل افتراضيًا ويُترك للمستخدم حرية تفعيلها من عدمه، لأنها قد تتضمن تعديلات جذرية على حزم النظام لذا نجد أن أسطرها تبدأ برمز التعليق <code>//</code>، ويمكن إزالته إن رغبتنا بتفعيل التحديثات التلقائية لها.
</p>

<p>
	إذا تابعنا تصفح الملف، سنجد عددًا من الأسطر التي يمكن التحكم بخياراتها باستخدام <code>true</code> أو <code>false</code>، على سبيل المثال يوجد خيار خاص بتفعيل إعادة التشغيل التلقائية بعد تثبيت الحزم التي تتطلب إعادة تشغيل كي تتفعل وتدخل حيز التنفيذ، يمكن تفعيل هذا الخيار بإزالة رمز التعليق <code>//</code> وتبديل القيمة <code>false</code> إلى <code>true</code>، لكن اتخاذ قرار كهذا سيعرضنا لانقطاعات غير متوقعة في الخدمة نتيجة إعادة التشغيل وينبغي أن تكون طبيعة تطبيقاتنا قادرة على تحمل التبعات الناتجة عن ذلك.
</p>

<pre class="ipsCode">// Automatically reboot *WITHOUT CONFIRMATION* if
//  the file /var/run/reboot-required is found after the upgrade
//Unattended-Upgrade::Automatic-Reboot "false";
</pre>

<p>
	بعد الانتهاء من إجراء التعديلات، نحفظ التغييرات على الملف ونغلقه بالخطوات التي تناسب محرر النصوص المستخدم ففي <code>nano</code> مثلًا نضغط على <code>Ctrl+X</code> ثم <code>Y</code> وبعدها <code>Enter</code>.
</p>

<p>
	والآن نعيد تشغيل الخدمة <code>unattended-upgrades</code> وفق الأمر التالي لتصبح التعديلات سارية المفعول:
</p>

<pre class="ipsCode">$ sudo systemctl restart unattended-upgrades.service
</pre>

<p>
	بهذا نكون قد انتهينا من شرح طريقة ضبط التحديثات الأمنية التلقائية لحزم النظام لتعمل تلقائيًا دون تدخل منا، سنناقش في الفقرة التالية تحديث نواة النظام والتعامل مع الحالات التي تقتضي إعادة التشغيل.
</p>

<h2 id="3">
	التحديث والتصحيح المباشر لنواة النظام
</h2>

<p>
	نواة <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-linux-%D9%88%D8%A3%D8%A8%D8%B1%D8%B2-%D9%85%D9%85%D9%8A%D8%B2%D8%A7%D8%AA%D9%87-%D9%88%D8%B9%D9%8A%D9%88%D8%A8%D9%87-r2252/" rel="">نظام لينكس</a> هي الجزء الأساسي من المظام حيث تحتوي على تعريفات معظم الأجهزة كالمعالج والذاكرة والطابعة وغيرها، وهي المسؤولة عن التفاعل منخفض المستوى بين العتاد الصلب والبرامج، وتصدر تحديثاتها بتواتر أقل من تحديثات الحزم الأخرى، وعادةً ما ترتبط بظهور ثغرة أمنية خطيرة ينبغي معالجتها، أو ظهور ميزة جديدة، وقد تكون ضرورية أيضًا في الحالات التي تصبح فيها النواة قديمة جدًا لدرجة نخشى معها وجود ثغرات أمنية وأخطاء متراكمة لا علم لنا بها.
</p>

<p>
	لا توجد منهجية جاهزة لجدولة تحديثات نواة نظام لينكس بصورة تلقائية فهذا الأمر يختلف من حالة لأخرى، فتحديثات النواة تتطلب إعادة تشغيل كاملة للنظام، ومنطقيًا لا يمكن جدولة أمر كهذا دون أخذ بيئة التشغيل الخاصة بكل خادم والخدمات التي يقدمها بالحسبان، فبعض <a href="https://academy.hsoub.com/devops/servers/" rel="">الخوادم</a> ينبغي أن تعمل على مدار اليوم والساعة دون انقطاع، وبعضها قد يأخذ وقتًا طويلًا لا يمكن التكهن به في إعادة التشغيل، أو قد يتطلب تدخلًا يدويًا لإتمام العملية أو غير ذلك، لذا لا يمكن جدولة التحديث ليجري تلقائيًا دون علم المستخدم وموافقته.
</p>

<p>
	أما إذا كنا لا نمانع بإجرائها تلقائيًا مع ما يترتب عليها من فترات انقطاع في الخدمة، فيمكننا ضبط ذلك في إعدادات التحديثات غير المُراقبة unattended updates بواسطة <code>apt</code> ليجري تحديث نواة النظام وتثبيت إصدارتها الجديدة مع بقية الحزم، وسيستخدم خادمنا النواة الجديدة المُحَدَّثة تلقائيًا بعد إعادة التشغيل، لكن عادةً ما يلجأ مديرو النظام في بيئات التشغيل الحقيقية لأساليب تضمن استمرارية تقديم الخدمة للعملاء مثل استخدام <a href="https://academy.hsoub.com/devops/servers/%D9%85%D9%82%D8%AF%D9%91%D9%85%D8%A9-%D8%A5%D9%84%D9%89-haproxy-%D9%88%D9%85%D8%A8%D8%A7%D8%AF%D8%A6-%D9%85%D9%88%D8%A7%D8%B2%D9%86%D8%A9-%D8%A7%D9%84%D8%AD%D9%85%D9%84-load-balancing-r41/" rel="">موازن حمل</a> يوجه حركة مرور البيانات إلى الخوادم البديلة أثناء إعادة تشغيل الخادم الذي يُحَدِّث نواته، وتكرار العملية مع بقية الخوادم بالترتيب ليكتمل تحديث المجموعة كلها دون أن يشعر المستخدم بالانقطاع.
</p>

<h3 id="livepatch">
	تفعيل التصحيح المباشر Livepatch لإبقاء الخادم يعمل عند تحديث النواة
</h3>

<p>
	يوفر نظام <a href="https://academy.hsoub.com/devops/linux/" rel="">لينكس</a> ميزة مفيدة تدعى التصحيح المباشر live patching تسمح لنا بتحديث النواة kernel دون إعادة التشغيل، ويُشرف على التصحيحات المباشرة -وفق هذه الميزة- أداتان أساسيتان هما:
</p>

<ul>
	<li>
		Canonical الخاصة بأنظمة أوبنتو حصرًا
	</li>
	<li>
		KernelCare التي تدعم معظم توزيعات لينكس الشهيرة إلى جانب أوبنتو
	</li>
</ul>

<p>
	علمًا أننا نحتاج لتسجيل حساب على موقع كلتا الأداتين لاستخدامهما، وتوفر Canonical إمكانية التسجيل والاستخدام المجاني للأفراد.
</p>

<p>
	يمكن تسجيل الدخول إلى Canonical والحصول على مفتاح خاص بالتصحيح المباشرة من <a href="https://auth.livepatch.canonical.com/" rel="external nofollow">هذا الرابط</a>، ويمكننا بعدها تثبيت الحزمة <code>canonical-livepatch</code> بواسطة <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%B3%D8%AD-%D8%AD%D8%B2%D9%85-snap-%D8%A7%D9%84%D9%85%D8%B9%D8%B7%D9%84%D8%A9-%D9%84%D8%AA%D8%AD%D8%B1%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%B3%D8%A7%D8%AD%D8%A9-%D9%81%D9%8A-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r623/" rel=""><code>snap</code></a> -وهو مدير حزم آخر يعمل إلى جانب <code>apt</code> في أنظمة أوبنتو- كما يلي:
</p>

<pre class="ipsCode">$ sudo snap install canonical-livepatch
</pre>

<p>
	ثم نكتب الأمر التالي لتفعيل الحزمة <code>canonical-livepatch</code>، ولا ننسى استبدال <code>your-key</code> بمفتاحنا الخاص الذي حصلنا عليه من موقع Canonical:
</p>

<pre class="ipsCode">$ sudo canonical-livepatch enable your-key
</pre>

<p>
	بعد التنفيذ ستظهر رسالة واضحة تعلمنا بنجاح تثبيت الخدمة <code>Successfully enabled device</code> وبذلك ستصبح <code>canonical-livepatch</code> خدمة عاملة على نظامنا، وستعمل في الخلفية ويمكن التأكد من ذلك بالاستعلام عن حالتها بواسطة <code>canonical-livepatch status</code> كما يلي:
</p>

<pre class="ipsCode">$ sudo canonical-livepatch status
</pre>

<p>
	سنحصل على خرج يشبه التالي:
</p>

<pre class="ipsCode">last check: 50 seconds ago
kernel: 5.15.0-25-generic
server check-in: succeeded
patch state: ✓ all applicable livepatch modules inserted
patch version: 84.1
tier: updates (Free usage; This machine beta tests new patches.)
machine id: 2565a9e7fc9f4405a167e4caf9b9dcf3
</pre>

<p>
	بمساعدة هذه الأداة نكون قد ضبطنا التحديثات التلقائية لنواة نظامنا كي تحدث من دون تدخل يدوي، ودون الحاجة لإعادة تشغيل النظام لنحافظ بذلك على بيئة تشغيل آمنة ومُحَدَّثة لتطبيقاتنا.
</p>

<h2 id="-2">
	الخلاصة
</h2>

<p>
	تعرفنا في هذا المقال على جانب أساسي من مهام <a href="https://academy.hsoub.com/devops/general/%D8%AA%D8%B9%D9%84%D9%85-devops/" rel="">مهندسي DevOps</a> وهو تحديث الخوادم والتعامل مع التحديثات التي تتطلب إعادة تشغيل، وتعلمنا الفرق بين تحديث حزم أوبنتو وتحديث نواة النظام، ويمكن التعمق أكثر في إدارة أوبنتو ومهام DevOps  الأخرى بتحميل كتاب <a href="https://academy.hsoub.com/files/10-%D8%AF%D9%84%D9%8A%D9%84-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%AE%D9%88%D8%A7%D8%AF%D9%8A%D9%85-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88/" rel="">دليل إدارة خوادم أوبنتو</a> مجانًا من أكاديمية حسوب، ومطالعة مقالات <a href="https://academy.hsoub.com/devops/" rel="">قسم DevOps</a> أيضًا على الأكاديمية.
</p>

<p>
	ترجمة -وبتصرف- لمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-keep-ubuntu-22-04-servers-updated" rel="external nofollow">How to Keep Ubuntu 22.04 Servers Updated</a> لصاحبه Alex Garnett.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D9%91%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%91%D9%84-%D9%81%D9%8A-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r24/" rel="">أساسيات إدارة الملفّات والتنقّل في لينكس</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%83%D8%A7%D9%85%D9%84-%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D9%88%D8%A7%D9%85%D8%B1-apt-%D9%81%D9%8A-%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r785/" rel="">الدليل الكامل لاستخدام أوامر apt في نظام لينكس</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D9%85%D8%B9-%D9%86%D8%B8%D8%A7%D9%85-%D9%88%D9%8A%D9%86%D8%AF%D9%88%D8%B2-r576/" rel="">تثبيت لينكس مع نظام ويندوز</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r634/" rel="">كيفية تثبيت التطبيقات في لينكس</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">840</guid><pubDate>Sat, 22 Mar 2025 12:04:01 +0000</pubDate></item><item><title>&#x625;&#x639;&#x627;&#x62F;&#x629; &#x627;&#x644;&#x62A;&#x634;&#x63A;&#x64A;&#x644; &#x627;&#x644;&#x62A;&#x644;&#x642;&#x627;&#x626;&#x64A; &#x644;&#x62D;&#x627;&#x648;&#x64A;&#x627;&#x62A; &#x62F;&#x648;&#x643;&#x631;</title><link>https://academy.hsoub.com/devops/cloud-computing/docker/%D8%A5%D8%B9%D8%A7%D8%AF%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D8%A7%D9%84%D8%AA%D9%84%D9%82%D8%A7%D8%A6%D9%8A-%D9%84%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-%D8%AF%D9%88%D9%83%D8%B1-r839/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_03/1170758675_.png.e431e4926be02b4bedadd36d94de31f3.png" /></p>
<p>
	عند إعادة تشغيل الخادم سنحتاج لإعادة تشغيل حاويات دوكر Docker من أجل ضمان استمرار تشغيل الخدمات داخل هذه الحاويات وتجنب توقفها. في هذا المقال سنوضح ثلاثة طرق مختلفة لبدء تشغيل حاويات دوكر تلقائيًا بعد إعادة تشغيل النظام، حيث ستعتمد الطريقة الأولى والثانية على وجود خدمة دوكر على مستوى الجهاز أي أنها تتطلب وجود عملية dockerd تعمل في الخلفية وتدير الحاويات وإلا سنواجه أخطاء في تطبيقها.
</p>

<p>
	لنشرح الطرق الثلاثة بالتفصيل في الفقرات التالية:
</p>

<h2 id="restartalways">
	الطريقة الأولى: استخدام خيار <code>--restart always</code>
</h2>

<p>
	هذه الطريقة هي الإعداد الافتراضي لتشغيل <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-docker-r310/" rel="">الحاوية</a>، فعندما نشغل حاوية دوكر باستخدام الأمر <code>docker run</code> يمكننا تحديد الخيار <code>restart always--</code> لنقرر تشغيل الحاوية تلقائيًا عند إعادة تشغيل الخادم، أو عند توقف الحاوية لأي سبب كان.
</p>

<p>
	يوضح المثال التالي تشغيل حاوية أباتشي مع تفعيل إعادة التشغيل التلقائي:
</p>

<pre class="ipsCode">docker run -p 8080:80 --name apache -v "${PWD}":/usr/local/apache2/htdocs -d --restart always httpd
</pre>

<p>
	هنالك عدة احتمالات ممكنة للخيار <code>restart--</code> نتحكم من خلالها في كيفية إعادة تشغيل الحاوية تلقائيًا تشمل التالي:
</p>

<ul>
	<li>
		<strong><code>restart no--</code></strong> وهو الإعداد الافتراضي، ويعني عدم تشغيل الحاوية تلقائيًا عند توقف الحاوية لأي سبب كان
	</li>
	<li>
		<strong><code>restart always--</code></strong> إعادة تشغيل الحاوية تلقائيًا بمجرد توقفها، وسيعاد تشغيل جميع الحاويات التي بدأ تشغيلها سابقًا باستخدام هذا الخيار<br>
		<strong>تحذير</strong>: إذا كانت الحاوية تحتوي على خطأ برمجي تسبب في توقفها، فيتسبب هذا الخيار في محاولة إعادة تشغيلها بصورة مستمرة ومتكررة، إلا لو أوقفناها يدويًا باستخدام الأمر <code>docker stop</code> فعندها سيتوقف تشغيلها في نفس الجلسة، لكن ستعاد محاولة تشغيلها من جديد عند إعادة تشغيل الخادم
	</li>
	<li>
		<strong><code>restart unless-stopped--</code></strong> يعمل هذا الخيار بشكل مشابه لخيار <code>restart always--</code> الفرق هنا هو عدم إعادة تشغيل الحاوية التي أوقفناها يدويًا باستخدام <code>docker stop</code>
	</li>
	<li>
		<strong><code>restart on-failure--</code></strong> إعادة تشغيل الحاوية تلقائيًا فقط في حالة توقفها بسبب خطأ ما
	</li>
</ul>

<p>
	يمكننا معرفة سلوك إعادة التشغيل حاوية باستخدام الأمر <code>docker inspect</code>:
</p>

<pre class="ipsCode">docker inspect &lt;containername&gt;
  ...
  "RestartPolicy": {
      "Name": "always",
      "MaximumRetryCount": 0
  },
  ...
</pre>

<p>
	لتغيير سلوك إعادة تشغيل الحاوية أثناء تشغيلها بالفعل، يمكننا استخدام الأمر <code>docker update</code>، على سبيل المثال، إذا كنا نحتاج لتغيير أسلوب إعادة تشغيل حاوية معينة ونريد إعادة تشغيلها فقط في حالة حدوث خطأ في الحاوية سنكتب الأمر التالي:
</p>

<pre class="ipsCode">docker update --restart on-failure &lt;containername&gt;
</pre>

<h2 id="dockercompose">
	الطريقة الثانية: استخدام Docker Compose
</h2>

<p>
	يمكننا أيضًا تحديد طريقة إعادة التشغيل التلقائي للحاوية من خلال <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%A7%D9%84%D8%A3%D8%AF%D8%A7%D8%A9-%D8%AF%D9%88%D9%83%D8%B1-%D9%83%D9%88%D9%85%D8%A8%D9%88%D8%B2-docker-compose-%D9%88%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D9%87%D8%A7-%D8%B6%D9%85%D9%86-%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A3%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-r654/" rel="">ملف docker-compose.yml</a> باستخدام الكلمة المفتاحية restart كما يلي:
</p>

<pre class="ipsCode">services:
   db:
     image: mariadb:latest
     restart: always
</pre>

<p>
	هناك أربعة إعدادات مسموح بها هنا وهي:
</p>

<ul>
	<li>
		no وهو الخيار الافتراضي ويعني إعادة تشغيل الحاوية يدويًا فقط
	</li>
	<li>
		always لإعادة تشغيل الحاوية دائمًا
	</li>
	<li>
		unless-stopped لإعادة تشغيل الحاوية في كافة الأحوال، إلا في حال أوقفناها يدويًا
	</li>
	<li>
		on-failure لإعادة تشغيل الحاوية فقط في حال توقفت نتيجة لحدوث خطأ
	</li>
</ul>

<p>
	تتشابه دلالة الخيارات السابقة مع الخيارات المشروحة في الطريقة الأولى التي تستخدم الخيار <code>restart--</code> مع الأمر <code>docker run</code>، لكن هنا سيكون المعامل <code>restart: always</code> ساري المفعول حتى نوقف الحاويات الموجودة في الملف <code>docker-compose.yml</code> ونحذفها يدويًا باستخدام الأمر <code>docker-compose down</code>.
</p>

<p>
	<strong>ملاحظة</strong>: ينبغي التأكد من تحديد الكلمة المفتاحية <code>restart</code> في المستوى الصحيح داخل إعدادات الحاوية المعنية وهي <code>db</code> في المثال.
</p>

<pre class="ipsCode">services:
  db:
    image: mariadb:latest
    volumes:
      - vol-db:/var/lib/mysql
    environment:
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: password
    restart: always
</pre>

<h2 id="systemd">
	الطريقة الثالثة: إعادة تشغيل الحاوية باستخدام systemd
</h2>

<p>
	الطريقة الأساسية لبدء تشغيل حاويات Docker هي عن باستخدام الأمر <code>docker run</code> والذي يسمح لنا بإنشاء حاوية من صورة دوكر Docker image وتشغيلها، لكن هناك مشكلة في هذه الطريقة ففي حال حدوث أي مشكلة تتسبب في توقف الخادم أو إعادة تشغيله فستتوقف الحاوية التي شغلناها عبر هذا الأمر ولن يعاد تشغيلها تلقائيًا، مما يؤدي إلى توقف الخدمة و التطبيق الذي توفره.
</p>

<p>
	يمكننا حل هذه المشكلة وضمان استمرار عمل الحاوية بتحويل الأمر <code>docker run</code> إلى خدمة نظام لضمان مواصلة عملها بعد إعادة تشغيل الخادم، فالحاوية في هذه الحالة ستكون جزءًا من خدمة وبالتالي يمكن تشغيلها تلقائيًا عند إقلاع الخادم، وإعادة تشغيلها تلقائيًا إذا توقفت لأي سبب كان.
</p>

<p>
	يمكن تحقيق ذلك باستخدام مدير الخدمات systemd، كما يمكننا استخدام supervisord أيضًا أو أي نظام مشابه آخر لإدارة الخدمات، ولكننا اخترنا systemd في مقالنا الحالي لكونه الأكثر استخدمًا وهو معتمد في معظم توزيعات لينكس.
</p>

<p>
	سنغلف حاوية دوكر في خدمة systemd، كي نتمكن من إدارة هذه الحاوية بنفس الطريقة التي ندير بها التطبيقات والخدمات الأخرى على جهازنا، وضمان الأمور التالية:
</p>

<ul>
	<li>
		إمكانية بدء الخدمة بشكل صحيح عند تشغيل الجهاز
	</li>
	<li>
		إمكانية إعادة تشغيل الحاوية تلقائيًا في حال توقفت لأي سبب كان باستخدام إعداد <code>Restart=always</code> في ملف خدمة systemd
	</li>
	<li>
		سهولة مراجعة وإدارة سجلات الحاوية stdout بواسطة نظام تسجيل السجلات Journald
	</li>
	<li>
		استخدام كامل نظام systemd لإدارة المتطلبات كالتبعيات والأوامر التي يجب أن تنفذ قبل وبعد تشغيل الحاوية وتعريف متغيرات البيئة التي تحتاجها الحاوية بكفاءة
	</li>
</ul>

<p>
	يعرض الكود التالي الحد الأدنى لمحتوى ملف خدمة systemd لتشغيل حاوية دوكر تحتوي على خادم nginx والموجود في المسار <code>/etc/systemd/system/nginx.service</code>:
</p>

<pre class="ipsCode">[Unit]
Description=Docker container

[Service]
ExecStart=/usr/bin/docker run --name nginx \
    --net host \
    -v /srv/nginx/conf.d:/etc/nginx/conf.d \
    -v /srv/nginx/index.html:/usr/share/nginx/html/index.html \
    nginx

ExecStop=/usr/bin/docker stop nginx
ExecStopPost=/usr/bin/docker rm -f nginx

[Install]
WantedBy=multi-user.target
</pre>

<p>
	يحدد هذا الملف الأمور التالية:
</p>

<ul>
	<li>
		تشغيل الحاوية التي تحتوي على خادم nginx تلقائيًا عند تشغيل النظام
	</li>
	<li>
		إيقاف الحاوية بشكل صحيح عند إيقاف الخدمة
	</li>
	<li>
		حذف الحاوية بعد إيقافها
	</li>
	<li>
		تشغيل الخدمة تلقائيًا عند تشغيل النظام ووصوله لمرحلة multi-user target
	</li>
</ul>

<p>
	لكن هذه الطريقة لها بعض السلبيات، ونحتاج لإجراء بعض التعديلات على ملف الخدمة لضمان عمله بشكل صحيح، فهنا بدأنا تشغيل حاوية Docker باسم nginx، وسحبنا أحدث صورة <a href="https://hub.docker.com/_/nginx/" rel="external nofollow"><code>nginx</code></a> من <a href="https://hub.docker.com/_/nginx" rel="external nofollow">المستودع الرسمي</a> مع تمرير مجلد الإعدادات على شكل volume داخل الحاوية،وفي حال لم يكن موجودًا ستنشئه حاوية دوكر على الخادم.
</p>

<p>
	سيعمل جزء <code>WantedBy</code> فقط عند تنفيذ الأمر <code>systemctl enable nginx</code>، وفي هذه الحالة ستفعَّل الخدمة عند بدء التشغيل لأنها ستكون مرتبطة بالوصول لمرحلة multi-user target في systemd، هذا يعني أنه عند تشغيل النظام، ستفعّل الخدمة تلقائيًا.
</p>

<p>
	الحل هو بإضافة الأسطر <code>BindsTo</code> و <code>After</code> في ملف الخدمة لضمان أن الخدمة تعتمد على دوكر. بمعنى آخر، إذا توقفت خدمة دوكر أو لم تكن تعمل بعد، فإن هذه الخدمة لن تبدأ بالعمل إلا بعد أن نضمن تشغيل دوكر بنجاح، وسيجري إيقافها أولًا إذا توقفت حاوية دوكر في أي وقت.
</p>

<p>
	أيضًا، في حالتنا nginx التي هي خدمة عديمة الحالة stateless أي أنها لا تحفظ حالتها بين عمليات التشغيل المختلفة، لذا سيكون من الأفضل تحديث صورة دوكر الخاصة بالخدمة لأحدث إصدار متاح كلما شغلنا هذه الخدمة لنضمن بأنها تعمل بأحدث نسخة من الحاوية، ولتحقيق سنضيف أمر <code>ExecStartPre</code> المسؤول عن تنفيذ <code>docker pull</code> كي يعمل قبل الأمر <code>ExecStart</code>.
</p>

<p>
	كما يمكننا فرض إعادة تشغيل الخدمة دائمًا في حال تعرضت لخطأ، وذلك كل 10 ثوانٍ مثلًا، من خلال الأسطر <code>Restart</code> و <code>RestartSec</code> والتي تشابه في تأثيرها الخيار <code>restart=on-failure</code> في أمر <code>docker run</code> الذي شرحناه في الطريقة الأولى.
</p>

<p>
	أخيرًا هناك مشكلة أخرى سنواجهها في حال استخدمنا systemd لإدارة الحاويات. فعند تشغيل خدمة حاوية دوكر عبر systemd، سنحتاج لتحديد سلوك إعادة التشغيل يدويًا عند إعادة تشغيل الخدمة أو توقفها لأن systemd يفترض بأننا نرغب في الاحتفاظ بالأسلوب المستخدم لإعادة التشغيل، حيث سيوقف الخدمة أولاً ثم يعيد تشغيلها مرة أخرى.
</p>

<p>
	لحل هذه المشكلة يمكن استخدام حل بسيط وهو استخدام الأمر <code>ExecReload</code> لإعادة تحميل عملية nginx داخل الحاوية فهذا يساعدنا عند الحاجة لتحديث إعدادات nginx أو إعادة تحميلها دون توقف الخدمة بالكامل، كما يمكننا استخدام حل بديل وهو إرسال إشارة باستخدام الأمر <code>kill -s HUP</code>، لنخبر nginx بأن يعيد تحميل إعداداته دون إيقافه بالكامل.
</p>

<p>
	أخيرًا، يمكننا تغيير الأسماء المستخدمة في ملف الخدمة باستخدام متغيرات البيئة في السطر <code>Environment</code>. وفي حال وجدنا أن المتغيرات أصبحت كثيرة أو كانت مشتركة بين عدة ملفات خدمة على نفس الخادم، فيمكن استخدام ملف منفصل للمتغيرات باستخدام الخيار <code>EnvironmentFile</code>.
</p>

<p>
	فيما يلي ملف الخدمة بعد إجراء التحسينات السابقة:
</p>

<pre class="ipsCode">[Unit]
Description=Docker container
BindsTo=docker.service
After=docker.service

[Service]
Environment=NAME=%N
Environment=IMG=nginx
Restart=on-failure
RestartSec=10
ExecStartPre=-/usr/bin/docker kill ${NAME}
ExecStartPre=-/usr/bin/docker rm ${NAME}
ExecStart=/usr/bin/docker run --name ${NAME} \
    -p 80:80 \
    -p 443:443 \
    -v /srv/nginx/conf.d:/etc/nginx/conf.d \
    -v /srv/nginx/html:/usr/share/nginx/html/ \
    ${IMG}
ExecStop=/usr/bin/docker stop ${NAME}
ExecReload=/usr/bin/docker exec ${NAME} nginx -s reload

[Install]
WantedBy=multi-user.target
</pre>

<p>
	<strong>ملاحظة</strong>: يجري استبدال <code>N%</code> تلقائيًا باسم ملف الخدمة، وعلينا أن لا ننسى تشغيل الأمر <code>systemctl daemon-reload</code> في كل مرة نغير فيها إعدادات ملف الخدمة.
</p>

<h3 id="-1">
	مراقبة سجلات الخدمة
</h3>

<p>
	عند تشغيل الخدمات باستخدام systemd مثل خدمة nginx في المثال السابق، ستوجه السجلات التي تنتج عن الخدمة إلى سجلات النظام <span ipsnoautolink="true">journald</span>، وهذا يسهل علينا الوصول لها وتحليلها.
</p>

<p>
	على سبيل المثال، يمكننا الوصول إلى سجلات nginx ببساطة لمراقبتها وعرضها في الزمن الحقيقي من خلال الأمر التالي:
</p>

<pre class="ipsCode">journalctl -fu nginx
</pre>

<p>
	لا يمنعنا هذا الأسلوب من الحصول على ملفات سجلات منفصلة، فقد نحتاج لذلك كما في حالة استخدام virtualhosts لإدارة عدة مواقع ويب على نفس الخادم باستخدام nginx، في هذه الحالة سيكون من الأنسب لنا مشاركة مجلد السجلات الخاص بالحاوية وهو بشكل افتراضي <code>/var/log/nginx</code> مع جهاز المضيف حتى لا نفقدها عند إعادة التشغيل.
</p>

<h2 id="-2">
	الخاتمة
</h2>

<p>
	تعرفنا في هذا المقال على ثلاث طرق مختلفة لإعادة تشغيل حاويات دوكر تلقائيًا بعد إعادة تشغيل الخادم، يعتمد اختيار الطريقة المناسبة على البئية ومتطلبات العمل، ففي الحالات البسيطة وإعدادات الحاوية الواحدة قد تكون طريقة <code>restart--</code> في أمر <code>docker run</code> أو طريقة Docker Compose كافية. لكن في البيئات المعقدة التي تتضمن عدة خدمات ستوفر لنا طريقة systemd تحكمًا أكبر بالحاوية.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://linuxhandbook.com/restart-docker-container-automatically/" rel="external nofollow">Restart Docker Container Automatically After Reboot</a> لكاتبه <a href="https://linuxhandbook.com/author/umair/" rel="external nofollow">Umair Khurshid</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D8%AB%D9%84%D8%A7%D8%AB-%D9%86%D8%B5%D8%A7%D8%A6%D8%AD-%D9%84%D8%AA%D8%B3%D9%85%D9%8A%D8%A9-%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-docker-r311/" rel="">ثلاث نصائح لتسمية حاويات Docker</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%A3%D9%85%D8%B1-docker-exec-%D9%81%D9%8A-%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-docker-r813/" rel="">استخدام الأمر docker exec في حاويات Docker</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%AA%D9%86%D8%B3%D9%8A%D9%82-%D8%A7%D9%84%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-r643/" rel="">أساسيات تنسيق الحاويات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-r641/" rel="">مدخل إلى الحاويات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">839</guid><pubDate>Sat, 15 Mar 2025 12:04:01 +0000</pubDate></item><item><title>&#x625;&#x646;&#x634;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646; &#x648;&#x646;&#x634;&#x631;&#x647; &#x639;&#x644;&#x649; &#x643;&#x648;&#x628;&#x631;&#x646;&#x62A;&#x633; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x645;&#x646;&#x635;&#x629; Okteto</title><link>https://academy.hsoub.com/devops/deployment/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88%D9%86%D8%B4%D8%B1%D9%87-%D8%B9%D9%84%D9%89-%D9%83%D9%88%D8%A8%D8%B1%D9%86%D8%AA%D8%B3-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D9%86%D8%B5%D8%A9-okteto-r838/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_03/Okteto_.png.11d9c7336953f797d2fc08c15a232db0.png" /></p>
<p>
	نشرح في هذا المقال إنشاء تطبيق مكتوب بلغة بايثون Python على كوبرنتس Kubernetes باستخدام منصة أوكتيتو Okteto التي تسهل عملية تطوير تطبيقات كوبرنتس Kubernetes، فهي تسمح للمطورين ببناء التطبيقات واختبارها مباشرة في <a href="https://academy.hsoub.com/devops/cloud-computing/%D8%AA%D8%B9%D9%84%D9%85-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-kubernetes-r468/" rel="">عناقيد كوبرنتس Kubernetes clusters</a> دون الحاجة لإعداد بيئات تطوير معقدة، كما توّفر <a href="https://www.okteto.com/" rel="external nofollow">منصة Okteto</a> ميزة التحديثات المباشرة live updates للتطبيقات التي تعمل في عناقيد كوبرنتس مما يسمح للمطورين رؤية التعديلات على الشيفرة في الوقت الفعلي دون الحاجة إلى إعادة بناء التطبيقات أو إعادة نشرها.
</p>

<h2 id="">
	المتطلبات الأساسية
</h2>

<p>
	لمتابعة خطوات هذا المقال، سنحتاج إلى ما يلي:
</p>

<ul>
	<li>
		عنقود كوبرنتس النسخة 1.28، وسنستخدم في مقالنا عنقود من <a href="https://docs.digitalocean.com/products/kubernetes/how-to/create-clusters/" rel="external nofollow">منصة DigitalOcean</a>، يحتوي على 3 عقد على الأقل
	</li>
	<li>
		تثبيت واجهة سطر الأوامر <code>kubectl</code> وضبطها للتواصل مع عنقود كوبرنتس
	</li>
	<li>
		حساب على <a href="https://hub.docker.com/" rel="external nofollow"> مستودع دوكر هب</a>
	</li>
	<li>
		تثبيت <a href="https://academy.hsoub.com/devops/cloud-computing/docker/" rel="">دوكر Docker</a> على جهازنا المحلي
	</li>
	<li>
		مفتاح ترخيص لاستخدام منصة Okteto، ويمكن <a href="https://www.okteto.com/free-trial/" rel="external nofollow">التسجيل في الإصدار التجريبي المجاني</a>.
	</li>
	<li>
		تثبيت مدير الحزم Helm لتطوير التطبيقات ضمن عناقيد Kubernetes، باتباع الخطوة الأولى من <a href="https://academy.hsoub.com/devops/cloud-computing/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D8%B6%D9%85%D9%86-%D8%B9%D9%86%D8%A7%D9%82%D9%8A%D8%AF-kubernetes-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-helm-r653/" rel="">المقال التالي</a>.
	</li>
	<li>
		نطاق Domain مسجَّل ومربوط مع Load Balancer لإدارة حركة مرور التطبيقات، وإنشاء سجل A record باسم <code>*</code> وعنوان IP موازن الحِمل.
	</li>
</ul>

<h2 id="-1">
	إنشاء تطبيق بايثون
</h2>

<p>
	علينا التأكد أولًا بأن بايثون مثبّت على جهاز يعمل بنظام التشغيل أوبنتو بكتابة الأمر التالي في الطرفية:
</p>

<pre class="ipsCode">python3 --version
</pre>

<p>
	إذا كانت حزم بايثون مثبّتة على الجهاز فسيعرضالأمر السابق نسختها، وإلّا نستخدم الأمر التالي لتثبيتها:
</p>

<pre class="ipsCode">sudo apt install python3 python3-venv python3-pip -y
</pre>

<p>
	بعدها، ننشئ مجلد لتخزين شيفرة البرنامج وإعداداته:
</p>

<pre class="ipsCode">mkdir my-app
</pre>

<p>
	ثم ننشئ بيئة افتراضية داخل مجلد التطبيق لوضع اعتماديات المشروع فيها:
</p>

<pre class="ipsCode">cd my-app
python3 -m venv python-env
</pre>

<p>
	والآن نفعّل البيئة الافتراضية باستخدام الأمر التالي:
</p>

<pre class="ipsCode">source python-env/bin/activate
</pre>

<p>
	ثم ننشئ ملف بايثون لكتابة شيفرة التطبيق فيه:
</p>

<pre class="ipsCode">nano app.py
</pre>

<p>
	ونضيف له الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3095_15" style=""><span class="kwd">from</span><span class="pln"> flask </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Flask</span><span class="pln">

app </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Flask</span><span class="pun">(</span><span class="pln">__name__</span><span class="pun">)</span><span class="pln">

</span><span class="lit">@app</span><span class="pun">.</span><span class="pln">route</span><span class="pun">(</span><span class="str">'/'</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">def</span><span class="pln"> hello</span><span class="pun">():</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"Hello, This is a simple Python App!"</span><span class="pln">

</span><span class="kwd">if</span><span class="pln"> __name__ </span><span class="pun">==</span><span class="pln"> </span><span class="str">'__main__'</span><span class="pun">:</span><span class="pln">
    app</span><span class="pun">.</span><span class="pln">run</span><span class="pun">(</span><span class="pln">debug</span><span class="pun">=</span><span class="kwd">True</span><span class="pun">,</span><span class="pln"> host</span><span class="pun">=</span><span class="str">'0.0.0.0'</span><span class="pun">)</span></pre>

<p>
	ثم نثبّت إطار عمل فلاسك Flask:
</p>

<pre class="ipsCode">pip install flask
</pre>

<p>
	نشغّل التطبيق الآن باستخدام الأمر التالي:
</p>

<pre class="ipsCode">python3 app.py 
</pre>

<p>
	سنحصل على هذا الخرج:
</p>

<pre class="ipsCode">Output

* Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://172.20.10.2:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 311-959-468
</pre>

<p>
	أصبح إطار فلاسك Flask يعمل على جهازنا المحلي، نتأكّد من ذلك باستخدام الأمر <code>curl</code>:
</p>

<pre class="ipsCode">curl -X GET -H "Content-Type: application/json" http://localhost:5000
</pre>

<p>
	سنحصل على الرد التالي من تطبيق فلاسك:
</p>

<pre class="ipsCode">Output
Hello, This is a simple Python App!
</pre>

<p>
	يدل هذا الخرج على أن تطبيق فلاسك قد نُفّذ بنجاح دون أخطاء.
</p>

<p>
	يمكن إيقاف تفعيل البيئة الافتراضية عند الانتهاء من العمل على مشروعنا، باستخدام الأمر التالي:
</p>

<pre class="ipsCode">deactivate
</pre>

<p>
	وبهذا تكون أنشأنا تطبيقنا واختبرنا عمله محليًا، وحان وقت الخطوة الثانية لتشغيل هذا التطبيق داخل دوكر Docker.
</p>

<h2 id="docker">
	تشغيل تطبيق بايثون داخل دوكر Docker
</h2>

<p>
	يتطلب تشغيل تطبيق بايثون داخل <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AD%D8%A7%D9%88%D9%8A%D8%A7%D8%AA-r641/" rel="">حاوية دوكر</a> إنشاء صورة دوكر Docker image تحتوي على بيئة بايثون والاعتماديات اللازمة لتشغيل التطبيق. ننشئ أولًا ملفًا باسم <code>Dockerfile</code> في المجلد الأساسي للتطبيق:
</p>

<pre class="ipsCode">nano Dockerfile
</pre>

<p>
	ثم نضيف عليه التعليمات التالية:
</p>

<pre class="ipsCode"># Use an official Python runtime as a parent image
FROM python:3.8-slim

# Set the working directory in the container
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

# Install any needed dependencies specified in requirements.txt
RUN pip install flask

# Make port 5000 available to the world outside this container
EXPOSE 5000

# Define environment variable
ENV NAME DockerizedPythonApp

# Run app.py when the container launches
CMD ["python3", "./app.py"]
</pre>

<p>
	فيما يلي شرح تعليمات ملف دوكر السابقة:
</p>

<ul>
	<li>
		تحدد التعليمة <code>FROM python:3.8-slim </code>الصورة الأساسية التي نحتاجها
	</li>
	<li>
		تضبط <code>WORKDIR /app</code> المجلد <code>‎/app</code> مجلدًا للعمل داخل الحاوية container
	</li>
	<li>
		تنسخ التعليمة <code>ADD . /app</code> محتوى المجلد الحالي لمجلد حاوية التطبيق <code>‎/app</code>
	</li>
	<li>
		تثبّت <code>RUN pip install flask </code>إطار العمل فلاسك Flask
	</li>
	<li>
		تستمع التعليمة <code>EXPOSE 5000 </code> على المنفذ 5000 للسماح بالاتصال
	</li>
	<li>
		يحدد <code>CMD ["python3", "app.py"] </code>الأمر الذي سينفذ عند تشغيل الحاوية وهو هنا الأمر <code>app.py</code>
	</li>
</ul>

<p>
	بعدها سنشغّل الأمر التالي لبناء صورة دوكر وفقًا للتعليمات السابقة:
</p>

<pre class="ipsCode">docker build -t my-app .
</pre>

<p>
	نستطيع تشغيل التطبيق الآن في الحاوية باستخدام الصورة التي أنشأناها:
</p>

<pre class="ipsCode">docker run -dit -p 5000:5000 my-app:latest
</pre>

<p>
	يُشَغّل الأمر السابق حاوية من الصورة <code>my-app</code> ويضبط منفذ الحاوية على المنفذ المضيف <code>5000</code>. نكتب الأمر التالي للتأكد من عمل الحاوية:
</p>

<pre class="ipsCode">docker ps
</pre>

<p>
	وسنحصل على الخرج التالي:
</p>

<pre class="ipsCode">Output
CONTAINER ID   IMAGE           COMMAND              CREATED         STATUS         PORTS                                       NAMES
761c54c77411   my-app:latest   "python3 ./app.py"   3 seconds ago   Up 3 seconds   0.0.0.0:5000-&gt;5000/tcp, :::5000-&gt;5000/tcp   pedantic_wescoff
</pre>

<p>
	نفتح متصفح الويب أو نستعمل موجّه الأوامر <code>curl</code> للوصول إلى التطبيق باستخدام العنوان <code>‎http://your-server-ip:5000/</code>، وستظهر رسالة Hello, This is a simple Python App!‎ التي تشير لأن التطبيق يعمل داخل حاوية دوكر. وبهذا نكون شغّلنا تطبيق بايثون في حاوية دوكر بنجاح.
</p>

<h2 id="dockerhub">
	رفع صورة تطبيق بايثون إلى سجل DockerHub
</h2>

<p>
	لتنفيذ هذه الخطوة يجب أن يكون لدينا حساب على موقع <a href="https://hub.docker.com/" rel="external nofollow">دوكر هب DockerHub</a>، بعدها نستخدم الأمر <code>docker login</code> لتسجيل الدخول، وسيُطلب منا إدخال اسم المستخدم وكلمة المرور.
</p>

<pre class="ipsCode">docker login 
</pre>

<p>
	سنحصل على الخرج التالي:
</p>

<pre class="ipsCode">Output
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: username@gmail.com
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
</pre>

<p>
	يجب وسم الصورة باسم حسابنا ومجلد الاعتمادية المطلوب قبل رفعها push لمستودع دوكر هب:
</p>

<pre class="ipsCode">docker tag my-app yourusername/my-app:latest
</pre>

<p>
	يمكن الآن رفع الصورة إلى دوكر هب باستخدام الأمر <code>docker push</code>:
</p>

<pre class="ipsCode">docker push yourusername/my-app:latest
</pre>

<p>
	نتأكّد بعدها أن الصورة أصبحت موجودة بالبحث عنها باستخدام طرفية دوكر هب Docker Hub CLI:
</p>

<pre class="ipsCode">docker search yourusername/my-app
</pre>

<p>
	وهكذا أصبحت صورة تطبيقنا مرفوعةً على منصة دوكر هب ويمكن للآخرين سحبها أو نشرها في عدّة بيئات.
</p>

<h2 id="kubernetesmanifests">
	إنشاء ملف Kubernetes Manifests لنشر التطبيق
</h2>

<p>
	علينا الآن إنشاء ملف Kubernetes Manifests باستخدام منصة Okteto لضبط عملية التطوير والخدمات وموارد Ingressللتطبيق. نكتب الأمر التالي لإنشاء الملف:
</p>

<pre class="ipsCode">nano k8s.yaml
</pre>

<p>
	ثم نضيف الإعدادات التالية إليه:
</p>

<pre class="ipsCode">apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - image: yourusername/my-app
        name: my-app

---

apiVersion: v1
kind: Service
metadata:
  name: my-app
spec:
  type: ClusterIP
  ports:
  - name: "my-app"
    port: 5000
  selector:
    app: my-app

---

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    dev.okteto.com/generate-host: my-app
spec:
  rules:
    - http:
        paths:
          - backend:
              service:
                name: my-app
                port:
                  number: 5000
            path: /
            pathType: ImplementationSpecific
</pre>

<p>
	سينشر الملف السابق تطبيقنا بالاسم <code>my-app</code> باستخدام منصة Okteto، وسينشره داخليًا عبر خدمة ClusterIP على المنفذ 5000، ثم سيُعدّ مورد Ingress لتوجيه حركة مرور HTTP إلى التطبيق. وهنا استخدمنا التعليقات التوضيحية الخاصة بـمنصة Okteto لتفعيل بعض الميزات التي تقدمها مثل إنشاء اسم المضيف hostname تلقائيًا.
</p>

<h2 id="oktetohelm">
	تثبيت Okteto باستخدام مدير الحزم Helm
</h2>

<p>
	لتثبيت Okteto باستخدام مدير الحزم Helm علينا أولًا إضافة مستودع Okteto Helm إلى عميل Helm:
</p>

<pre class="ipsCode">helm repo add okteto https://charts.okteto.com
</pre>

<p>
	وبعد ذلك علينا تحديث مستودعات مدير الحزم Helm لنضمن أن لدينا معلومات حديثة عن المخططات أو الحزم التي تحتوي على تعريفات موارد Kubernetes اللازمة لتشغيل التطبيق:
</p>

<pre class="ipsCode">helm repo update
</pre>

<p>
	ثم ننشئ ملف <code>config.yaml</code>:
</p>

<pre class="ipsCode">nano config.yaml
</pre>

<p>
	ونضيف بعدها مفتاح رخصة استخدام Okteto الخاص بنا، والنطاق الفرعي والإعدادات، كما هو موضح أدناه:
</p>

<pre class="ipsCode">license: INSERTYOURKEYHERE
subdomain: okteto.example.com

buildkit:
  persistence:
    enabled: true

registry:
  storage:
    filesystem:
      persistence:
        enabled: true
</pre>

<p>
	ثم نثبّت أحدث نسخة من Okteto باستخدام ملف الإعدادات <code>config.yaml</code>:
</p>

<pre class="ipsCode">helm install okteto okteto/okteto -f config.yaml --namespace okteto --create-namespace
</pre>

<p>
	سنحصل على الخرج التالي عند انتهاء التثبيت:
</p>

<pre class="ipsCode">Output
NAME: okteto
LAST DEPLOYED: Tue Mar 12 20:27:21 2024
NAMESPACE: okteto
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Congratulations! Okteto is successfully installed!

Follow these steps to complete your domain configuration:

1. Create a wildcard A record "*.okteto.example.com", pointing it to the Okteto NGINX ingress controller External-IP:

   $ kubectl get service -l=app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/component=controller --namespace=okteto

2. Access your Okteto instance at `https://okteto.okteto.example.com/login#token=88f8cc11`
</pre>

<p>
	<strong>ملاحظة:</strong> علينا الاحتفاظ برمز الوصول الشخصي Personal Access Tokens ذو القيمة <code>88f8cc11</code> من الشيفرة أعلاه لأننا سنحتاجه لتوثيق حسابنا في Okteto. ننتظر قليلًا ثم نجلب عنوان IP لخادم NGINX Ingress:
</p>

<pre class="ipsCode">kubectl get service -l=app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/component=controller --namespace=okteto
</pre>

<p>
	سنحصل على الخرج التالي:
</p>

<pre class="ipsCode">Output
NAME                              TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                      AGE
okteto-ingress-nginx-controller   LoadBalancer   10.101.31.239   45.76.14.191   80:31150/TCP,443:31793/TCP   3m21s
</pre>

<p>
	علينا إضافة العنوان الخارجي <code>EXTERNAL-IP</code> إلى سجل <code>A</code> تحت الاسم <code>*</code> في إعدادات بروتوكول DNS. والآن، نفتح المتصفح للوصول إلى Okteto باستخدام الرابط التالي: <a href="https://okteto.okteto.example.com/login#token=88f8cc11." ipsnoembed="true" rel="external nofollow">https://okteto.okteto.example.com/login#token=88f8cc11.</a>
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="168974" href="https://academy.hsoub.com/uploads/monthly_2025_03/okteto-dashboard.png.16ba6123f2a05284679de0e357b722a1.png" rel=""><img alt="okteto dashboard" class="ipsImage ipsImage_thumbnailed" data-fileid="168974" data-unique="csw3hqokr" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/okteto-dashboard.thumb.png.98da596f57f007a5d0ed3fe2e8f9183f.png"> </a>
</p>

<h2 id="okteto-1">
	تثبيت وضبط إعدادات واجهة أوامر Okteto
</h2>

<p>
	تُتيح لنا واجهة أوامر Okteto مفتوحة المصدر تطوير التطبيقات مباشرة على Kubernetes. بإمكاننا تثبيت واجهة الأوامر على نظامي لينوكس Linux وماك MacOS باستخدام الأمر <code>curl</code> التالي:
</p>

<pre class="ipsCode">sudo curl https://get.okteto.com -sSfL | sh
</pre>

<p>
	نتأكد من تثبيت واجهة أوامر Okteto باستخدام الأمر التالي الذي سيعرض تانسخة المثبّتة على جهازنا
</p>

<pre class="ipsCode">Output
okteto version 2.25.2 
</pre>

<p>
	والآن علينا استخدام رمز الوصول الشخصي Personal Access Tokens للمصادقة:
</p>

<pre class="ipsCode">okteto context use https://okteto.okteto.example.com --token 88f8cc11 --insecure-skip-tls-verify
</pre>

<p>
	وسنحصل على الخرج التالي:
</p>

<pre class="ipsCode">Output
✓  Using okteto-admin @ okteto.okteto.example.com
</pre>

<p>
	نشغّل الأمر التالي للتأكد من إعدادات واجهة الأوامر:
</p>

<pre class="ipsCode">okteto context list
</pre>

<p>
	وسنحصل على الخرج التالي:
</p>

<pre class="ipsCode">Output
Name                                      Namespace     Builder                                 Registry
https://okteto.okteto.example.com *      okteto-admin  tcp://buildkit.okteto.example.com:443  registry.okteto.example.com
vke-4b7aaaa6-78fa-4a19-9fb3-cf7b8c1ec678  default       docker                                  -
</pre>

<h2 id="oktetomanifest">
	إنشاء Okteto Manifest
</h2>

<p>
	علينا إنشاء ملف Okteto Manifest وضبط إعدادات بيئة التطوير لنتمكن من إنشاء تطبيقات بلغة بايثون. أولًا، ننشئ ملف <code>okteto.yaml</code> مخصص لتطبيق بايثون بسيط.
</p>

<pre class="ipsCode">nano okteto.yaml
</pre>

<p>
	ثم نضيف الإعدادات التالية:
</p>

<pre class="ipsCode">deploy:
  - kubectl apply -f k8s.yaml

dev:
  my-app:
    command: bash
    environment:
      - FLASK_ENV=development
    sync:
      - .:/app
    reverse:
      - 9000:9000
    volumes:
      - /root/.cache/pip
</pre>

<p>
	فيما يلي شرح الأوامر السابقة:
</p>

<p>
	يتضمن القسم <code>deploy </code>إعدادات النشر. وعند تشغيل الأمر <code>okteto up</code> أو <code>okteto deploy</code> يُنَفَّذ الأمر <code>kubectl apply -f k8s.yaml</code> الذي ينشر موارد Kubernetes الموجودة في الملف <code>k8s.yaml</code>، ويُتيح ذلك فصل إعدادات النشر عن إعدادات التطوير.
</p>

<p>
	يُحدد <code>command: bash </code>الأمر الذي يجب تشغيله عند بدء تشغيل بيئة التطوير ويحدد <code>environment </code>متغيرات البيئة التي يجب أن تكون في بيئة التطوير. وهنا صرّحنا أن متغيرات فلاسك <code>FLASK_ENV</code> يجب أن تكون في بيئة التطوير <code>FLASK_ENV=development</code>.
</p>

<p>
	يحدد القسم <code>sync </code>القسم الملفات التي يجب مزامنتها من جهازنا المحلي مع ملفات بيئة التطوير، إذ يجب مزامنة المجلد <code>(.)</code> مع المجلد <code>/app‎</code> من بيئة التطوير. ويوّضح  <code>reverse</code> قواعد توجيه المنافذ بين بيئة التطوير وجهازك المحلي. وفي مثالنا، يوجّه المنفذ 9000 من بيئة التطوير إلى المنفذ 9000 على جهازنا المحلي.
</p>

<p>
	أخيرًا يوّضح <code>v</code> وحدات التخزين الإضافية التي ستُضاف إلى بيئة التطوير. وهنا سنضيف المجلد <code>/root/.cache/pip</code>، لنستخدمه لتخزين حزم <code>pip</code> في بيئة التطوير مؤقتًا.
</p>

<p>
	الآن علينا نشر تطبيق بايثون إلى عنقود كوبرنيتس باستخدام الأمر التالي:
</p>

<pre class="ipsCode">okteto deploy
</pre>

<p>
	وسنحصل على الخرج التالي عند نجاح عملية النشر:
</p>

<pre class="ipsCode">Output
deployment.apps/my-app created
service/my-app created
ingress.networking.k8s.io/my-app created
 i  There are no available endpoints for 'Okteto'.
    Follow this link to know more about how to create public endpoints for your application:
    https://www.okteto.com/docs/cloud/ssl/
 ✓  Development environment 'Okteto' successfully deployed
 i  Run 'okteto up' to activate your development container
</pre>

<p>
	ثم نحدّث لوحة تحكم Okteto من المتصفح لتعاين التطبيق بعد نشره:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="168976" href="https://academy.hsoub.com/uploads/monthly_2025_03/python-app-on-okteto-dashboard.png.0c3fc917bb6f8f14a3983c4bf69c0307.png" rel=""><img alt="python app on okteto dashboard" class="ipsImage ipsImage_thumbnailed" data-fileid="168976" data-unique="v6y5n6k85" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/python-app-on-okteto-dashboard.thumb.png.55a8244801db73159f440f8327a6a0c2.png"> </a>
</p>

<p>
	كما يمكننا الوصول إلى التطبيق من الرابط التالي: <a href="https://my-app-okteto-admin.okteto.example.com." ipsnoembed="true" rel="external nofollow">https://my-app-okteto-admin.okteto.example.com.</a>
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="168975" href="https://academy.hsoub.com/uploads/monthly_2025_03/python-application.png.a85a7e4a9a3a04f4947a003c3672e050.png" rel=""><img alt="python application" class="ipsImage ipsImage_thumbnailed" data-fileid="168975" data-unique="9wnj953cd" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/python-application.thumb.png.8cc834b4197455df0fd918b654186eda.png"> </a>
</p>

<h2 id="-2">
	تطوير تطبيق بايثون في منصة كوبرنتس مباشرة
</h2>

<p>
	سنستخدم الأمر <code>okteto up</code> لنشر التطبيق مباشرةً على كوبرنتس، إذ يزامن هذا الأمر الشيفرة المحلية مع بيئة التطوير. وبإمكاننا تعديل الشيفرة باستخدام أي بيئة تطوير أو <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AD%D8%B1%D8%B1-%D8%A3%D9%83%D9%88%D8%A7%D8%AF-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">محرر أكواد</a> على جهازنا، وستجري مزامنة التغييرات تلقائيًا مع بيئة التطوير في <a href="https://academy.hsoub.com/devops/cloud-computing/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D9%83%D9%88%D8%A8%D8%B1%D9%86%D9%8A%D8%AA%D8%B3-kubernetes-r815/" rel="">كوبرنيتس</a>.
</p>

<p>
	علينا أولًا تشغيل بيئة التطوير باستخدام Okteto:
</p>

<pre class="ipsCode">okteto up
</pre>

<p>
	يُنشئ هذا الأمر بيئة تطوير وفقًا للإعدادات الموجودة في ملف <code>okteto.yaml</code>:
</p>

<pre class="ipsCode">Output
i  Using okteto-admin @ okteto.okteto.example.com as context
 i  'Okteto' was already deployed. To redeploy run 'okteto deploy' or 'okteto up --deploy'
 i  Build section is not defined in your okteto manifest
 ✓  Persistent volume successfully attached
 ✓  Images successfully pulled
 ✓  Files synchronized
    Context:   okteto.okteto.example.com
    Namespace: okteto-admin
    Name:      my-app
    Reverse:   9000 &lt;- 9000
root@my-app-okteto-7767588c8d-ndzj7:/app# 
</pre>

<p>
	نشغّل بعدها تطبيق بايثون:
</p>

<pre class="ipsCode">python3 app.py 
</pre>

<p>
	وسنحصل على الخرج التالي:
</p>

<pre class="ipsCode">* Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://10.244.97.92:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 126-620-675
</pre>

<p>
	ثم نعدّل ملف التطبيق <code>app.py</code>:
</p>

<pre class="ipsCode">nano app.py
</pre>

<p>
	وعدّل السطر التالي:
</p>

<pre class="ipsCode">   return "Hello, This is a simple Python App Deployed on Kubernetes"
</pre>

<p>
	نحفظ الملف ثم نغلقه، وستٌطبَق التعديلات على الشيفرة البرمجية تلقائيًا في كوبرنتس. ثم نعيد تحميل صفحة التطبيق من متصفح الويب لتعاين التطبيق بعد التعديل.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="168973" href="https://academy.hsoub.com/uploads/monthly_2025_03/modified-python-application.png.70f7cf5d0ef8b4e969cb495f602b9173.png" rel=""><img alt="modified python application" class="ipsImage ipsImage_thumbnailed" data-fileid="168973" data-unique="7mlfepqvc" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_03/modified-python-application.thumb.png.c48fa82613a4fc4f9ce0b9abfaa0b1f2.png"> </a>
</p>

<h2 id="-3">
	الخاتمة
</h2>

<p>
	تعلمنا في هذا المقال كيفية إنشاء تطبيق بايثون بسيط وكيفية نشرته ضمن عناقيد كوبرنتس باستخدام منصة Okteto التي تتيح خاصية مزامنة التعديلات على الشيفرة المحلية مع بيئة التطوير، كما تسمح لنا بالتركيز على بناء تطبيقات بايثون عالية الجودة دون القلق حيال بنية كوبرنتس المعقّدة.
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-deploy-python-application-on-kubernetes-with-okteto" rel="external nofollow">How to Deploy Python Application on Kubernetes with Okteto</a> لكاتبيه hitjethva وEasha Abid.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/python/flask/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-%D9%81%D9%84%D8%A7%D8%B3%D9%83-flask-%D9%85%D9%86-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1622/" rel="">إنشاء تطبيق ويب باستخدام إطار عمل فلاسك Flask من لغة بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/deployment/%D9%86%D8%B4%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%AC%D8%A7%D9%86%D8%BA%D9%88-%D8%A2%D9%85%D9%86-%D9%88%D9%82%D8%A7%D8%A8%D9%84-%D9%84%D9%84%D8%AA%D9%88%D8%B3%D9%8A%D8%B9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%83%D9%88%D8%A8%D9%8A%D8%B1%D9%86%D8%AA%D8%B3-kubernetes-r662/" rel="">نشر تطبيق جانغو آمن وقابل للتوسيع باستخدام كوبيرنتس Kubernetes</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-%D9%88%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A8%D9%8A%D8%A6%D8%AA%D9%87%D8%A7-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r714/" rel="">تثبيت بايثون 3 وإعداد بيئتها البرمجية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D9%83%D9%88%D8%A8%D8%B1%D9%86%D9%8A%D8%AA%D8%B3-kubernetes-r815/" rel="">تعرّف على كوبرنيتس Kubernetes</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">838</guid><pubDate>Sat, 01 Mar 2025 12:00:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x628;&#x631;&#x648;&#x62A;&#x648;&#x643;&#x648;&#x644; SSH &#x644;&#x644;&#x627;&#x62A;&#x635;&#x627;&#x644; &#x628;&#x62E;&#x627;&#x62F;&#x645; &#x628;&#x639;&#x64A;&#x62F;</title><link>https://academy.hsoub.com/devops/security/ssh/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%B1%D9%88%D8%AA%D9%88%D9%83%D9%88%D9%84-ssh-%D9%84%D9%84%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D8%A8%D8%AE%D8%A7%D8%AF%D9%85-%D8%A8%D8%B9%D9%8A%D8%AF-r837/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_02/How-To-Use-SSH-to-Connect-to-a-Remote-Server.png.b4d0de2a2f663022fdb837e5899b2a92.png" /></p>
<p>
	يعد بروتوكول النقل الآمن Secure Shell، أو <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr> </abbr>اختصارًا، إحدى الأدوات الأساسية التي يجب على مسؤول النظام إتقانها، حيث يستخدم لتسجيل الدخول الآمن إلى الأجهزة البعيدة عن طريق الطرفية أو سطر الأوامر، وهو من أكثر الطرق شيوعًا للوصول إلى خوادم لينكس، وستتعلم في هذا المقال كيفية التعامل مع بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> للاتصال بنظام بعيد خطوة بخطوة.
</p>

<h2 id="windows">
	العمل على نظام تشغيل ويندوز Windows
</h2>

<p>
	إذا كنا نستخدم نظام التشغيل ويندوز على جهازنا، فسنحتاج إلى تثبيت إحدى إصدارات برنامج OpenSSH لنتمكن من استخدام الأمر <code><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr></code> من <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a>. أما إن فضّلنا العمل في برنامج PowerShell، فيمكن قراءة <a href="https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse" rel="external nofollow">توثيقات مايكروسوفت</a> لإضافة OpenSSH إلى PowerShell. أما في حال العمل في بيئة لينكس Linux كاملة، فيمكن إعداد WSL -أي نظام ويندوز الفرعي لنظام لينكس Windows Subsystem for Linux- وهو طبقة توافق لتشغيل أنظمة لينكس داخل أنظمة ويندوز، مما يتيح لنا تشغيل طرفية حقيقية مباشرةً في نظام ويندوز دون الحاجة لآلة افتراضية ويتضمن الأمر <code><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr></code>. وأخيرًا، يمكننا <a href="https://academy.hsoub.com/programming/workflow/git/%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D9%86%D8%B8%D8%A7%D9%85-%D8%A5%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D9%84%D8%A5%D8%B5%D8%AF%D8%A7%D8%B1%D8%A7%D8%AA-%D8%AC%D9%8A%D8%AA-git-r1593/" rel="">تثبيت Git </a>على نظام ويندوز، والذي يوفر طرفية Bash أصلية تتضمن أيضًا الأمر <code><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr></code>. فجميع هذه البيئات الثلاثة مدعومة والخيار متروك لكم في العمل مع إحداها حسب ما تفضلونه.
</p>

<h2 id="linuxmac">
	العمل على نظام تشغيل لينكس Linux أو ماك Mac
</h2>

<p>
	إن كنا نستخدم نظام لينكس أو ماك فسيكون الأمر <code><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr></code> جاهزًا في الطرفية، وفيما يلي صيغته الأساسية:
</p>

<pre class="ipsCode">ssh remote_host
</pre>

<p>
	<code>remote_host</code> هو عنوان IP أو اسم النطاق domain الذي نريد الاتصال به.
</p>

<p>
	يفترض الأمر السابق أن اسم المستخدم على النظام البعيد هو نفس اسم المستخدم على نظامنا المحلي، أما إن كان اسم المستخدم مختلفًا، فيمكننا تحديده باستخدام الأمر التالي:
</p>

<pre class="ipsCode">ssh remote_username@remote_host
</pre>

<p>
	قد يُطلب منا إدخال كلمة مرور للاتصال بالخادم، وسنتحدث لاحقًا عن كيفية استخدام مفاتيح المصادقة المشفرة keys بدل كلمات المرور passwords.
</p>

<p>
	نستخدم الأمر التالي للخروج من جلسة <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> والعودة إلى الصدفة:
</p>

<pre class="ipsCode">Exit
</pre>

<h2 id="ssh-1">
	آلية عمل بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>
</h2>

<p>
	يعمل بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> عن طريق توصيل برنامج عميل client program يسمى sshd مع خادم <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr>، فبالعودة إلى مثالنا السابق، فإن <code><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr></code> هو بمثابة برنامج العميل، أما خادم <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr> فيعمل على الجهاز البعيد remote host الذي حددناه. يجب أن يبدأ خادم sshd بالعمل تلقائيًا في معظم بيئات لينكس. وإذا لم يعمل لسبب ما، فقد نحتاج إلى الوصول إلى الخادم من خلال إحدى طرفيّات المتصفح المعتمدة على الويب web-based console، أوالطرفيّة المحلية local serial console.
</p>

<p>
	يعتمد الأمر اللازم لتشغيل خادم <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr> على <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81-%D8%AA%D8%AE%D8%AA%D8%A7%D8%B1-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A7%D9%84%D9%85%D9%86%D8%A7%D8%B3%D8%A8%D8%A9-%D9%84%D9%83%D8%9F-r811/" rel="">توزيعة لينكس</a> التي نستخدمها، فعند استخدام توزيعة أوبنتو، يمكننا تشغيل الأمر التالي:
</p>

<pre class="ipsCode">sudo systemctl start ssh
</pre>

<p>
	يُشغّل الأمر السابق خادم sshd وبإمكاننا الآن تسجيل الدخول إلى الخادم عن بعد.
</p>

<h2 id="ssh-2">
	كيفية ضبط إعدادات بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>
</h2>

<p>
	عندما نغيّر إعدادات بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> فإننا نغيّر أيضًا إعدادات خادم sshd. سنجد ملف الإعدادات ضمن المسار <code>/etc/<abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr>/sshd_config</code>. ومن الضروري أخذ نسخة احتياطيةً عن هذا الملف قبل بدء التعديل، باستخدام الأمر التالي:
</p>

<pre class="ipsCode">sudo cp /etc/ssh/sshd_config{,.bak}
</pre>

<p>
	ثم نفتحه باستخدام محرر النصوص <code>nano</code> أو أي محرر آخر:
</p>

<pre class="ipsCode">sudo nano /etc/ssh/sshd_config
</pre>

<p>
	لنطلع على بعض خيارات الملف:
</p>

<pre class="ipsCode">Port 22
</pre>

<p>
	عند الإعلان عن منفذ جديد port declaration فإننا نحدد المنفذ الذي سيستخدمه خادم sshd للاتصال، والمنفذ الافتراضي لذلك هو <code>22</code>. يُفضّل ألا نغيّر هذا الإعداد، إلا إن كان لدينا سبب لذلك. وسنوضح لاحقًا كيفية الاتصال بالمنفذ الجديد.
</p>

<pre class="ipsCode">HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
</pre>

<p>
	عند الإعلان عن مفاتيح المضيف host keys فإننا نحدد مكان البحث عن مفاتيح المضيف العامة global host keys. وسنتحدث عن ذلك لاحقًا.
</p>

<pre class="ipsCode">SyslogFacility AUTH
LogLevel INFO
</pre>

<p>
	يحدد العنصران السابقان مستوى التسجيل logging level. إذ قد تكون زيادة التسجيل طريقة جيدة لاكتشاف المشكلات في <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>:
</p>

<pre class="ipsCode">LoginGraceTime 120
PermitRootLogin yes
StrictModes yes
</pre>

<p>
	تحدد المعاملات السابقة بعض معلومات تسجيل الدخول:
</p>

<ul>
	<li>
		يحدد المعامل <code>LoginGraceTime</code> عدد الثواني إبقاء الاتصال قبل تسجيل الدخول، ويُنصح أن نجعل هذا الوقت أكثر بقليل من الوقت اللازم لتسجيل الدخول عادة
	</li>
	<li>
		يحدد المعامل <code>PermitRootLogin</code> هل يُسمح للمستخدم الجذر تسجيل الدخول أم لا، ويجب في معظم الحالات ضبط هذا الإعداد على الخيار <code>no</code> عند إنشاء مستخدم لديه امتيازات الجذر لتقليل خطر حصول أي شخص على امتيازات الجذر والوصول إلى خادمنا
	</li>
	<li>
		المعامل <code>strictModes</code> هو حارس أمان لا يسمح بتسجيل الدخول إذا كانت ملفات المصادقة متاحة القراءة للجميع. وهذا يمنع تسجيل الدخول عندما تكون ملفات الإعدادات غير آمنة
	</li>
</ul>

<pre class="ipsCode">X11Forwarding yes
X11DisplayOffset 10
</pre>

<ul>
	<li>
		تضبط هذه المعاملات خاصيةً تسمى <code>X11 Forwarding</code> تتيح لنا عرض واجهة المستخدم الرسومية GUI للنظام البعيد على النظام المحلي، ويجب تفعيل هذا الخيار على الخادم واستخدامه مع عميل <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> أثناء الاتصال مصحوبًا بالخيار <code>x-</code>.
	</li>
</ul>

<p>
	بعد التعديل على الملف نحفظه ثم نغلقه. وإن كنا نستخدم محرر النصوص nano، نضغط على المفتاحين Ctrl+X، ثم Y عندما يُطلب منا ذلك، ثم نضغط على مفتاح Enter. ولا ننسى إعادة تحميل خادم sshd لتنفيذ التعديلات التي أجريناها في الملف باستخدام الأمر التالي:
</p>

<pre class="ipsCode">sudo systemctl reload ssh
</pre>

<p>
	يجب اختبار التعديلات بدقة للتأكد أنها تعمل كما يجب. كما يمكننا فتح بضع جلسات في الطرفية أثناء التعديل على ملف الإعدادات، لإلغاء التعديلات إن لزم الأمر دون الحاجة إلى إعادة تسجيل الدخول.
</p>

<h2 id="sshkeys">
	تسجيل الدخول إلى <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> باستخدام مفاتيح المصادقة Keys
</h2>

<p>
	يُنصح لتسجيل الدخول إلى نظام بعيد استخدام كلمات المرور passwords أو مفاتيح المصادقة keys والتي تتصف بأنها أسرع وأكثر أمانًا من كلمات المرور.
</p>

<h3 id="-1">
	آلية عمل المصادقة المعتمدة على المفاتيح
</h3>

<p>
	تعمل المصادقة المعتمدة على مفاتيح المصادقة عن طريق إنشاء زوج من المفاتيح: مفتاح خاص private key ومفتاح عام public key. يوجد المفتاح الخاص على جهاز العميل وهو سرّي، أما المفتاح العام فيمكن إعطاؤه لأي شخص أو وضعه على أي خادم نرغب الوصول إليه.
</p>

<p>
	عندما نحاول الاتصال باستخدام زوج من المفاتيح، نستخدم الخادم المفتاح العام لإنشاء رسالة محمية لحاسوب العميل لا يمكن قراءتها إلا باستخدام المفتاح الخاص. بعد ذلك، يرسل حاسوب العميل الاستجابة المناسبة إلى الخادم وسيعرف الخادم أن العميل يُسمح له بالاتصال. تُنفَّذ هذه العملية تلقائيًا بعد ضبط إعدادات المفاتيح.
</p>

<h3 id="ssh-3">
	كيفية إنشاء مفاتيح المصادقة في بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>
</h3>

<p>
	يجب إنشاء مفاتيح المصادقة في بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> على الحاسوب الذي سنسجل الدخول منه، أي جهازنا المحلي. نكتب ما يلي في سطر الأوامر:
</p>

<pre class="ipsCode">ssh-keygen -t rsa
</pre>

<p>
	قد يُطلب منا تعيين كلمة مرور على ملفات المفاتيح نفسها، ولكن هذا الأمر غير شائع. يجب الضغط على مفتاح Enter لقبول الإعدادات الافتراضية. وستُنشأ المفاتيح في المجلد <code>‎~/.<abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr>/id_rsa.pub</code> و المجلد <code>‎~/.<abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr>/id_rsa</code>.
</p>

<p>
	نغيّر مكان إنشاء المفاتيح إلى المجلد <code>‎.<abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr></code>باستخدام الأمر التالي:
</p>

<pre class="ipsCode">cd ~/.ssh
</pre>

<p>
	نستعرض سماحيات الملف باستخدام الأمر:
</p>

<pre class="ipsCode">ls -l
</pre>

<p>
	وسنحصل على الخرج التالي:
</p>

<pre class="ipsCode">-rw-r--r-- 1 demo demo  807 Sep  9 22:15 authorized_keys
-rw------- 1 demo demo 1679 Sep  9 23:13 id_rsa
-rw-r--r-- 1 demo demo  396 Sep  9 23:13 id_rsa.pub
</pre>

<p>
	نلاحظ أن الملف <code>id_rsa</code> قابل القراءة والكتابة بواسطة المالك فقط، مما يساعد على إبقاء الملف سريًّا. أما الملف <code>id_rsa.pub</code> فيمكن مشاركته وله سماحيات تمكّن ذلك.
</p>

<h3 id="-2">
	نقل المفاتيح العامة إلى الخادم
</h3>

<p>
	إن فعّلنا الوصول إلى الخادم باستخدام كلمة المرور، فبإمكاننا نسخ مفتاح المرور العام إلى الخادم باستخدام الأمر التالي:
</p>

<pre class="ipsCode">ssh-copy-id remote_host
</pre>

<p>
	سيؤدي الأمر إلى إنشاء جلسة <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>، وبعد إدخال كلمة المرور، سيُنسخ المفتاح العام إلى ملف المفاتيح المعتمدة authorized keys في الخادم، مما يسمح لنا بتسجيل الدخول دون كلمة المرور في المرة القادمة.
</p>

<h2 id="ssh-4">
	الخيارات المتاحة لعميل <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>
</h2>

<p>
	هناك خيارات عدة عند الاتصال عبر بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>، بعضها قد يكون ضروريًا لمطابقة الإعدادات الموجودة في sshd عند المضيف البعيد remote host. مثلًا، إذا غيّرنا رقم المنفذ في إعدادات sshd، فعلينا تغييره عند العميل باستخدام الأمر التالي:
</p>

<pre class="ipsCode">ssh -p port_number remote_host
</pre>

<p>
	<strong>ملاحظة:</strong>: يُعدَ تغيير منفذ بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr> طريقةً مناسبةً لزيادة الأمان، فإذا كنا نسمح بالاتصال بخادم معروف عبر بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr> على المنفذ 22 كالمعتاد، ووضعنا كلمة مرور، فمن المحتمل أن نتعرض لمحاولات تسجيل دخول آلية عديدة automated logins. لكن، لا يُعدّ استخدام مفاتيح المصادقة وتشغيل بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr> على منفذ غير المنفذ المعتاد الحل الأمني الأمثل، ولكنه يفيد في الحد من الهجمات الآلية.
</p>

<p>
	إذا أردنا تنفيذ أمر واحد فقط على النظام البعيد، فيمكن تحديده بعد المضيف، كما يلي:
</p>

<pre class="ipsCode">ssh remote_host command_to_run
</pre>

<p>
	سنتصل بالنظام البعيد وسيُنَفذ الأمر بعد إدخال مفتاح المرور. إن كانت خاصية <strong>X11 Forwarding</strong> مفعلةً على كلا الجهازين، فيمكننا تفعيل ميزاتها باستخدام الأمر التالي:
</p>

<pre class="ipsCode">ssh -X remote_host
</pre>

<p>
	بشرط توفر الأدوات المناسبة لذلك على حاسوبنا، فبرامج واجهة المستخدم الرسومية التي نستخدمها على النظام البعيد ستُشَغّل الآن على نظامنا المحلي.
</p>

<h2 id="-3">
	إلغاء التحقق من كلمة المرور
</h2>

<p>
	يمكن عند إنشاء مفاتيح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>، تحسين أمان الخادم وتعطيل مصادقة كلمة المرور، وتمكين تسجيل الدخول إلى الخادم من خلال المفتاح الخاص المقترن بالمفتاح العام الذي نبّته على الخادم.
</p>

<p>
	<strong>تنبيه:</strong> قبل متابعة هذه الخطوة، يتوجب التأكد من تثبيت مفتاح عام على الخادم. وإلا لن نستطيع تسجيل الدخول!
</p>

<p>
	نفتح ملف إعدادات sshd كمستخدم مسؤول root user أو أو مستخدم يتمتع بامتيازات sudo باستخدام الأمر التالي:
</p>

<pre class="ipsCode">sudo nano /etc/ssh/sshd_config
</pre>

<p>
	نبحث عن سطر <code>Password Authentication</code> ثم نزيل التعليق عنه بمسح رمز <code>#</code>، ونغيّر قيمته إلى القيمة <code>no</code>:
</p>

<pre class="ipsCode">PasswordAuthentication no
</pre>

<p>
	أما الخيار <code>PubkeyAuthentication</code> و <code>ChallengeResponseAuthentication</code> فلا يحتاجان إلى تعديل، شريطة ألا نكون عدلنا هذا الملف، إذ يجب تركهما على الضبط الإفتراضي:
</p>

<pre class="ipsCode">PubkeyAuthentication yes
ChallengeResponseAuthentication no
</pre>

<p>
	نحفظ الملف ونغلقه بعد الانتهاء من التعديلات. ثم نعيد تحميل برنامج <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr>:
</p>

<pre class="ipsCode">sudo systemctl reload ssh
</pre>

<p>
	وبهكذا نكون قد ألغينا التحقق من كلمة المرور، وفعّلنا خيار الوصول إلى الخادم حصرًا عبر مفتاح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>.
</p>

<h2 id="-4">
	الخاتمة
</h2>

<p>
	شرحنا في هذا المقال كيفية استخدام بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> وهو أمر مفيد جدًا في مجال الحوسبة السحابية، وسنحتاج أثناء استخدام الخيارات المتنوعة مزيدًا من الميزات المتقدمة التي من شأنها تسهيل عملنا. لهذا بقي بروتوكول <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> شائع الاستخدام لأنه آمن وصغير الحجم ومفيد في الكثير من الحالات.
</p>

<p>
	ترجمة ، وبتصرّف، للمقال <a href="https://www.digitalocean.com/community/tutorials/how-to-use-ssh-to-connect-to-a-remote-server" rel="external nofollow">How To Use <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> to Connect to a Remote Server</a> لكاتبه Justin Ellingwood.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/devops/security/ssh/%D9%85%D8%A7-%D9%87%D9%8A-%D8%AA%D9%82%D9%86%D9%8A%D8%A9-ssh%D8%9F-r793/" rel="">ما هي تقنية <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/security/ssh/%D8%A3%D9%86%D9%81%D8%A7%D9%82-ssh%D8%8C-%D9%85%D8%A7%D9%87%D9%8A%D8%AA%D9%87%D8%A7-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF%D9%87%D8%A7-r76/" rel="">أنفاق <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr>، ماهيتها وكيفية إعدادها</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/security/ssh/%D9%83%D9%8A%D9%81%D9%8A%D9%91%D8%A9-%D9%85%D9%86%D8%B9-ssh-%D9%85%D9%86-%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%AC%D9%84%D8%B3%D8%A7%D8%AA-r391/" rel="">كيفيّة منع <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> من فصل الجلسات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B7%D8%B1%D9%81%D9%8A%D9%91%D8%A9-%D9%84%D9%8A%D9%86%D9%83%D8%B3-linux-terminal-r18/" rel="">مدخل إلى طرفيّة لينكس Linux Terminal</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">837</guid><pubDate>Sat, 22 Feb 2025 15:07:00 +0000</pubDate></item><item><title>&#x623;&#x62A;&#x645;&#x62A;&#x629; &#x646;&#x634;&#x631; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; GitHub Webhooks</title><link>https://academy.hsoub.com/devops/deployment/%D8%A3%D8%AA%D9%85%D8%AA%D8%A9-%D9%86%D8%B4%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-github-webhooks-r836/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_02/webhooksGitHub_.png.6fa651ee0affcf3c402960b2eb27c539.png" /></p>
<p>
	تعد عملية نشر نسخ instances من تطبيق ويب يدويًا على خادم أو أكثر عملية رتيبة وتستغرق الكثير من الوقت، لكن ببذل جهد بسيط يمكنك جعل عملية نشر تطبيق الويب مؤتمتة دون الحاجة لتدخل يدوي. سنسلط الضوء في هذا المقال على طريقة بسيطة لأتمتة نشر تطبيقات الويب باستخدام كل من <a href="https://academy.hsoub.com/questions/3480-%D9%85%D8%A7-%D9%87%D9%88-web-hook-%D9%88%D9%85%D8%A7-%D9%87%D9%8A-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85%D8%A7%D8%AA%D9%87%D8%9F/" rel="">خطافات الويب</a> webhooks وحزم البناء buildpacks وملفات Procfiles.
</p>

<p>
	لطالما كانت عملية تشغيل تطبيقات الويب على الخادم ودفع push أية تحديثات لاحقة إليه عملية مضجرة وشاقة لمطوري الويب، مما يدفعهم للاستعانة بأحد مزودي المنصة كخدمة Platform as a service أو PaaS اختصارًا فهذه المنصات توفر لهم بيئة متكاملة لتطوير وتشغيل التطبيقات دون الحاجة إلى إدارة البنية التحتية، كما تُسهّل نشر التطبيقات دون أن يضطر المطوّر للاهتمام بإجراءات حجز الخادم وإعداده مقابل زيادة طفيفة في التكلفة ونقص بسيط في المرونة.
</p>

<p>
	لكن قد يحتاج المطور أو <a href="https://academy.hsoub.com/devops/general/%D8%AA%D8%B9%D9%84%D9%85-devops/" rel="">مسؤول DevOps</a> لنشر التطبيقات على خوادم مدارة دون الاستعانة بطرف ثالث. وفي هذه الحالة يفضل أن يبتكر أداة بسيطة لأتمتة عملية النشر، وهو أمر غير معقّد، ويغدو أسهل كلما كانت المتطلبات أبسط، لذا سنتعلم في مقال اليوم كيف نطور هذه الأداة وندعها تنفذ الأجزاء الروتينية والمملة من عمليات نشر تطبيقات الويب نيابة عنا ونوفر وقتنا وجهدنا.
</p>

<p>
	<strong>ملاحظة</strong>: تختلف خطوات أتمتة نشر موقع مبني بلغة PHP عن خطوات نشر تطبيق Node.js. كما توجد حلول جاهزة أشمل تدعى حزم البناء buildpacks مثل دوكو Dokku تناسب مجموعة أوسع من التقنيات. سنشرح في هذا المقال أداة بسيطة لأتمتة عمليات نشر تطبيقات الويب الخاصة بنا، وذلك باستخدام خطافات الويب webhooks وحزم البناء buildpacks وملفات Procfiles في غيت هب GitHub. ويمكن الاطلاع على الشيفرة المصدرية source code للنموذج الأولي منها على <a href="https://github.com/hjr265/toptal-hopper" rel="external nofollow">موقع غيت هب GitHub</a>. <strong>ملاحظة</strong>: تستخدم حزم البناء buildpacks لتحديد كيفية تكوين بيئة التطبيق قبل نشره، وتحدد ملفات Procfiles النصية أنواع العمليات في التطبيق، مما يسهل تشغيلها تلقائياً. وسنشرحها مفصلًا في الفقرات التالية.
</p>

<h2 id="">
	بناء تطبيق الويب
</h2>

<p>
	ستكون أولى خطواتنا كتابة برنامج بسيط <a href="https://academy.hsoub.com/files/41-%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-go/" rel="">بلغة غو</a> Go، لا تقلق إذا كنت لا تألف العمل بلغة غو GO فبنية الشيفرة البرمجية المستخدمة بسيطة نسبيًا وستجدها سهلة الفهم. ويمكن كتابة البرنامج باللغة التي تناسبنا. قبل البدء لنتأكد من تثبيت Go distribution على نظام التشغيل باتباع الخطوات المذكورة <a href="https://go.dev/doc/install" rel="external nofollow">الموقع الرسمي</a>، بعدها يفضل تنزيل الشيفرة المصدرية لأداتنا باستنساخ clone <a href="https://github.com/hjr265/toptal-hopper" rel="external nofollow">مستودع غيت هب</a> GitHub repository، فهذا سيسهّل متابعة تتمة الخطوات، فمقاطع الشيفرة البرمجية في المستودع مسمّاة بنفس أسماء الملفات التي سنحددها في المقال. إن استخدام برنامج غو Go دونًا عن غيره لكتابة برنامجنا سيحدّ من حاجتنا للاستعانة باعتماديات dependencies خارجية، ففي حالتنا لن نحتاج لتشغيل برنامج غو على الخادم إلا لتثبيت غيت Git وباش <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%8A-bash%D8%9F-r794/" rel="">Bash</a>، كما أن البرنامج لا يستهلك الكثير من موارد الجهاز مثل الذاكرة رام RAM والمعالج CPU عندما نضبطه بطريقة صحيحة.
</p>

<h2 id="webhooks">
	ما هي خطافات الويب Webhooks في غيت هب
</h2>

<p>
	تمكّننا خطافات الويب في غيت هب من ضبط المستودع GitHub repository ليصدّر الأحداث events كلما طرأ تغيير ضمن المستودع أو أجرى مستخدم إجراء معينًا في المستودع المستضاف، وهذا يتيح للمستخدمين التسجيل subscribe في هذه الأحداث وتلقي إشعارات بمختلف الأحداث التي تجري في مستودعنا وما يتعلق به من خلال استدعاءات الروابط URL invocations حيث سيستدعى عنوان URL معين تلقائيًا عندما يقع حدث معين في المستودع.
</p>

<h3 id="webhooks-1">
	خطوات إنشاء Webhooks في غيت هب
</h3>

<p>
	يعد إنشاء خطاف ويب Webhooks عملية بسيطة سنلخصها في الخطوات التالية:
</p>

<ol>
	<li>
		نفتح صفحة الإعدادات settings في مستودعنا
	</li>
	<li>
		نضغط على خطافات الويب والخدمات Webhooks &amp; Services في قائمة الخيارات
	</li>
	<li>
		نضغط على زر إضافة خطاف ويب Add webhook.
	</li>
	<li>
		ندخل رابط، ونضيف إن شئنا رمزًا سريًا secret يتيح للمستقبل التحقق من حمولة البيانات الواردة payload
	</li>
	<li>
		نحدد الاختيارات الأخرى في الصفحة حسب متطلباتنا
	</li>
	<li>
		أخيرًا، نضغط على الزر الأخضر Add webhook.
	</li>
</ol>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="167016" href="https://academy.hsoub.com/uploads/monthly_2025_02/001--webhook.png.27e240bca19e9c50ee7cc042261abca6.png" rel=""><img alt="001 إضافة webhook" class="ipsImage ipsImage_thumbnailed" data-fileid="167016" data-ratio="88.00" data-unique="gjdz9a653" style="width: 400px; height: auto;" width="400" src="https://academy.hsoub.com/uploads/monthly_2025_02/001--webhook.png.27e240bca19e9c50ee7cc042261abca6.png"> </a>
</p>

<p>
	يقدم غيت هب توثيقًا مستفيضًا <a href="https://developer.github.com/webhooks/" rel="external nofollow">لخطافات الويب</a> وجميع التفاصيل المرتبطة بها، لكن الجزء الذي سنحتاج إليه في مقالنا هذا هو <a href="https://developer.github.com/v3/activity/events/types/#pushevent" rel="external nofollow">حدث الدفع</a> push event الذي يُصدَّر كلما أجرى مستخدم عملية دفع إلى أي فرع branch في المستودع repository.
</p>

<h2 id="buildpacks">
	حزم البناء Buildpacks
</h2>

<p>
	أصبحت حزم البناء جزءًا لا يتجزأ من عملية نشر التطبيقات في وقتنا هذا، ويستخدمها العديد من مزودي المنصات كخدمة PaaS. حيث تتيح لنا حزم البناء تحديد كيفية ضبط مكدس التقنيات stack التي سنحتاجها لتشغيل تطبيقنا قبل أن ننشره، وكتابتها بغاية السهولة، ففي معظم الأحيان لن نحتاج حتى لكتابتها بنفسنا حيث تستطيع بإجراء بحث سريع على الإنترنت إيجاد حزم بناء جاهزة يمكننا استخدامها في عملية نشر تطبيقنا دون الحاجة إلى تعديلها. فمثلًا تقدم منصة Heroku <a href="https://devcenter.heroku.com/articles/buildpack-api" rel="external nofollow">توثيقًا شاملًا عن بنية حزم البناء</a> و<a href="https://devcenter.heroku.com/articles/buildpacks#default-buildpacks" rel="external nofollow">قائمة تضم أكثر حزم البناء المستخدمة وأفضلها بنيةً</a>. تستخدم أداة الأتمتة التي نبنيها سكربت تصريف compile script لتجهيز التطبيق قبل تشغيله. ولا بد أن ننوه أننا لن نستفيض في كتابة حزم البناء، وإنما سنفترض أن سكربتات حزم البناء buildpack scripts الجاهزة تتوافق مع بيئة سطر الأوامر باش <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%8A-bash%D8%9F-r794/" rel="">Bash</a>، وأنها ستُشغَّل على نظام تشغيل أبونتو Ubuntu مثُبّت حديثًا ولم يطرأ عليه أي تعديل، ويمكن لاحقًا التوسّع أكثر فيها حسب متطلبات عملنا.
</p>

<h2 id="procfiles">
	ملفات Procfiles
</h2>

<p>
	هي ملفات نصية بسيطة تمكّننا من تحديد مختلف أنواع الإجرائيات processes المستخدمة في تطبيقنا، وتُستخدم عادةً في معظم التطبيقات البسيطة إجرائية ويب web واحدة فقط تعالج طلبات بروتوكول HTTP. لكتابة هذه الملفات ما علينا سوى تحديد نوع إجرائية واحد في كل سطر وذلك بكتابة اسمها متبوعًا بنقطة مزدوجة <code>:</code> متبوعة بالأمر الذي سيولّد spawn هذه الإجرائية كمل يلي:
</p>

<pre class="ipsCode">&lt;type&gt;: &lt;command&gt;
</pre>

<p>
	على سبيل المثال، إذا كان تطبيقنا مكتوب بنود جي إس Node.js، علينا تنفيذ الأمر <code>node index.js</code> لتشغيل خادم الويب. يمكن إذًا أن ننشئ ملف Procfile في المجلد الأساسي للشيفرة البرمجية ونسميه Procfile ونكتب ما يلي ضمنه:
</p>

<pre class="ipsCode">web: node index.js
</pre>

<p>
	نلزم تطبيقاتنا بتحديد أنواع الإجرائيات في ملفات Procfiles لنتمكن من تشغيلها تلقائيًا بعد سحب pulling الشيفرة البرمجية.
</p>

<h2 id="-1">
	معالجة الأحداث
</h2>

<p>
	نحتاج في برنامجنا إلى إضافة خادم HTTP مهمته استقبال طلبات POST الواردة من غيت هب، لذا علينا أن نخصص مسار رابط URL path لمعالجة هذه الطلبات. ونقدم في النموذج التالي مثالًا عن بنية الدالة function التي ستعالج الحمولات payloads الواردة من هذا النوع:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7582_10" style=""><span class="com">// hook.go</span><span class="pln">
type </span><span class="typ">HookOptions</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">App</span><span class="pln">    </span><span class="pun">*</span><span class="typ">App</span><span class="pln">
    </span><span class="typ">Secret</span><span class="pln"> string
</span><span class="pun">}</span><span class="pln">


func </span><span class="typ">NewHookHandler</span><span class="pun">(</span><span class="pln">o </span><span class="pun">*</span><span class="typ">HookOptions</span><span class="pun">)</span><span class="pln"> http</span><span class="pun">.</span><span class="typ">Handler</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> http</span><span class="pun">.</span><span class="typ">HandlerFunc</span><span class="pun">(</span><span class="pln">func</span><span class="pun">(</span><span class="pln">w http</span><span class="pun">.</span><span class="typ">ResponseWriter</span><span class="pun">,</span><span class="pln"> r </span><span class="pun">*</span><span class="pln">http</span><span class="pun">.</span><span class="typ">Request</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        evName </span><span class="pun">:=</span><span class="pln"> r</span><span class="pun">.</span><span class="typ">Header</span><span class="pun">.</span><span class="typ">Get</span><span class="pun">(</span><span class="str">"X-Github-Event"</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> evName </span><span class="pun">!=</span><span class="pln"> </span><span class="str">"push"</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            log</span><span class="pun">.</span><span class="typ">Printf</span><span class="pun">(</span><span class="str">"Ignoring '%s' event"</span><span class="pun">,</span><span class="pln"> evName</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">


        body</span><span class="pun">,</span><span class="pln"> err </span><span class="pun">:=</span><span class="pln"> ioutil</span><span class="pun">.</span><span class="typ">ReadAll</span><span class="pun">(</span><span class="pln">r</span><span class="pun">.</span><span class="typ">Body</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> err </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
            http</span><span class="pun">.</span><span class="typ">Error</span><span class="pun">(</span><span class="pln">w</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Internal Server Error"</span><span class="pun">,</span><span class="pln"> http</span><span class="pun">.</span><span class="typ">StatusInternalServerError</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">


        </span><span class="kwd">if</span><span class="pln"> o</span><span class="pun">.</span><span class="typ">Secret</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="str">""</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            ok </span><span class="pun">:=</span><span class="pln"> </span><span class="kwd">false</span><span class="pln">
            </span><span class="kwd">for</span><span class="pln"> _</span><span class="pun">,</span><span class="pln"> sig </span><span class="pun">:=</span><span class="pln"> range strings</span><span class="pun">.</span><span class="typ">Fields</span><span class="pun">(</span><span class="pln">r</span><span class="pun">.</span><span class="typ">Header</span><span class="pun">.</span><span class="typ">Get</span><span class="pun">(</span><span class="str">"X-Hub-Signature"</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">!</span><span class="pln">strings</span><span class="pun">.</span><span class="typ">HasPrefix</span><span class="pun">(</span><span class="pln">sig</span><span class="pun">,</span><span class="pln"> </span><span class="str">"sha1="</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    </span><span class="kwd">continue</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
                sig </span><span class="pun">=</span><span class="pln"> strings</span><span class="pun">.</span><span class="typ">TrimPrefix</span><span class="pun">(</span><span class="pln">sig</span><span class="pun">,</span><span class="pln"> </span><span class="str">"sha1="</span><span class="pun">)</span><span class="pln">
                mac </span><span class="pun">:=</span><span class="pln"> hmac</span><span class="pun">.</span><span class="typ">New</span><span class="pun">(</span><span class="pln">sha1</span><span class="pun">.</span><span class="typ">New</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">byte</span><span class="pun">(</span><span class="pln">o</span><span class="pun">.</span><span class="typ">Secret</span><span class="pun">))</span><span class="pln">
                mac</span><span class="pun">.</span><span class="typ">Write</span><span class="pun">(</span><span class="pln">body</span><span class="pun">)</span><span class="pln">
                </span><span class="kwd">if</span><span class="pln"> sig </span><span class="pun">==</span><span class="pln"> hex</span><span class="pun">.</span><span class="typ">EncodeToString</span><span class="pun">(</span><span class="pln">mac</span><span class="pun">.</span><span class="typ">Sum</span><span class="pun">(</span><span class="pln">nil</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    ok </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
                    </span><span class="kwd">break</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">!</span><span class="pln">ok </span><span class="pun">{</span><span class="pln">
                log</span><span class="pun">.</span><span class="typ">Printf</span><span class="pun">(</span><span class="str">"Ignoring '%s' event with incorrect signature"</span><span class="pun">,</span><span class="pln"> evName</span><span class="pun">)</span><span class="pln">
                </span><span class="kwd">return</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">


        ev </span><span class="pun">:=</span><span class="pln"> github</span><span class="pun">.</span><span class="typ">PushEvent</span><span class="pun">{}</span><span class="pln">
        err </span><span class="pun">=</span><span class="pln"> json</span><span class="pun">.</span><span class="typ">Unmarshal</span><span class="pun">(</span><span class="pln">body</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ev</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> err </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
            log</span><span class="pun">.</span><span class="typ">Printf</span><span class="pun">(</span><span class="str">"Ignoring '%s' event with invalid payload"</span><span class="pun">,</span><span class="pln"> evName</span><span class="pun">)</span><span class="pln">
            http</span><span class="pun">.</span><span class="typ">Error</span><span class="pun">(</span><span class="pln">w</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Bad Request"</span><span class="pun">,</span><span class="pln"> http</span><span class="pun">.</span><span class="typ">StatusBadRequest</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">


        </span><span class="kwd">if</span><span class="pln"> ev</span><span class="pun">.</span><span class="typ">Repo</span><span class="pun">.</span><span class="typ">FullName</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> nil </span><span class="pun">||</span><span class="pln"> </span><span class="pun">*</span><span class="pln">ev</span><span class="pun">.</span><span class="typ">Repo</span><span class="pun">.</span><span class="typ">FullName</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> o</span><span class="pun">.</span><span class="typ">App</span><span class="pun">.</span><span class="typ">Repo</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            log</span><span class="pun">.</span><span class="typ">Printf</span><span class="pun">(</span><span class="str">"Ignoring '%s' event with incorrect repository name"</span><span class="pun">,</span><span class="pln"> evName</span><span class="pun">)</span><span class="pln">
            http</span><span class="pun">.</span><span class="typ">Error</span><span class="pun">(</span><span class="pln">w</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Bad Request"</span><span class="pun">,</span><span class="pln"> http</span><span class="pun">.</span><span class="typ">StatusBadRequest</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">


        log</span><span class="pun">.</span><span class="typ">Printf</span><span class="pun">(</span><span class="str">"Handling '%s' event for %s"</span><span class="pun">,</span><span class="pln"> evName</span><span class="pun">,</span><span class="pln"> o</span><span class="pun">.</span><span class="typ">App</span><span class="pun">.</span><span class="typ">Repo</span><span class="pun">)</span><span class="pln">


        err </span><span class="pun">=</span><span class="pln"> o</span><span class="pun">.</span><span class="typ">App</span><span class="pun">.</span><span class="typ">Update</span><span class="pun">()</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> err </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">})</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نبدأ بالتحقق من نوع الحدث event الذي ولّد هذه الحمولة payload، وبما أن حدث الدفع push هو الحدث الوحيد الذي يهمنا يمكننا تجاهل بقية الأحداث. لكن حتى لو ضبطنا خطاف الويب ليصدّر أحداث الدفع فقط، لا بد أن نتوقع استقبال نوع آخر على الأقل من الأحداث على نقطة اتصال خطافنا hook endpoint التي تمثل الرابط الذي تُرسَل إليه البيانات وهو الحدث <code>Ping</code>.
</p>

<p>
	الغاية من هذا الحدث التأكد من ضبط خطاف الويب بطريقة صحيحة على غيت هب. الخطوة التالية هي قراءة محتوى الطلب الوارد بأكمله، ثم حساب قيمة التشفير باستخدام خوارزمية التشفير HMAC-SHA1 والرمز السري secret ذاته الذي حددناه عند ضبط خطاف الويب، ومقارنتها بالبصمة signature المتضمَّنة في ترويسة الطلب للتحقق من صلاحية الحمولة payload الواردة، وفي حالتنا اخترنا تجاهل خطوة التحقق هذه إذا لم يكن الرمز السري محدَّدًا. 
</p>

<p>
	<strong>ملاحظة</strong>: ننصح بوضع حد أقصى لحجم البيانات الذي يمكن التعامل معه قبل قراءة محتوى الطلب كاملًا، لكننا لن نتطرق إلى هذا في مقالنا وسنركز على الجوانب الأساسية للأداة فقط. بعدها، نستخدم هيكل بيانات struct من مكتبة <a href="https://github.com/google/go-github" rel="external nofollow"> GitHub client library for Go</a> لتفريغ أو تحويل unmarshal حمولة البيانات الواردة فيها، وبما أننا نعلم أنه حدث دفع push فيمكننا استخدام <a href="https://godoc.org/github.com/google/go-github/github#PushEvent" rel="external nofollow">بنية حدث الدفع</a>. ثم نستخدم مكتبة التشفير القياسية بصيغة Json لتفريغ الحمولة إلى نسخة من البنية، ستنفذ بعدها بعض عمليات التحقق من الصحة وفي حال خلوها من أية مشكلة ستُستدعى الدالة التي تبدأ بتحديث تطبيقنا.
</p>

<h2 id="-2">
	تحديث التطبيق
</h2>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="167017" href="https://academy.hsoub.com/uploads/monthly_2025_02/003----.png.b343b6d831ea50f61841cbc5d7db72b8.png" rel=""><img alt="003 التحقق من جاهزية المستودع" class="ipsImage ipsImage_thumbnailed" data-fileid="167017" data-ratio="83.25" data-unique="tcfynt3qt" style="width: 400px; height: auto;" width="400" src="https://academy.hsoub.com/uploads/monthly_2025_02/003----.png.b343b6d831ea50f61841cbc5d7db72b8.png"> </a>
</p>

<h2 id="-3">
	تهيئة المستودع المحلي
</h2>

<p>
	تبدأ هذه العملية بإجراء تحقق بسيط لنحدد إذا كانت هذه المرة الأولى التي نحاول نشر التطبيق فيها، وذلك بالتحقق من وجود مجلد المستودع المحلي local repository، فإذا لم يكن موجودًا نبدأ أولى خطواتنا بتهيئة initialize المستودع المحلي لدينا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7582_13" style=""><span class="com">// app.go</span><span class="pln">


func </span><span class="pun">(</span><span class="pln">a </span><span class="pun">*</span><span class="typ">App</span><span class="pun">)</span><span class="pln"> initRepo</span><span class="pun">()</span><span class="pln"> error </span><span class="pun">{</span><span class="pln">
    log</span><span class="pun">.</span><span class="typ">Print</span><span class="pun">(</span><span class="str">"Initializing repository"</span><span class="pun">)</span><span class="pln">


    err </span><span class="pun">:=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">MkdirAll</span><span class="pun">(</span><span class="pln">a</span><span class="pun">.</span><span class="pln">repoDir</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0755</span><span class="pun">)</span><span class="pln">
    </span><span class="com">// Check err</span><span class="pln">


    cmd </span><span class="pun">:=</span><span class="pln"> exec</span><span class="pun">.</span><span class="typ">Command</span><span class="pun">(</span><span class="str">"git"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"--git-dir="</span><span class="pun">+</span><span class="pln">a</span><span class="pun">.</span><span class="pln">repoDir</span><span class="pun">,</span><span class="pln"> </span><span class="str">"init"</span><span class="pun">)</span><span class="pln">
    cmd</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln">
    err </span><span class="pun">=</span><span class="pln"> cmd</span><span class="pun">.</span><span class="typ">Run</span><span class="pun">()</span><span class="pln">
    </span><span class="com">// Check err</span><span class="pln">


    cmd </span><span class="pun">=</span><span class="pln"> exec</span><span class="pun">.</span><span class="typ">Command</span><span class="pun">(</span><span class="str">"git"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"--git-dir="</span><span class="pun">+</span><span class="pln">a</span><span class="pun">.</span><span class="pln">repoDir</span><span class="pun">,</span><span class="pln"> </span><span class="str">"remote"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"add"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"origin"</span><span class="pun">,</span><span class="pln"> fmt</span><span class="pun">.</span><span class="typ">Sprintf</span><span class="pun">(</span><span class="str">"git@github.com:%s.git"</span><span class="pun">,</span><span class="pln"> a</span><span class="pun">.</span><span class="typ">Repo</span><span class="pun">))</span><span class="pln">
    cmd</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln">
    err </span><span class="pun">=</span><span class="pln"> cmd</span><span class="pun">.</span><span class="typ">Run</span><span class="pun">()</span><span class="pln">
    </span><span class="com">// Check err</span><span class="pln">


    </span><span class="kwd">return</span><span class="pln"> nil
</span><span class="pun">}</span></pre>

<p>
	يمكننا تهيئة المستودع المحلي لتطبيقنا بالخطوات التالية:
</p>

<ol>
	<li>
		ننشئ مجلدًا للمستودع المحلي local repository في حال عدم وجوده.
	</li>
	<li>
		نستخدم بالأمر <code>git init</code> لإنشاء مستودع فارغ bare repository
	</li>
	<li>
		نضيف رابطًا للمستودع البعيد remote repository إلى مستودعنا المحلي ونسميه <code>origin</code>. بعد تهيئة المستودع سيكون جلب التغييرات عملية بسيطة.
	</li>
</ol>

<h2 id="-4">
	جلب التغييرات
</h2>

<p>
	لجلب التغييرات من المستودع البعيد لن نحتاج سوى لاستدعاء أمر واحد، وذلك وفق ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7582_15" style=""><span class="com">// app.go</span><span class="pln">


func </span><span class="pun">(</span><span class="pln">a </span><span class="pun">*</span><span class="typ">App</span><span class="pun">)</span><span class="pln"> fetchChanges</span><span class="pun">()</span><span class="pln"> error </span><span class="pun">{</span><span class="pln">
    log</span><span class="pun">.</span><span class="typ">Print</span><span class="pun">(</span><span class="str">"Fetching changes"</span><span class="pun">)</span><span class="pln">


    cmd </span><span class="pun">:=</span><span class="pln"> exec</span><span class="pun">.</span><span class="typ">Command</span><span class="pun">(</span><span class="str">"git"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"--git-dir="</span><span class="pun">+</span><span class="pln">a</span><span class="pun">.</span><span class="pln">repoDir</span><span class="pun">,</span><span class="pln"> </span><span class="str">"fetch"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"-f"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"origin"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"master:master"</span><span class="pun">)</span><span class="pln">
    cmd</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> cmd</span><span class="pun">.</span><span class="typ">Run</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إن تنفيذ أمر جلب التغييرات <code>git fetch</code> إلى مستودعنا المحلي بهذه الطريقة يجنبنا مشاكل عجز Git عن التقدم السريع fast-forward في بعض الحالات. وصحيح أننا لا ننصح بالاعتماد على عمليات الجلب الإجبارية، لكن إن احتجنا إلى تنفيذ عملية دفع إجبارية إلى مستودعنا البعيد يمكن اتباع هذه الطريقة.
</p>

<h2 id="-5">
	تصريف التطبيق
</h2>

<p>
	بما أننا نستخدم سكربتات من حزم البناء buildpacks لتصريف compile تطبيقاتنا التي ننشرها، تتمثل مهمتنا بتنفيذ الخطوات البسيطة التالية من أجل تحضير وتصريف التطبيق بشكل آلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7582_17" style=""><span class="com">// app.go</span><span class="pln">


func </span><span class="pun">(</span><span class="pln">a </span><span class="pun">*</span><span class="typ">App</span><span class="pun">)</span><span class="pln"> compileApp</span><span class="pun">()</span><span class="pln"> error </span><span class="pun">{</span><span class="pln">
    log</span><span class="pun">.</span><span class="typ">Print</span><span class="pun">(</span><span class="str">"Compiling application"</span><span class="pun">)</span><span class="pln">


    _</span><span class="pun">,</span><span class="pln"> err </span><span class="pun">:=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">Stat</span><span class="pun">(</span><span class="pln">a</span><span class="pun">.</span><span class="pln">appDir</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">!</span><span class="pln">os</span><span class="pun">.</span><span class="typ">IsNotExist</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        err </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">RemoveAll</span><span class="pun">(</span><span class="pln">a</span><span class="pun">.</span><span class="pln">appDir</span><span class="pun">)</span><span class="pln">
        </span><span class="com">// Check err</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    err </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">MkdirAll</span><span class="pun">(</span><span class="pln">a</span><span class="pun">.</span><span class="pln">appDir</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0755</span><span class="pun">)</span><span class="pln">
    </span><span class="com">// Check err</span><span class="pln">
    cmd </span><span class="pun">:=</span><span class="pln"> exec</span><span class="pun">.</span><span class="typ">Command</span><span class="pun">(</span><span class="str">"git"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"--git-dir="</span><span class="pun">+</span><span class="pln">a</span><span class="pun">.</span><span class="pln">repoDir</span><span class="pun">,</span><span class="pln"> </span><span class="str">"--work-tree="</span><span class="pun">+</span><span class="pln">a</span><span class="pun">.</span><span class="pln">appDir</span><span class="pun">,</span><span class="pln"> </span><span class="str">"checkout"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"-f"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"master"</span><span class="pun">)</span><span class="pln">
    cmd</span><span class="pun">.</span><span class="typ">Dir</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> a</span><span class="pun">.</span><span class="pln">appDir
    cmd</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln">
    err </span><span class="pun">=</span><span class="pln"> cmd</span><span class="pun">.</span><span class="typ">Run</span><span class="pun">()</span><span class="pln">
    </span><span class="com">// Check err</span><span class="pln">


    buildpackDir</span><span class="pun">,</span><span class="pln"> err </span><span class="pun">:=</span><span class="pln"> filepath</span><span class="pun">.</span><span class="typ">Abs</span><span class="pun">(</span><span class="str">"buildpack"</span><span class="pun">)</span><span class="pln">
    </span><span class="com">// Check err</span><span class="pln">


    cmd </span><span class="pun">=</span><span class="pln"> exec</span><span class="pun">.</span><span class="typ">Command</span><span class="pun">(</span><span class="str">"bash"</span><span class="pun">,</span><span class="pln"> filepath</span><span class="pun">.</span><span class="typ">Join</span><span class="pun">(</span><span class="pln">buildpackDir</span><span class="pun">,</span><span class="pln"> </span><span class="str">"bin"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"detect"</span><span class="pun">),</span><span class="pln"> a</span><span class="pun">.</span><span class="pln">appDir</span><span class="pun">)</span><span class="pln">
    cmd</span><span class="pun">.</span><span class="typ">Dir</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> buildpackDir
    cmd</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln">
    err </span><span class="pun">=</span><span class="pln"> cmd</span><span class="pun">.</span><span class="typ">Run</span><span class="pun">()</span><span class="pln">
    </span><span class="com">// Check err</span><span class="pln">


    cacheDir</span><span class="pun">,</span><span class="pln"> err </span><span class="pun">:=</span><span class="pln"> filepath</span><span class="pun">.</span><span class="typ">Abs</span><span class="pun">(</span><span class="str">"cache"</span><span class="pun">)</span><span class="pln">
    </span><span class="com">// Check err</span><span class="pln">
    err </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">MkdirAll</span><span class="pun">(</span><span class="pln">cacheDir</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0755</span><span class="pun">)</span><span class="pln">
    </span><span class="com">// Check err</span><span class="pln">


    cmd </span><span class="pun">=</span><span class="pln"> exec</span><span class="pun">.</span><span class="typ">Command</span><span class="pun">(</span><span class="str">"bash"</span><span class="pun">,</span><span class="pln"> filepath</span><span class="pun">.</span><span class="typ">Join</span><span class="pun">(</span><span class="pln">buildpackDir</span><span class="pun">,</span><span class="pln"> </span><span class="str">"bin"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"compile"</span><span class="pun">),</span><span class="pln"> a</span><span class="pun">.</span><span class="pln">appDir</span><span class="pun">,</span><span class="pln"> cacheDir</span><span class="pun">)</span><span class="pln">
    cmd</span><span class="pun">.</span><span class="typ">Dir</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> a</span><span class="pun">.</span><span class="pln">appDir
    cmd</span><span class="pun">.</span><span class="typ">Stdout</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">Stdout</span><span class="pln">
    cmd</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> cmd</span><span class="pun">.</span><span class="typ">Run</span><span class="pun">()</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنشرح الكود أعلاه خطوة بخطوة:
</p>

<ul>
	<li>
		نبدأ بحذف المجلد السابق الخاص بتطبيقنا في حال وجوده، ثم ننشئ مجلدًا جديدًا ونسحب checkout محتويات الفرع الرئيسي master branch إليه
	</li>
	<li>
		نستخدم بعد ذلك سكريبت detect الموجود ضمن حزمة البناء التي ضبطناها لنحدد إذا كان باستطاعتنا معالجة التطبيق
	</li>
	<li>
		ننشئ مجلد لذاكرة التخزين المؤقتة ونسميه cache من أجل عملية تصريف حزمة البناء إذا دعت الحاجة لذلك، علمًا أن المجلد قد يكون موجودًا مسبقًا في حال أجرينا عمليات تصريف سابقة
	</li>
	<li>
		يمكننا في هذه المرحلة استدعاء سكربت التصريف المسمى compile من حزمة البناء وجعله يجهّز كل ما يحتاجه التطبيق قبل تشغيله عندما تُشغَّل حزم البناء بطريقة صحيحة فستتكمن من التعامل مع التخزين المؤقت caching وإعادة استخدام الموارد مسبقة التخزين
	</li>
</ul>

<h2 id="-6">
	إعادة تشغيل التطبيق
</h2>

<p>
	سنعمل في تطبيق أتمتة النشر الذي نعدّه وفق نهج إيقاف الإجرائيات القديمة قبل تشغيل عملية التصريف، وتشغيل الإجرائيات الجديدة عند اكتمالها، ونجد هذا في الشيفرة التالية. في حال رغبنا بتطوير هذا النموذج الأولي يمكن تعديله بحيث نضمن عدم حدوث أي فترة انقطاع downtime خلال عمليات التحديث.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7582_19" style=""><span class="com">// app.go</span><span class="pln">


func </span><span class="pun">(</span><span class="pln">a </span><span class="pun">*</span><span class="typ">App</span><span class="pun">)</span><span class="pln"> stopProcs</span><span class="pun">()</span><span class="pln"> error </span><span class="pun">{</span><span class="pln">
    log</span><span class="pun">.</span><span class="typ">Print</span><span class="pun">(</span><span class="str">".. stopping processes"</span><span class="pun">)</span><span class="pln">


    </span><span class="kwd">for</span><span class="pln"> _</span><span class="pun">,</span><span class="pln"> n </span><span class="pun">:=</span><span class="pln"> range a</span><span class="pun">.</span><span class="pln">nodes </span><span class="pun">{</span><span class="pln">
        err </span><span class="pun">:=</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">Stop</span><span class="pun">()</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> err </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> err
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">


    </span><span class="kwd">return</span><span class="pln"> nil
</span><span class="pun">}</span><span class="pln">


func </span><span class="pun">(</span><span class="pln">a </span><span class="pun">*</span><span class="typ">App</span><span class="pun">)</span><span class="pln"> startProcs</span><span class="pun">()</span><span class="pln"> error </span><span class="pun">{</span><span class="pln">
    log</span><span class="pun">.</span><span class="typ">Print</span><span class="pun">(</span><span class="str">"Starting processes"</span><span class="pun">)</span><span class="pln">


    err </span><span class="pun">:=</span><span class="pln"> a</span><span class="pun">.</span><span class="pln">readProcfile</span><span class="pun">()</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> err </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> err
    </span><span class="pun">}</span><span class="pln">


    </span><span class="kwd">for</span><span class="pln"> _</span><span class="pun">,</span><span class="pln"> n </span><span class="pun">:=</span><span class="pln"> range a</span><span class="pun">.</span><span class="pln">nodes </span><span class="pun">{</span><span class="pln">
        err </span><span class="pun">=</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">Start</span><span class="pun">()</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> err </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> err
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">


    </span><span class="kwd">return</span><span class="pln"> nil
</span><span class="pun">}</span></pre>

<p>
	في هذا النموذج من برنامجنا نوقف مختلف الإجرائيات ونشغلها بالمرور على مصفوفة من العقد nodes، تكون فيها كل عقدة عبارة عن إجرائية تقابل إحدى نسخ التطبيق وفق الإعدادات المضبوطة قبل تشغيل أداة الأتمتة على الخادم. نراقب في أداتنا الحالة الأساسية لإجرائية كل عقدة، كما نحتفظ بملفات سجل log files عنها. قبل تشغيل جميع العقد يُسنَد إلى كل منها رقم منفذ فريد، بدءًا من رقم نحدده وفق ما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7582_21" style=""><span class="com">// node.go</span><span class="pln">


func </span><span class="typ">NewNode</span><span class="pun">(</span><span class="pln">app </span><span class="pun">*</span><span class="typ">App</span><span class="pun">,</span><span class="pln"> name string</span><span class="pun">,</span><span class="pln"> no </span><span class="typ">int</span><span class="pun">,</span><span class="pln"> port </span><span class="typ">int</span><span class="pun">)</span><span class="pln"> </span><span class="pun">(*</span><span class="typ">Node</span><span class="pun">,</span><span class="pln"> error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    logFile</span><span class="pun">,</span><span class="pln"> err </span><span class="pun">:=</span><span class="pln"> os</span><span class="pun">.</span><span class="typ">OpenFile</span><span class="pun">(</span><span class="pln">filepath</span><span class="pun">.</span><span class="typ">Join</span><span class="pun">(</span><span class="pln">app</span><span class="pun">.</span><span class="pln">logsDir</span><span class="pun">,</span><span class="pln"> fmt</span><span class="pun">.</span><span class="typ">Sprintf</span><span class="pun">(</span><span class="str">"%s.%d.txt"</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> no</span><span class="pun">)),</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">O_RDWR</span><span class="pun">|</span><span class="pln">os</span><span class="pun">.</span><span class="pln">O_CREATE</span><span class="pun">|</span><span class="pln">os</span><span class="pun">.</span><span class="pln">O_APPEND</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0666</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> err </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> nil</span><span class="pun">,</span><span class="pln"> err
    </span><span class="pun">}</span><span class="pln">


    n </span><span class="pun">:=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="typ">Node</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">App</span><span class="pun">:</span><span class="pln">     app</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Name</span><span class="pun">:</span><span class="pln">    name</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">No</span><span class="pun">:</span><span class="pln">      no</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Port</span><span class="pun">:</span><span class="pln">    port</span><span class="pun">,</span><span class="pln">
        stateCh</span><span class="pun">:</span><span class="pln"> make</span><span class="pun">(</span><span class="pln">chan </span><span class="typ">NextState</span><span class="pun">),</span><span class="pln">
        logFile</span><span class="pun">:</span><span class="pln"> logFile</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">


    go func</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            next </span><span class="pun">:=</span><span class="pln"> </span><span class="pun">&lt;-</span><span class="pln">n</span><span class="pun">.</span><span class="pln">stateCh
            </span><span class="kwd">if</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">State</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> next</span><span class="pun">.</span><span class="typ">State</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                </span><span class="kwd">if</span><span class="pln"> next</span><span class="pun">.</span><span class="pln">doneCh </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
                    close</span><span class="pun">(</span><span class="pln">next</span><span class="pun">.</span><span class="pln">doneCh</span><span class="pun">)</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
                </span><span class="kwd">continue</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">


            </span><span class="kwd">switch</span><span class="pln"> next</span><span class="pun">.</span><span class="typ">State</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="kwd">case</span><span class="pln"> </span><span class="typ">StateUp</span><span class="pun">:</span><span class="pln">
                log</span><span class="pun">.</span><span class="typ">Printf</span><span class="pun">(</span><span class="str">"Starting process %s.%d"</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">Name</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">No</span><span class="pun">)</span><span class="pln">


                cmd </span><span class="pun">:=</span><span class="pln"> exec</span><span class="pun">.</span><span class="typ">Command</span><span class="pun">(</span><span class="str">"bash"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"-c"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"for f in .profile.d/*; do source $f; done; "</span><span class="pun">+</span><span class="pln">n</span><span class="pun">.</span><span class="typ">Cmd</span><span class="pun">)</span><span class="pln">
                cmd</span><span class="pun">.</span><span class="typ">Env</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> append</span><span class="pun">(</span><span class="pln">cmd</span><span class="pun">.</span><span class="typ">Env</span><span class="pun">,</span><span class="pln"> fmt</span><span class="pun">.</span><span class="typ">Sprintf</span><span class="pun">(</span><span class="str">"HOME=%s"</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">App</span><span class="pun">.</span><span class="pln">appDir</span><span class="pun">))</span><span class="pln">
                cmd</span><span class="pun">.</span><span class="typ">Env</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> append</span><span class="pun">(</span><span class="pln">cmd</span><span class="pun">.</span><span class="typ">Env</span><span class="pun">,</span><span class="pln"> fmt</span><span class="pun">.</span><span class="typ">Sprintf</span><span class="pun">(</span><span class="str">"PORT=%d"</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">Port</span><span class="pun">))</span><span class="pln">
                cmd</span><span class="pun">.</span><span class="typ">Env</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> append</span><span class="pun">(</span><span class="pln">cmd</span><span class="pun">.</span><span class="typ">Env</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">App</span><span class="pun">.</span><span class="typ">Env</span><span class="pun">...)</span><span class="pln">
                cmd</span><span class="pun">.</span><span class="typ">Dir</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">App</span><span class="pun">.</span><span class="pln">appDir
                cmd</span><span class="pun">.</span><span class="typ">Stdout</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> n</span><span class="pun">.</span><span class="pln">logFile
                cmd</span><span class="pun">.</span><span class="typ">Stderr</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> n</span><span class="pun">.</span><span class="pln">logFile
                err </span><span class="pun">:=</span><span class="pln"> cmd</span><span class="pun">.</span><span class="typ">Start</span><span class="pun">()</span><span class="pln">
                </span><span class="kwd">if</span><span class="pln"> err </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
                    log</span><span class="pun">.</span><span class="typ">Printf</span><span class="pun">(</span><span class="str">"Process %s.%d exited"</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">Name</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">No</span><span class="pun">)</span><span class="pln">
                    n</span><span class="pun">.</span><span class="typ">State</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StateUp</span><span class="pln">


                </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    n</span><span class="pun">.</span><span class="typ">Process</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> cmd</span><span class="pun">.</span><span class="typ">Process</span><span class="pln">
                    n</span><span class="pun">.</span><span class="typ">State</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StateUp</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">


                </span><span class="kwd">if</span><span class="pln"> next</span><span class="pun">.</span><span class="pln">doneCh </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
                    close</span><span class="pun">(</span><span class="pln">next</span><span class="pun">.</span><span class="pln">doneCh</span><span class="pun">)</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">


                go func</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                    err </span><span class="pun">:=</span><span class="pln"> cmd</span><span class="pun">.</span><span class="typ">Wait</span><span class="pun">()</span><span class="pln">
                    </span><span class="kwd">if</span><span class="pln"> err </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
                        log</span><span class="pun">.</span><span class="typ">Printf</span><span class="pun">(</span><span class="str">"Process %s.%d exited"</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">Name</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">No</span><span class="pun">)</span><span class="pln">
                        n</span><span class="pun">.</span><span class="pln">stateCh </span><span class="pun">&lt;-</span><span class="pln"> </span><span class="typ">NextState</span><span class="pun">{</span><span class="pln">
                            </span><span class="typ">State</span><span class="pun">:</span><span class="pln"> </span><span class="typ">StateDown</span><span class="pun">,</span><span class="pln">
                        </span><span class="pun">}</span><span class="pln">
                    </span><span class="pun">}</span><span class="pln">
                </span><span class="pun">}()</span><span class="pln">


            </span><span class="kwd">case</span><span class="pln"> </span><span class="typ">StateDown</span><span class="pun">:</span><span class="pln">
                log</span><span class="pun">.</span><span class="typ">Printf</span><span class="pun">(</span><span class="str">"Stopping process %s.%d"</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">Name</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">No</span><span class="pun">)</span><span class="pln">


                </span><span class="kwd">if</span><span class="pln"> n</span><span class="pun">.</span><span class="typ">Process</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
                    n</span><span class="pun">.</span><span class="typ">Process</span><span class="pun">.</span><span class="typ">Kill</span><span class="pun">()</span><span class="pln">
                    n</span><span class="pun">.</span><span class="typ">Process</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> nil
                </span><span class="pun">}</span><span class="pln">


                n</span><span class="pun">.</span><span class="typ">State</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StateDown</span><span class="pln">
                </span><span class="kwd">if</span><span class="pln"> next</span><span class="pun">.</span><span class="pln">doneCh </span><span class="pun">!=</span><span class="pln"> nil </span><span class="pun">{</span><span class="pln">
                    close</span><span class="pun">(</span><span class="pln">next</span><span class="pun">.</span><span class="pln">doneCh</span><span class="pun">)</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}()</span><span class="pln">


    </span><span class="kwd">return</span><span class="pln"> n</span><span class="pun">,</span><span class="pln"> nil
</span><span class="pun">}</span><span class="pln">


func </span><span class="pun">(</span><span class="pln">n </span><span class="pun">*</span><span class="typ">Node</span><span class="pun">)</span><span class="pln"> </span><span class="typ">Start</span><span class="pun">()</span><span class="pln"> error </span><span class="pun">{</span><span class="pln">
    n</span><span class="pun">.</span><span class="pln">stateCh </span><span class="pun">&lt;-</span><span class="pln"> </span><span class="typ">NextState</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">State</span><span class="pun">:</span><span class="pln"> </span><span class="typ">StateUp</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> nil
</span><span class="pun">}</span><span class="pln">


func </span><span class="pun">(</span><span class="pln">n </span><span class="pun">*</span><span class="typ">Node</span><span class="pun">)</span><span class="pln"> </span><span class="typ">Stop</span><span class="pun">()</span><span class="pln"> error </span><span class="pun">{</span><span class="pln">
    doneCh </span><span class="pun">:=</span><span class="pln"> make</span><span class="pun">(</span><span class="pln">chan </span><span class="typ">int</span><span class="pun">)</span><span class="pln">
    n</span><span class="pun">.</span><span class="pln">stateCh </span><span class="pun">&lt;-</span><span class="pln"> </span><span class="typ">NextState</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">State</span><span class="pun">:</span><span class="pln">  </span><span class="typ">StateDown</span><span class="pun">,</span><span class="pln">
        doneCh</span><span class="pun">:</span><span class="pln"> doneCh</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">&lt;-</span><span class="pln">doneCh
    </span><span class="kwd">return</span><span class="pln"> nil
</span><span class="pun">}</span></pre>

<p>
	قد تبدو كتابة هذه الشيفرة البرمجية للوهلة الأولى أكثر الخطوات تعقيدًا حتى الآن، لذا دعنا نبسطها بتقسيمها إلى 4 أجزاء: يمثل مضمون دالة <code>NewNode</code> أول جزئين، فعندما تُستدعَى هذه الدالة تهيئ populate نسخة من هيكل بيانات <code>Node</code> بإعدادات محددة وتولّد برنامجًا routine بلغة غو Go مهمته إيقاف الإجرائيات المقابلة لهذه العقدة وتشغيلها. 
</p>

<p>
	يمثل التابعان <code>Start</code> و<code>Stop</code> في بنية <code>Node</code> الجزئين التاليين، حيث توقَف الإجرائية أو تشغَّل بتمرير رسالة <code>message</code> خلال قناة معينة تفيد بأن برنامج غو هذا الذي ينفَّذ على كل عقدة على حدة يستمر في المراقبة. يمكن تمرير رسالة لتشغيل إجرائية ورسالة مختلفة لإيقافها.
</p>

<p>
	بما أن الخطوات الفعلية لإيقاف أو تشغيل إجرائية تحدث ضمن برنامج Go routine، فلا مجال لحدوث حالات تسابق race conditions بسبب الطريقة التي تتم بها إدارة تشغيل الأكواد بشكل متوازي يبدأ برنامج غو Go routine حلقة لا نهائية infinite loop تبقى بانتظار رسالة <code>message</code> في قناة <code>stateCh</code>، فإذا مرت الرسالة في القناة يُرسَل طلب إلى العقدة node بتشغيل الإجرائية ضمن <code>case StateUp</code>، ونستخدم برنامج باش Bash لتنفيذ هذا الأمر. 
</p>

<p>
	أثناء هذه العملية تُضبط العقدة الأمر بحيث يستخدم متغيرات البيئة environment variables التي حددها المستخدم، ويعيد توجيه الخرج القياسي standard output ومجاري الخطأ error streams إلى ملف سجل log file سبق تحديده. أما لإيقاف الإجرائية ضمن <code>case StateDown</code> فإن البرنامج يوقفها قسريًا باستخدام أمر <code>kill</code>. في حال رغبنا بإيقاف الإجرائية تدريجيًا يمكننا جعله يرسل إشارة SIGTERM والانتظار بضع ثوان قبل إيقافها. يسهّل تابعا <code>Start</code> و <code>Stop</code> تمرير الرسالة المناسبة إلى القناة، إذ يرسل التابع <code>Start</code> رسالة إلى القناة بتشغيل الإجرائية ويعود، في حين ينتظر تابع <code>Stop</code> أن تتوقف الإجرائية قبل أن يعود.
</p>

<h2 id="-7">
	جمع الخطوات السابقة
</h2>

<p>
	لم يتبقَ لنا الآن سوى إضافة جميع الشيفرات السابقة إلى الدالة الرئيسية لبرنامجنا، حيث سيُنفَّذ تحميل ملف الإعدادت وتحليله parse، وتحديث حزم البناء، ومحاولة تحديث تطبيقنا لمرة واحدة، وتشغيل خادم الويب ليصغي إلى حمولات حدث الدفع push event التي ترد من غيت هب.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7582_23" style=""><span class="com">// main.go</span><span class="pln">


func main</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    cfg</span><span class="pun">,</span><span class="pln"> err </span><span class="pun">:=</span><span class="pln"> toml</span><span class="pun">.</span><span class="typ">LoadFile</span><span class="pun">(</span><span class="str">"config.tml"</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">


    url</span><span class="pun">,</span><span class="pln"> ok </span><span class="pun">:=</span><span class="pln"> cfg</span><span class="pun">.</span><span class="typ">Get</span><span class="pun">(</span><span class="str">"buildpack.url"</span><span class="pun">).(</span><span class="pln">string</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">!</span><span class="pln">ok </span><span class="pun">{</span><span class="pln">
        log</span><span class="pun">.</span><span class="typ">Fatal</span><span class="pun">(</span><span class="str">"buildpack.url not defined"</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    err </span><span class="pun">=</span><span class="pln"> </span><span class="typ">UpdateBuildpack</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">


    </span><span class="com">// Read configuration options into variables repo (string), env ([]string) and procs (map[string]int)</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">


    app</span><span class="pun">,</span><span class="pln"> err </span><span class="pun">:=</span><span class="pln"> </span><span class="typ">NewApp</span><span class="pun">(</span><span class="pln">repo</span><span class="pun">,</span><span class="pln"> env</span><span class="pun">,</span><span class="pln"> procs</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">


    err </span><span class="pun">=</span><span class="pln"> app</span><span class="pun">.</span><span class="typ">Update</span><span class="pun">()</span><span class="pln">
    </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">


    secret</span><span class="pun">,</span><span class="pln"> _ </span><span class="pun">:=</span><span class="pln"> cfg</span><span class="pun">.</span><span class="typ">Get</span><span class="pun">(</span><span class="str">"hook.secret"</span><span class="pun">).(</span><span class="pln">string</span><span class="pun">)</span><span class="pln">


    http</span><span class="pun">.</span><span class="typ">Handle</span><span class="pun">(</span><span class="str">"/hook"</span><span class="pun">,</span><span class="pln"> </span><span class="typ">NewHookHandler</span><span class="pun">(&amp;</span><span class="typ">HookOptions</span><span class="pun">{</span><span class="pln">
        </span><span class="typ">App</span><span class="pun">:</span><span class="pln">    app</span><span class="pun">,</span><span class="pln">
        </span><span class="typ">Secret</span><span class="pun">:</span><span class="pln"> secret</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">}))</span><span class="pln">


    addr</span><span class="pun">,</span><span class="pln"> ok </span><span class="pun">:=</span><span class="pln"> cfg</span><span class="pun">.</span><span class="typ">Get</span><span class="pun">(</span><span class="str">"core.addr"</span><span class="pun">).(</span><span class="pln">string</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">!</span><span class="pln">ok </span><span class="pun">{</span><span class="pln">
        log</span><span class="pun">.</span><span class="typ">Fatal</span><span class="pun">(</span><span class="str">"core.addr not defined"</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">


    err </span><span class="pun">=</span><span class="pln"> http</span><span class="pun">.</span><span class="typ">ListenAndServe</span><span class="pun">(</span><span class="pln">addr</span><span class="pun">,</span><span class="pln"> nil</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	بما أن أداتنا تتطلب أن تكون حزم البناء مستودعات غيت بسيطة، فلن تنفذ دالة <code>UpdateBuildpack</code> المتضمنة في شيفرة <a href="https://github.com/hjr265/toptal-hopper/blob/master/buildpack.go" rel="external nofollow">buildpack.go</a> سوى عمليتي نسخ <code>git clone</code> وسحب <code>git pull</code> لرابط المستودع عند الضرورة وذلك لتحديث النسخة المحلية من حزمة البناء.
</p>

<h2 id="-8">
	تجربة الأداة
</h2>

<p>
	في حال لم نستنسخ <a href="https://github.com/hjr265/toptal-hopper" rel="external nofollow">المستودع</a> بعد فيمكن استنساخه الآن. وإذا كان بGo distribution مثبتًا فيمكن البدء بتصريف compile البرنامج.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7582_25" style=""><span class="pln">mkdir hopper
cd hopper
</span><span class="kwd">export</span><span class="pln"> GOPATH</span><span class="pun">=`</span><span class="pln">pwd</span><span class="pun">`</span><span class="pln">
go get github</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="pln">hjr265</span><span class="pun">/</span><span class="pln">toptal</span><span class="pun">-</span><span class="pln">hopper
go install github</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="pln">hjr265</span><span class="pun">/</span><span class="pln">toptal</span><span class="pun">-</span><span class="pln">hopper</span></pre>

<p>
	لنطلع على ما تنفذه هذه الأوامر على التوالي:
</p>

<ul>
	<li>
		إنشاء مجلد وتسميته hopper
	</li>
	<li>
		الانتقال إليه
	</li>
	<li>
		جعله المجلد الخاص بمساحة العمل GOPATH
	</li>
	<li>
		جلب الشيفرة من غيت هب إلى جانب مكتبات غو Go الضرورية، وتصريف البرنامج لشيفرة binary يمكن إيجادها في مجلد <code>‎$GOPATH/bin</code>
	</li>
</ul>

<p>
	قبل استخدام هذه الأداة على خادم علينا أن ننشئ تطبيق ويب بسيط لنختبر هذه الأداة عليه. في حال رغبنا بالحصول على تطبيق جاهز قد أنشأ صاحب المقال تطبيق ويب بسيط يعرض عبارة "Hello World" بلغة Node.js ورفعه إلى <a href="https://github.com/hjr265/hopper-hello.js" rel="external nofollow">مستودع غيت هب</a> آخر يمكننا اشتقاقه fork وإعادة استخدامه لاختبار الأداة. علينا في الخطوة التالية رفع الشيفرة الثنائية المصرَّفة compiled binary إلى خادم وإنشاء ملف إعدادات في المجلد ذاته:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_7582_27" style=""><span class="com"># config.tml </span><span class="pln">


</span><span class="pun">[</span><span class="pln">core</span><span class="pun">]</span><span class="pln">
addr </span><span class="pun">=</span><span class="pln"> </span><span class="str">":26590"</span><span class="pln">
</span><span class="pun">[</span><span class="pln">buildpack</span><span class="pun">]</span><span class="pln">
url </span><span class="pun">=</span><span class="pln"> </span><span class="str">"https://github.com/heroku/heroku-buildpack-nodejs.git"</span><span class="pln">


</span><span class="pun">[</span><span class="pln">app</span><span class="pun">]</span><span class="pln">
repo </span><span class="pun">=</span><span class="pln"> </span><span class="str">"hjr265/hopper-hello.js"</span><span class="pln">


    </span><span class="pun">[</span><span class="pln">app</span><span class="pun">.</span><span class="pln">env</span><span class="pun">]</span><span class="pln">
    GREETING </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Hello"</span><span class="pln">


    </span><span class="pun">[</span><span class="pln">app</span><span class="pun">.</span><span class="pln">procs</span><span class="pun">]</span><span class="pln">
    web </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">


</span><span class="pun">[</span><span class="pln">hook</span><span class="pun">]</span><span class="pln">
secret </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span></pre>

<p>
	لنبدأ بأول خيار في ملف الإعدادات لدينا وهو <code>core.addr</code> الذي يتيح لنا ضبط منفذ HTTP لخادم الويب الداخلي لبرنامجنا، وقد اخترناه في مثالنا هذا ليكون <code>26590:</code> وهذا يعني أن برنامجنا سيصغي إلى حمولات حدث الدفع عن طريق الرابط <code>http://{host}:26590/hook</code>.
</p>

<p>
	عندما نضبط خطاف الويب في غيت هب ما عليك سوى تبديل <code>{host}</code> أي المضيف باسم النطاق أو عنوان IP الذي يشير إلى خادمنا. وفي حال كنا نستخدم جدار حماية على خادمنا لا ننسى أن فتح هذا المنفذ عليه. ثم نحدد حزمة البناء بإضافة رابط غيت الذي يشير إليها، ونستخدم في مثالنا <a href="https://github.com/heroku/heroku-buildpack-nodejs" rel="external nofollow">حزمة بناء بلغة Node.js الخاصة بهيروكو Heroku</a>.
</p>

<p>
	أما خيار <code>app</code> فقد حددنا ضمنه في خيار <code>repo</code> الاسم الكامل لمستودع غيت هب الذي يستضيف شيفرة التطبيق البرمجية، وبما أن رابط استضافة شيفرة التطبيق المستخدم في مثالنا <code><a href="https://github.com/hjr265/hopper-hello.js" ipsnoembed="true" rel="external nofollow">https://github.com/hjr265/hopper-hello.js</a></code> حددنا الاسم الكامل للمستودع <code>hjr265/hopper-hello.js</code>، ثم حددنا بعض متغيرات البيئة الخاصة بالتطبيق وعدد كل نوع من الإجرائيات التي نحتاجها، وأنهينا الملف باختيار رمز سري لنتحقق من حمولات حدث الدفع الواردة.
</p>

<p>
	يمكننا الآن تشغيل برنامج الأتمتة الذي بنيناه على الخادم. في حال كانت جميع إعداداتنا صحيحة إضافة إلى ضبط مفاتيح <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> Keys حتى يُسمح للخادم بالوصول إلى المستودع، سينجح البرنامج في جلب الشيفرة البرمجية وتجهيز البيئة باستخدام حزمة البناء وتشغيل التطبيق.
</p>

<p>
	ما علينا الآن سوى إعداد خطاف ويب في مستودع غيت هب لتصدير أحداث الدفع وتوجيهها إلى الرابط <code>http://{host}:26590/hook</code>. ولا ننسى بالطبع استبدال <code>{host}</code> باسم النطاق أو عنوان IP الذي يشير إلى الخادم. لنختبر عمل الأداة التي بنيناها، نحاول إجراء بعض التغييرات على التطبيق وندفعها إلى غيت هب، ستلاحظ حينها أن أداة الأتمتة ستبدأ العمل فورًا وتٌحدّث المستودع على الخادم وتصّرف التطبيق ثم تعيد تشغيله.
</p>

<h2 id="-9">
	الخاتمة
</h2>

<p>
	نأمل أن تساعدكم هذه المقالة في بناء أداة أتمتة نشر تطبيقات الويب يمكن تعديلها وفق متطلبات العمل. بالطبع لا ننصح بتطبيقها بشكلها الحالي في بيئة تشغيلية دون تطويرها وتحسينها، إذ يمكن مثلًا تحسين تعاملها مع الأخطاء، وجعل الأداة تدعم عمليات الإيقاف وإعادة التشغيل التدريجية، واستخدام <a href="https://academy.hsoub.com/devops/cloud-computing/docker/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AF%D9%88%D9%83%D8%B1-docker-r607/" rel="">دوكر Docker</a> لاحتواء الإجرائيات عوضًا عن تشغيلها مباشرة. أما في حال لم يتطلب عملنا تصميم أداة مخصصة لتنفيذ متطلبات معينة فيمكن دومًا استخدام أحد الحلول الجاهزة الموجودة على الإنترنت والتي تكون مجرَّبة ومستقرة.
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a href="https://www.toptal.com/devops/deploy-web-applications-automatically-using-github-webhooks" rel="external nofollow">Deploy Web Applications Automatically Using GitHub Webhooks </a> لصاحبه Mahmud Ridwan.
</p>

<h2 id="-10">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/go/%D9%83%D9%8A%D9%81-%D8%AA%D9%86%D8%B4%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%88%D9%8A%D8%A8-%D8%A8%D9%84%D8%BA%D8%A9-go-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AE%D8%A7%D8%AF%D9%85-nginx-%D8%B9%D9%84%D9%89-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-1804-r747/" rel="">كيف تنشر تطبيق ويب بلغة Go باستخدام خادم Nginx على أوبنتو 18.04</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D9%86%D8%B4%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D9%85%D9%88%D8%AC%D9%87%D8%A9-%D9%84%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%A7%D8%AC-r132/" rel="">نشر تطبيقات الويب الموجهة لبيئة الإنتاج</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%86%D8%B4%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-nodejs-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%AE%D8%AF%D9%85%D8%A9-%D9%87%D9%8A%D8%B1%D9%88%D9%83%D9%88-heroku-%D9%85%D8%AB%D8%A7%D9%84%D9%8B%D8%A7-r1100/" rel="">نشر تطبيق Node.js على الويب: خدمة هيروكو (Heroku) مثالًا</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/cloud-computing/%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D8%A3%D8%AF%D8%A7%D8%AA%D9%8A-%D8%AF%D9%88%D9%83%D8%B1-docker-%D9%88%D8%A8%D9%88%D8%AF%D9%85%D8%A7%D9%86-podman-r816/" rel="">الفرق بين أداتي دوكر Docker وبودمان Podman</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">836</guid><pubDate>Sat, 08 Feb 2025 15:03:01 +0000</pubDate></item><item><title>&#x627;&#x644;&#x641;&#x648;&#x627;&#x626;&#x62F; &#x627;&#x644;&#x639;&#x634;&#x631; &#x644;&#x646;&#x638;&#x627;&#x645; &#x62A;&#x634;&#x63A;&#x64A;&#x644; &#x644;&#x64A;&#x646;&#x643;&#x633; &#x641;&#x64A; &#x62A;&#x637;&#x648;&#x64A;&#x631; &#x627;&#x644;&#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A;</title><link>https://academy.hsoub.com/devops/general/%D8%A7%D9%84%D9%81%D9%88%D8%A7%D8%A6%D8%AF-%D8%A7%D9%84%D8%B9%D8%B4%D8%B1-%D9%84%D9%86%D8%B8%D8%A7%D9%85-%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D9%81%D9%8A-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-r835/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_02/-------.png.8b6a97bf768ba623b26ae56bb7cc267e.png" /></p>
<p>
	ظهر نظام تشغيل لينكس عام 1991، ومنذ ذلك الحين اكتسب شهرة واسعة وزاد استخدامه بين مطوري البرمجيات، خاصة في مجال تطوير التطبيقات. وسنستعرض في هذا المقال عشرة من أهم مزايا لينكس التي تجعل منه أداة أساسية في بيئة العمل لتطوير التطبيقات الناجحة.
</p>

<h2 id="-1">
	ما هو نظام تشغيل لينكس؟
</h2>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="https://academy.hsoub.com/uploads/monthly_2025_02/-------.png.8a936896c6d7cba96f3c06d225746248.png" data-fileid="166512" data-fileext="png" rel=""><img alt="ما هو نظام تشغيل لينكس" class="ipsImage ipsImage_thumbnailed" data-fileid="166512" data-ratio="62.50" data-unique="gcu8dbufn" style="width: 400px; height: auto;" width="900" src="https://academy.hsoub.com/uploads/monthly_2025_02/-------.thumb.png.4a1486a92992da98e0a67774dc1cd683.png"></a>
</p>

<p>
	يعد <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r799/" rel="">لينكس</a> نظام تشغيل <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوح المصدر</a> طوره المبرمج الأمريكي لينوس بنديكت تورڤالدس Linus Benedict Torvalds ليكون شبيهًا لنظام يونكس UNIX-Like و<a href="https://academy.hsoub.com/devops/linux/%D8%B9%D8%B1%D8%B6-%D9%85%D9%88%D8%AC%D8%B2-%D9%84%D8%A3%D8%B4%D9%87%D8%B1-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A7%D8%AA-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r631/" rel="">متعدد التوزيعات</a>، فمن أشهر توزيعاته دبيان Debian، وأوبونتو Ubuntu، وفيدورا Fedora، كما تتوفر توزيعات لينكس مدفوعة أيضًا وأشهرها ريد هات إنتربرايز لينكس Red Hat Enterprise Linux وسينت أو إس CentOS.
</p>

<p>
	وكذلك يعد لينكس <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%B9%D9%85%D9%84%D9%87%D8%A7-r2260/" rel="">نظام تشغيل</a> متعدد المنصات، إذ يمكن تشغيل توزيعاته المختلفة على أجهزة متعددة دون حدوث أعطال أو مشكلات، مما يجعله خيارًا مميزًا لمطوري البرمجيات المدمجة Embedded Software، سواءً كانت هذه البرمجيات المدمجة موجهة لأجهزة الحاسوب الشخصية أو حتى موجهة إلى خوادم وأجهزة فائقة القدرات.
</p>

<h2 id="-2">
	لماذا نستخدم لينكس عند تطوير التطبيقات؟
</h2>

<p>
	يعود تفضيل نظام التشغيل لينكس عند تطوير التطبيقات إلى كثير من مميزات نظام التشغيل، لنستكشف أبرزها معًا.
</p>

<h3 id="1">
	1. لينكس مجاني ومفتوح المصدر
</h3>

<p>
	يتميز لينكس Linux بكونه نظام تشغيل مجاني بالكامل فلسنا بحاجة لدفع أية مبالغ مالية لتثبيته واستخدامه، مما يجعله مناسبًا لتنفيذ مشاريع <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA/" rel="">تطوير التطبيقات</a> ذات الميزانية المحدودة، كما أنه مفتوح المصدر، ما يعني أن شيفرات نظام التشغيل متوفرة للاطلاع عليها وتعديلها، وبالتالي يمكننا تعديل نظام التشغيل وإضافة المميزات إليه وتخصيصه حسب احتياجاتنا المختلفة.
</p>

<h3 id="2">
	2. يدعم لينكس قابلية التوسع
</h3>

<p>
	يدعم نظام تشغيل لينكس تطوير وتشغيل التطبيقات مختلفة الأحجام؛ سواءً كانت تطبيقات ضخمة وكثيرة المستخدمين أو كانت صغيرة وقليلة المستخدمين، ما يجعله مناسبًا لتشغيل البيئات الافتراضية للتطوير والإنتاج، وكذلك لا يحتاج نظام تشغيل لينكس موارد ضخمة، مما يوفر علينا كثيرًا من أموال شراء العتاد والخوادم.
</p>

<h3 id="3">
	3. لينكس نظام تشغيل مستقر
</h3>

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

<h3 id="4">
	4. يتميز لينكس بالأمان الشديد
</h3>

<p>
	يجب أن يكون الأمان على رأس أولويات كل فريق تطوير تطبيقات، ويُعرف لينكس بأنه واحد من أكثر أنظمة التشغيل أمانًا، ما يضمن لنا أمان تطبيقاتنا وبياناتنا، علاوةً على ذلك يمكننا تحسين أمانه باستخدام <a href="https://academy.hsoub.com/apps/web/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B6%D8%A7%D9%81%D8%A7%D8%AA-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D8%A3%D9%81%D8%B6%D9%84-%D8%A7%D8%B3%D8%AA%D8%B6%D8%A7%D9%81%D8%A9-%D9%84%D9%85%D9%88%D9%82%D8%B9%D9%83-%D8%A3%D9%88-%D9%85%D8%AA%D8%AC%D8%B1%D9%83-%D8%A7%D9%84%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A-r392/" rel="">خادم خاص افتراضي Virtual Private Server (VPS)</a>، ما يعزل التطبيق والبيانات الخاصة بنا عن تطبيقات وبيانات سائر المستخدمين، فإن كان لدينا مشكلة أمنية في تطبيقنا فلن تؤثر على الآخرين، وكذلك إن كان لدى الآخرين مشكلات أمنية في تطبيقاتهم فلن تؤثر على أمان تطبيقنا وبياناتنا.
</p>

<h3 id="5">
	5. تتعدد توزيعات نظام تشغيل لينكس
</h3>

<p>
	تتوفر <a href="https://academy.hsoub.com/devops/linux/%D8%B9%D8%B1%D8%B6-%D9%85%D9%88%D8%AC%D8%B2-%D9%84%D8%A3%D8%B4%D9%87%D8%B1-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A7%D8%AA-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r631/" rel="">توزيعات عديدة</a> ومتنوعة من نظام تشغيل لينكس Linux، فسنجد حتمًا التوزيعة المناسبة لاحتياجات مشروعنا، فمثلًا تتناسب توزيعة أوبونتو Ubuntu مع المطورين المبتدئين، وذلك لتوافر واجهة رسومية سهلة الاستخدام فيها، بينما قد تلائم توزيعة دبيان Debian الفئة الخبيرة من المطورين نظرًا لتعدد مميزاتها و<a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AE%D8%A7%D8%B1%D8%AC%D9%8A%D8%A9-%D8%B6%D9%85%D9%86-%D8%A3%D8%A8%D9%88%D9%86%D8%AA%D9%88-r795/" rel="">حزم البرمجيات</a> التي تدعمها التوزيعة.
</p>

<h3 id="6">
	6. يدعم لينكس مختلف أنواع التطبيقات
</h3>

<p>
	مهما كان نوع التطبيق الذي نود تطويره سواء تطبيق ويب، أو تطبيق هاتف محمول، أو تطبيق سطح مكتب، سنجد حتمًا حزمة البرمجيات المستخدمة في تطويره مدعومة من نظام تشغيل لينكس، بل يمكننا حتى تثبيت أدوات التطوير الضرورية عبر<a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%85%D8%B3%D8%AA%D9%88%D8%AF%D8%B9%D8%A7%D8%AA-%D8%A3%D9%88%D8%A8%D9%86%D8%AA%D9%88-r629/" rel=""> مستودعات لينكس Linux Repositories</a> دون الحاجة للبحث الطويل عبر الإنترنت.
</p>

<h3 id="7">
	7. يدعم لينكس مجموعة واسعة من لغات البرمجة
</h3>

<p>
	يدعم نظام لينكس جميع <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a> المستخدمة في تطوير البرمجيات تقريبًا، مثل: لغة <a href="https://academy.hsoub.com/python/" rel="">بايثون</a> ولغة <a href="https://academy.hsoub.com/programming/java/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-java-r2275/" rel="">جافا</a> ولغة <a href="https://academy.hsoub.com/programming/cpp/" rel="">++C</a>، كما يدعم أكثر من <a href="https://academy.hsoub.com/programming/c/%D8%A7%D9%84%D9%81%D8%B5%D9%84-%D8%A7%D9%84%D8%A3%D9%88%D9%84-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AA%D8%B5%D8%B1%D9%8A%D9%81-compilation-%D9%81%D9%8A-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r976/" rel="">مصرف Compiler</a> لكل لغة، ما يعطينا مرونة في اختيار لغة البرمجة المفضلة لنا لتطوير التطبيقات.
</p>

<h3 id="8">
	8. يحظى لينكس بشهرة كبيرة ومجتمع واسع
</h3>

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

<h3 id="9">
	9. يدعم لينكس مختلف محررات الأكواد
</h3>

<p>
	أيًا كانت تفضيلاتنا الشخصية فلا بد أن نجد محرر أكواد مناسبًا لنا ومدعومًا من نظام تشغيل لينكس، فمثلًا إن أردنا محرر نصوص بسيطًا وسهل الاستخدام فيمكن استخدام محرر Gedit، وفي حال كنا نفضل استخدام <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A8%D9%8A%D8%A6%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D9%83%D8%A7%D9%85%D9%84%D8%A9-ide-r1513/" rel="">بيئة تطوير متكاملة</a> فيمكننا الاختيار بين مجموعة واسعة من بيئات التطوير مثل NetBeans أو Eclipse أو IntelliJ IDEA.
</p>

<h3 id="10">
	10. يتميز لينكس بالمرونة
</h3>

<p>
	يعد لينكس Linux نظام تشغيل مرن، إذ يمكن لفريق العمل تعديله وتخصيصه تبعًا لاحتياجاته الشخصية ومتطلبات التطبيقات دون مواجهة أية عقبات، فمثلًا يسمح لنا نظام تشغيل لينكس بحذف حزم البرمجيات التي لا نحتاجها في عملنا، كما يمكننا تغيير التوزيعة المستخدمة إن احتجنا لذلك.
</p>

<h2 id="-3">
	أمثلة على تطبيقات ناجحة تستخدم لينكس
</h2>

<p>
	قد تراود البعض منا شكوك حول نظام تشغيل لينكس وكفاءته، لكونه لا يحظى بشعارات برَّاقة، ولكنَّ لينكس قد أثبت كفاءته واعتماديته عبر السنين، وهناك تطبيقات شهيرة ومميزة استخدم مطوروها نظام تشغيل لينكس لتطويرها وتشغيلها، نذكر لكم منها:
</p>

<h3>
	سكايب Skype
</h3>

<p>
	قد يستخدم معظمنا اليوم ديسكورد أو زوم في التواصل مع الأصدقاء أو الزملاء، لكن سكايب كان التطبيق الأكثر شيوعًا لهذه المهمة في وقت ما، وقد استُخدِم لينكس في تطوير هذا البرنامج الكلاسيكي.
</p>

<h3>
	ستيم Steam
</h3>

<p>
	يعد ستيم أكبر متجر إلكتروني لبيع الألعاب الرقمية، وما كان هذا المتجر الإلكتروني المميز ليبرز لولا نظام تشغيل لينكس.
</p>

<h3>
	دروب بوكس Dropbox
</h3>

<p>
	لا بد أن أي شخص يستخدم منصات التخزين السحابي لتخزين ملفاته يعرف جيدًا برنامج دروب بوكس ويعرف مكانته وأصالته بين سائر منصات التخزين السحابي؛ وقد استُخدِم نظام تشغيل لينكس عند تطوير دروب بوكس أيضًا.
</p>

<h2 id="-4">
	الخاتمة
</h2>

<p>
	يمكن أن نخلص لأن نظام لينكس نظام تشغيل قوي ومتعدد المميزات، ويحظى بمجتمع واسع يضم كثيرًا من المبرمجين الخبراء الذين سيساعدوننا في تطوير تطبيقاتنا وحل مشكلاتنا في التعامل معه، فضلًا عن كونه مناسبًا للخبراء من المبرمجين والمبتدئين منهم على حد سواء، وهو نظام تشغيل مجاني تمامًا، وسيساعدنا على تطوير تطبيقنا حتى لو كانت ميزانيتنا محدودة، وبالتالي يمكن القول أنه الخيار الأمثل <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-r1827/" rel="">لتطوير التطبيقات</a> على اختلاف أنواعها.
</p>

<p>
	ترجمة -وبتصرف- لمقال <a href="https://www.unixmen.com/top-10-benefits-of-linux-for-app-development/" rel="external nofollow">Top 10 Benefits of Linux for App Development</a> لكاتبه Janus Atienza
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-linux-%D9%88%D8%A3%D8%A8%D8%B1%D8%B2-%D9%85%D9%85%D9%8A%D8%B2%D8%A7%D8%AA%D9%87-%D9%88%D8%B9%D9%8A%D9%88%D8%A8%D9%87-r2252/" rel="">تعرف على نظام لينكس Linux وأبرز مميزاته وعيوبه</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81-%D8%AA%D8%AE%D8%AA%D8%A7%D8%B1-%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A9-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%A7%D9%84%D9%85%D9%86%D8%A7%D8%B3%D8%A8%D8%A9-%D9%84%D9%83%D8%9F-r811/" rel="">كيف تختار توزيعة لينكس المناسبة لك؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA/" rel="">دليلك الشامل إلى برمجة التطبيقات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AD%D8%B2%D9%8A%D9%85-%D9%88%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r106/" rel="">كيفية تحزيم وتوزيع تطبيقات بايثون</a>
	</li>
</ul>

<p>
	 
</p>
]]></description><guid isPermaLink="false">835</guid><pubDate>Sat, 01 Feb 2025 15:05:02 +0000</pubDate></item><item><title>&#x623;&#x62A;&#x645;&#x62A;&#x629; &#x627;&#x644;&#x645;&#x647;&#x627;&#x645; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x628;&#x627;&#x634; Bash</title><link>https://academy.hsoub.com/devops/linux/%D8%A3%D8%AA%D9%85%D8%AA%D8%A9-%D8%A7%D9%84%D9%85%D9%87%D8%A7%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D8%B4-bash-r833/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_01/----Bash.jpg.050957f5836f6eba796ab75228e28475.jpg" /></p>
<p>
	تُعدّ أتمتة المهام المتكررة هدفًا ضمنيًا لكل مستخدم يتعلم أساسيات باش، فهي في الغالب الغاية الأساسية من كل سكربت نكتبه، وسنعرض في هذا المقال مجموعة سكربتات مفيدة يمكنك الاستناد عليها لتتعلم أتمتة أي مهمة تريدها على خوادم لينكس، سنستخدم هنا جميع المهارات التي تعلمناها خلال السلسلة بدايةً من <a href="https://academy.hsoub.com/devops/linux/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D8%B4-bash-r827/" rel="">المصفوفات</a> و<a href="https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D8%AC%D9%85%D9%84-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D8%B4-bash-r830/" rel="">الجمل الشرطية</a> وكذلك <a href="https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D8%B4-bash-r831/" rel="">الحلقات</a> والمفاهيم الأخرى.
</p>

<h2 id="">
	إنشاء سكربت باش لإدارة المستخدمين
</h2>

<p>
	تُعدّ عملية إنشاء مستخدم على خوادم متعددة مهمة تقليدية لأي مدير نظام، لكنها مع التكرار تصبح مملة وشاقة، سننشئ معًا سكربت باش يؤتمت هذه العملية ويُنشئ المستخدمين تلقائيًا.
</p>

<p>
	سنُجهّز في البداية ملفًا نصيًّا يتضمن الأسماء hostnames أو عناوين IP للخوادم المطلوب إنشاء المستخدم عليها.
</p>

<p>
	يبين الملف servers.txt التالي على سبيل المثال أسماء خمسة خوادم:
</p>

<pre class="ipsCode">kabary@handbook:~$ cat servers.txt
server1
server2
server3
server4
server5
</pre>

<p>
	لقد استخدمنا أسماء الخوادم هنا بدلًا من عناوين IP الخاصة بها لأن هذه العناوين مخزنة لدينا في الملف "etc/hosts/" الذي يتضمن الاسم hostname وعنوان IP المقابل له، وتستطيع أيضًا استخدام <a href="https://academy.hsoub.com/devops/security/ssh/%D8%AF%D9%84%D9%8A%D9%84-%D8%A8%D8%B5%D8%B1%D9%8A-%D9%84%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D9%86%D9%81%D8%A7%D9%82-ssh-r508/" rel="">ملف إعدادات <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a>.
</p>

<p>
	والآن لنفحص سكربت إنشاء المستخدمين adduser.sh التالي:
</p>

<pre class="ipsCode">#!/bin/bash

servers=$(cat servers.txt)

echo -n "Enter the username: "
read name
echo -n "Enter the user id: "
read uid

for i in $servers; do
echo $i
ssh $i "sudo useradd -m -u $uid $name"
if [ $? -eq 0 ]; then
echo "User $name added on $i"
else
echo "Error on $i"
fi
done
</pre>

<p>
	عند تنفيذ السكربت سيطلب منا إدخال اسم المستخدم والمُعرِّف الخاص به، ثم سيتصل السكربت بكل خادم من الخوادم المحددة في الملف servers.txt السابق عبر اتصال <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> ويُنشئ عليه المستخدم الذي طلبناه.
</p>

<p>
	ننصح بمطالعة المزيد عن تقنية <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr> للاتصال بالخوادم البعيدة عبر مشاهدة الفيديو التالي:
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="487" id="ips_uid_512_5" src="https://academy.hsoub.com/applications/core/interface/index.html" title="ما هي تقنية SSH" width="866" data-embed-src="https://www.youtube.com/embed/CGDYjTTDOYg"></iframe>
</p>

<p>
	لنلاحظ الآن طريقة عمل السكربت بعد التنفيذ:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="165330" href="https://academy.hsoub.com/uploads/monthly_2025_01/img01-bash-script-adduser.png.5c5d22212ab4dd5163e5715ab4e44a99.png" rel=""><img alt="img01-bash-script-adduser.png" class="ipsImage ipsImage_thumbnailed" data-fileid="165330" data-ratio="79.00" data-unique="gzcarrfwq" style="width: 400px; height: auto;" width="759" src="https://academy.hsoub.com/uploads/monthly_2025_01/img01-bash-script-adduser.thumb.png.873f90984939bfef1fa13b3e3d46b229.png"></a>
</p>

<p>
	بهذا نكون قد نجحنا في إنشاء مستخدم يدعى ansible على الخوادم الخمسة المطلوبة.
</p>

<p>
	يوجد نقطتان مهمتان للغاية يتوجب علينا فهمها عند التعامل مع سكربت من هذا النوع:
</p>

<ul>
	<li>
		ضرورة استخدم مفاتيح مرور pass phrases فارغة لجلسات <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr>، أو استخدم الوكيل <abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">ssh</abbr></abbr>-agent لتمرير هويتنا إلى الخادم البعيد حتى لا نضطر لإدخال كلمة المرور في أثناء عمل السكربت
	</li>
	<li>
		الحرص على امتلاك حساب مستخدم فعال وعالي الصلاحيات يتمتع بأذونات الوصول إلى جميع الخوادم المطلوبة بدون متطلبات تتعلق بكلمة المرور
	</li>
</ul>

<p>
	تخيل أنك تحتاج لتعرف مستخدمين جدد على مائة خادم مثلًا، فكم ستكون العملية طويلة وشاقة، هنا تكمن أهمية الأتمتة إذ توفر عليك ساعات من العمل المضني.
</p>

<h2 id="-1">
	أتمتة عمليات النسخ الاحتياطي
</h2>

<p>
	النسخ الاحتياطي مهمة أساسية لا غنى عنها في كل منظومة، وكثيرًا ما نستخدم سكربتات باش لأتمتة المهام، يبين السكربت backup.sh التالي مثالًا عليها:
</p>

<pre class="ipsCode">#!/bin/bash

backup_dirs=("/etc" "/home" "/boot")
dest_dir="/backup"
dest_server="server1"
backup_date=$(date +%b-%d-%y)

echo "Starting backup of: ${backup_dirs[@]}"

for i in "${backup_dirs[@]}"; do
sudo tar -Pczf /tmp/$i-$backup_date.tar.gz $i
if [ $? -eq 0 ]; then
echo "$i backup succeeded."
else
echo "$i backup failed."
fi
scp /tmp/$i-$backup_date.tar.gz $dest_server:$dest_dir
if [ $? -eq 0 ]; then
echo "$i transfer succeeded."
else
echo "$i transfer failed."
fi
done

sudo rm /tmp/*.gzecho "Backup is done."
</pre>

<p>
	لنشرح السكربت أعلاه: أنشأنا في البداية مصفوفة تدعى backup_dirs سنكتب فيها أسماء المجلدات التي نريد أخذ نسخ احتياطية عنها، ثم عرّفنا ثلاثة متغيرات:
</p>

<ul>
	<li>
		<code>dest_dir</code>: لتحديد مجلد الوجهة الذي ستُحفظ فيه النسخ الاحتياطية
	</li>
	<li>
		<code>dest_server</code>: لتعيين الخادم الهدف
	</li>
	<li>
		<code>backup_time</code>: لتحديد تاريخ بدء عملية النسخ الاحتياطي
	</li>
</ul>

<p>
	وبعدها أنشأنا حلقة for ستمر على المجلدات الموجودة في المصفوفة "backup_dirs" واحدًا واحدًا و<a href="https://academy.hsoub.com/devops/linux/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A3%D9%85%D8%B1-tar-%D9%81%D9%8A-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r243/" rel="">تضغط كل مجلد منها باستخدام التعليمة <code>tar</code></a> وتحتفظ بالنسخة المضغوطة في المجلد المؤقت <code>tmp/</code>، ثم <a href="https://academy.hsoub.com/devops/networking/%D9%85%D8%A7-%D9%87%D9%88-%D8%A8%D8%B1%D9%88%D8%AA%D9%88%D9%83%D9%88%D9%84-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-%D8%A7%D9%84%D8%A2%D9%85%D9%86-scp%D8%9F-r617/" rel="">تنسخها إلى الخادم الهدف بواسطة التعليمة <code>scp</code></a>، وبعد اكتمال النسخ ستُحذف الملفات المضغوطة من المجلد <code>tmp/</code>.
</p>

<p>
	يبين الخرج التالي أمثلة على طريقة عمل السكربت:
</p>

<pre class="ipsCode">kabary@handbook:~$ ./backup.sh
Starting backup of: /etc /home /boot
/etc backup succeeded.
etc-Aug-30-20.tar.gz 100% 1288KB 460.1KB/s   00:02
/etc transfer succeeded.
/home backup succeeded.
home-Aug-30-20.tar.gz 100% 2543KB 547.0KB/s   00:04
/home transfer succeeded.
/boot backup succeeded.
boot-Aug-30-20.tar.gz 100%  105MB 520.2KB/s   03:26
/boot transfer succeeded.
Backup is done.
</pre>

<p>
	يمكننا جدولة السكربت باستخدام مهام cron job لتبدأ أعمال النسخ الاحتياطي عند منتصف الليل مثلًا، وذلك وفق التالي:
</p>

<pre class="ipsCode">kabary@handbook:~$ crontab -e
0    0   *   *   * /home/kabary/scripts/backup.sh
</pre>

<p>
	يمكن معرفة المزيد عن جدولة المهام بواسطة cron بالاطلاع على الفيديو التالي على قناة أكاديمية حسوب:
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="487" id="ips_uid_512_6" src="https://academy.hsoub.com/applications/core/interface/index.html" title="ما هي تقنية cron لجدولة المهام" width="866" data-embed-src="https://www.youtube.com/embed/uLnPBHU_lw4"></iframe>
</p>

<h2 id="-2">
	مراقبة المساحات التخزينية المتوفرة على القرص الصلب
</h2>

<p>
	المساحات التخزينية على <a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81-%D8%AA%D9%81%D9%87%D9%85-%D9%87%D9%8A%D9%83%D9%84%D9%8A%D8%A9-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D9%86%D9%83%D8%B3-r53/" rel="">نظام الملفات</a> في تناقص دائم وهذا أمرٌ مفروغٌ منه، وكل ما نستطيع القيام به تجاه هذا الأمر هو المراقبة والتصرف بالوقت المناسب حتى يؤدي التناقص المستمر إلى نفاذ المساحة وتعطل النظام، يساعدنا الأمر <code>df</code> على عرض المساحات المتوفرة على أي نظام ملفات، وهذا مثال على استخدامه:
</p>

<pre class="ipsCode">kabary@handbook:~$ df -h / /apps /database
Filesystem Size  Used Avail Use% Mounted on
/dev/sda5 20G  7.9G   11G  44% /
/dev/mapper/vg1-applv 4.9G  2.4G  2.3G  52% /apps
/dev/mapper/vg1-dblv 4.9G  4.5G  180M  97% /database
</pre>

<p>
	كما نلاحظ هنا فالمساحة المخصصة لقاعدة البيانات <code>database/</code> على نظام الملفات هذا تكاد تنفذ فنسبة استخدامها 97%، يمكنك عرض نسبة الاستخدام فقط بدون التفاصيل الأخرى بواسطة الأمر <code>awk</code>.
</p>

<p>
	دعونا لنستعمل الآن هذين الأمرين في السكربت disk_space.sh كما يلي:
</p>

<pre class="ipsCode">#!/bin/bash
filesystems=("/" "/apps" "/database")
for i in ${filesystems[@]}; do
usage=$(df -h $i | tail -n 1 | awk '{print $5}' | cut -d % -f1)
if [ $usage -ge 90 ]; then
alert="Running out of space on $i, Usage is: $usage%"
echo "Sending out a disk space alert email."
echo $alert | mail -s "$i is $usage% full" your_email
fi
done
</pre>

<p>
	يبدأ السكربت بإنشاء مصفوفة تدعى filesystems نكتب فيها أسماء مسارات أنظمة الملفات التي نود مراقبتها، ويوجد بعدها حلقة تكرار تُنَفذ عند كل عنصر من عناصر المصفوفة، وتستعلم عن نسبة استخدام المساحة التخزينية المخصصة له فإذا كانت أكبر من 90% سيُرسل لنا السكربت تنبيهًا عبر البريد الإلكتروني يخبرنا بأن المساحة على وشك النفاذ.
</p>

<p>
	ملاحظة: لاستخدام السكربت السابق يتوجب استبدال <code>your_email</code> بعنوان بريد إلكتروني حقيقي.
</p>

<p>
	سنحصل على هذا الخرج بعد تنفيذ السكربت:
</p>

<pre class="ipsCode">kabary@handbook:~$ ./disk_space.sh
Sending out a disk space alert email.
</pre>

<p>
	وستكون رسالة البريد الإلكتروني مشابهة للتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="165328" href="https://academy.hsoub.com/uploads/monthly_2025_01/img02-send-email-bash-sript.png.4672ada3fe627b72e95c18a659ae912a.png" rel=""><img alt="img02 send email bash sript" class="ipsImage ipsImage_thumbnailed" data-fileid="165328" data-ratio="71.33" data-unique="1v9dtxk5o" style="width: 400px; height: auto;" width="300" src="https://academy.hsoub.com/uploads/monthly_2025_01/img02-send-email-bash-sript.png.4672ada3fe627b72e95c18a659ae912a.png"> </a>
</p>

<p>
	لنفترض الآن أننا نرغب بجدولة السكربت disk_space.sh ليعمل كل ست ساعات مرة باستخدام مهام cron، فسنكتب التالي:
</p>

<pre class="ipsCode">kabary@handbook:~$ crontab -e
0    */6 *       *       * /home/kabary/scripts/disk_space.sh
</pre>

<h2>
	الخاتمة
</h2>

<p>
	وصلنا لنهاية مقالنا الذي عرضنا لكم فيه أمثلة بسيطة توضح أتمتة المهام بواسطة سكربتات باش، إن الإمكانات الفعلية لباش أكبر بكثير، لذا ندعوكم لتجربة تطوير السكربتات الموجودة هنا وتنفيذ أفكار أخرى مختلفة لتقوية مهاراتكم في باش واكتساب القدرة على أتمتة أي عمل تحتاجه على خوادم لينكس، كانت هذه نصيحتنا في ختام سلسلتنا التعليمية عن باش، نذكرك أخيرًا برابط <a href="https://academy.hsoub.com/tags/bash%20scripting/" rel="">سلسلة تعلم البرمجة باستخدام باش</a><span style="display: none;">    </span>.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://linuxhandbook.com/bash-automation/" rel="external nofollow">Automation With Bash</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/devops/linux/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%A8%D8%A7%D8%B4-bash-r832/" rel="">استخدام الدوال في باش Bash</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%85%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%B3%D8%A7%D8%A8%D9%8A%D8%A9-%D9%81%D9%8A-%D8%B3%D9%83%D8%B1%D8%A8%D8%AA%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B4-bash-r828/" rel="">استخدام المعاملات الحسابية في سكربتات باش Bash</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D8%B4-bash-r829/" rel="">عمليات السلاسل النصية في باش Bash</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D8%AC%D9%85%D9%84-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D8%B4-bash-r830/" rel="">الجمل الشرطية في باش Bash</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D8%B4-bash-r831/" rel="">الحلقات في باش </a><span style="display: none;"> </span><a href="https://academy.hsoub.com/devops/linux/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%A8%D8%A7%D8%B4-bash-r832/" rel="">Bash</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">833</guid><pubDate>Sat, 25 Jan 2025 15:04:01 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; &#x641;&#x64A; &#x628;&#x627;&#x634; Bash</title><link>https://academy.hsoub.com/devops/linux/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%A8%D8%A7%D8%B4-bash-r832/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_01/----Bash.jpg.9b518f5aa9b6bb37cf92e2d38e3fbcbf.jpg" /></p>
<p>
	تساعد  <span ipsnoautolink="true">الدوال البرمجية </span>على تنظيم سكربتات باش وجعلها أسهل في القراءة، وخاصة السكربتات كبيرة الحجم، إذ يمكننا استدعاء الدالة لأداء المهمة نفسها في عدة مواضع داخل السكربت دون الحاجة لتكرار كتابة التعليمات البرمجية الخاصة بهذه المهمة أكثر من مرة.
</p>

<p>
	سنتعلم في هذا المقال كيفية إنشاء <a href="https://academy.hsoub.com/programming/python/%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-functions-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1906/#:~:text=%D8%AA%D9%8F%D8%B9%D9%8E%D8%AF%D9%91%20%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84%20Functions%20%D9%81%D9%8A%20%D9%84%D8%BA%D8%A7%D8%AA,%D9%83%D8%AA%D8%A7%D8%A8%D8%A9%20%D8%AF%D9%88%D8%A7%D9%84%20%D9%85%D8%AE%D8%B5%D8%B5%D8%A9%20%D9%81%D9%8A%20%D9%84%D8%BA%D8%A9" rel="">الدوال البرمجية Functions</a> في سكربتات باش وتمرير الوسطاء إليها وإرجاع النتائج منها، كما سنتعرف على الفرق بين المتغيرات المحلية والمتغيرات العامة، وعلى ماهية الدوال العودية Recursive Functions وكيفية تحقيقها في باش.
</p>

<h2 id="">
	إنشاء الدوال في باش
</h2>

<p>
	توجد صيغتان للتصريح عن <a href="https://wiki.hsoub.com/Bash/introduction_to_functions" rel="external">الدوال البرمجية في باش</a>، وتُعدّ الصيغة التالية هي الأكثر استخدامًا:
</p>

<pre class="ipsCode">function_name () {
commands
}
</pre>

<p>
	أما الصيغة الثانية الأقل شهرة، فهي تبدأ بالكلمة المفتاحية <code>function</code> يليها اسم الدالة كما يلي:
</p>

<pre class="ipsCode">function function_name {
commands
}
</pre>

<p>
	ينبغي الانتباه إلى الأساسيات التالية عن التعامل مع الدوال:
</p>

<ul>
	<li>
		لا تعمل الدالة أبدًا ما لم نقم باستدعائها
	</li>
	<li>
		لا يمكننا استدعاء الدالة قبل تعريفها، لذا نجد أن تعريف الدالة في تسلسل تعليمات السكربت يأتي قبل أي استدعاء لها
	</li>
	<li>
		نستدعي الدالة بكتابة اسمها فقط
	</li>
	<li>
		نمرر الوسيط إن وجد مباشرة بعد اسم الدالة
	</li>
</ul>

<p>
	لنلقِ نظرة على السكربت fun.sh التالي:
</p>

<pre class="ipsCode">#!/bin/bash

hello () {
echo "Hello World"
}

hello
hello
hello
</pre>

<p>
	عرّفنا في بداية السكربت دالة تدعى <code>hello</code> وظيفتها عرض العبارة Hello World على <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B7%D8%B1%D9%81%D9%8A%D9%91%D8%A9-%D9%84%D9%8A%D9%86%D9%83%D8%B3-linux-terminal-r18/" rel="">الطرفية Terminal</a>، ثم استدعينا الدالة ثلاث مرات بكتابة اسمها، لذا عند تنفيذ السكربت ستظهر العبارة Hello World على الشاشة ثلاث مرات كالتالي:
</p>

<pre class="ipsCode">kabary@handbook:~$ ./fun.sh
Hello World
Hello World
Hello World
</pre>

<h2 id="-1">
	إرجاع القيم من الدالة في باش
</h2>

<p>
	لا تُرجع لنا دوال باش قيمًا عند استدعائها على عكس السائد في معظم <a href="https://academy.hsoub.com/programming/general/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات البرمجة</a>، فعند انتهاء التنفيذ الدالة تُرجع حالة الخروج من آخر أمر مُنفذ وتُخَزَّن الحالة في <a href="https://wiki.hsoub.com/Bash/variables" rel="external">المتغير الخاص ‎‏‎‏‏‎‏?$</a>، فإذا كان التنفيذ ناجحًا سيأخذ المتغير <code>‎‏‏‎‏?$</code> القيمة صفر، وإذا فشل التنفيذه فسيأخذ قيمة عدد صحيح موجب آخر يقع ضمن المجال [1-255] حسب سبب الفشل.
</p>

<p>
	لكن يمكننا استخدام التعليمة <code>return</code> لتغيير حالة الخروج من الدالة كما يوضح السكربت error.sh التالي:
</p>

<pre class="ipsCode">#! /bin/bash

error () {
blabla
return 0
}

error
echo "The return status of the error function is: $?"
</pre>

<p>
	إذا شغلنا السكربت السابق سنحصل على الخرج التالي:
</p>

<pre class="ipsCode">kabary@handbook:~$ ./error.sh
./error.sh: line 4: blabla: command not found
The return status of the error function is: 0
</pre>

<p>
	الكلمة <code>blabla</code> التي كتبناها في جسم الدالة <code>() error</code> ما هي إلّا كلمة عشوائية لا تمثل أي أمر برمجي، لذا فالتعليمية <code>return 0</code> هي السبب في حصولنا على حالة خروج صفرية من الدالة <code>() error</code> أي حالة خروج ناجحة، وبدونها ما كانت الدالة ستعطينا هذه النتيجة لأن <code>blabla</code> حتمًا سترجع رسالة خطأ مفادها لم يتم العثور على الأمر.
</p>

<p>
	رغم أن دوال باش لا تعيد قيمًا، فقد ساعدتنا طريقة الكتابة السابقة على تغيير حالة الخروج من الدالة ونجاح عملية تنفيذها، وبدونها لن تتمكن الدالة من إرجاع قيمة للبرنامج المستدعي أي لن نحصل على نتيجة تنفيذ الدالة لأن أي رمز خروج غير الصفر يشير إلى وجود خطأ.
</p>

<p>
	<strong>ملاحظة</strong>: لنتذكر دائمًا أن <code>return</code> تعني إنهاء تنفيذ الدالة والخروج منها.
</p>

<h2 id="-2">
	تمرير الوسطاء إلى دالة باش
</h2>

<p>
	يشبه تمرير الوسطاء إلى دوال باش كثيرًا تمرير الوسطاء إلى سكربتات باش، فكل ما يتطلبه الأمر كتابة الوسطاء أو سردها إلى جانب اسم الدالة عند استدعائها.
</p>

<p>
	يوضح السكربت iseven.sh التالي طريقة القيام بذلك:
</p>

<pre class="ipsCode">#!/bin/bash

iseven () {
if [ $(($1 % 2)) -eq 0 ]; then
echo "$1 is even."
else
echo "$1 is odd."
fi
}

iseven 3
iseven 4
iseven 20
iseven 111
</pre>

<p>
	تُميّز الدالة <code>() iseven</code> في الكود أعلاه بين الأعداد الزوجية والأعداد الفردية، وقد استدعيناها أربع مرات في السكربت وفي كل استدعاء مررنا لها عددًا مختلفًا، وطالما أننا كتبنا الوسيط مباشرة بعد الدالة فهو الوسيط الأول وسيُشير له المتغير <code>$1</code>.
</p>

<p>
	لنختبر الآن طريقة عمل السكربت:
</p>

<pre class="ipsCode">kabary@handbook:~$ ./iseven.sh
3 is odd.
4 is even.
20 is even.
111 is odd.
</pre>

<p>
	وسطاء الدالة مغايرين لوسطاء السكربت، وكل منهم يعمل في مستوى خاص مختلف عن الآخر، سيبين السكربت التالي funarg.sh الفرق:
</p>

<pre class="ipsCode">#!/bin/bash

fun () {
echo "$1 is the first argument to fun()"
echo "$2 is the second argument to fun()"
}

echo "$1 is the first argument to the script."
echo "$2 is the second argument to the script."

fun Yes 7
</pre>

<p>
	والآن لنشغل السكربت مع تمرير وسيطين له، ونلاحظ النتيجة:
</p>

<pre class="ipsCode">kabary@handbook:~$ ./funarg.sh Cool Stuff
Cool is the first argument to the script.
Stuff is the second argument to the script.
Yes is the first argument to fun()7 is the second argument to fun()
</pre>

<p>
	لقد استخدمنا المتغيرين <code>$1</code> و <code>$2</code> لوظيفتين، للتعبير عن الوسيطين الأول والثاني مرة للدالة ومرة للسكربت، وعند استدعائهما من داخل الدالة كان لهما معنى مختلف عن وسطاء السكربت.
</p>

<h2 id="-3">
	المتغيرات المحلية والمتغيرات العامة داخل سكربتات باش
</h2>

<p>
	تكون <a href="https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B3%D9%83%D8%B1%D8%A8%D8%AA%D8%A7%D8%AA-%D8%A7%D9%84%D8%B5%D8%AF%D9%81%D8%A9-%D8%A8%D8%A7%D8%B4-bash-r824/" rel="">متغيرات باش</a> عامة Global Variables أو محلية Local Variables، يمكننا استخدام المتغيرات العامة على مستوى السكربت كاملًا، أما المتغيرات المحلية فلا تستخدم إلّا ضمن نطاق الدالة.
</p>

<p>
	يوضح السكربت domain.sh bash التالي الفرق بينهما:
</p>

<pre class="ipsCode">#!/bin/bash

v1='A'
v2='B'

myfun() {
local v1='C'
v2='D'
echo "Inside myfun(): v1: $v1, v2: $v2"
}

echo "Before calling myfun(): v1: $v1, v2: $v2"
myfun
echo "After calling myfun(): v1: $v1, v2: $v2"
</pre>

<p>
	عرّفنا في بداية السكربت متغيرين عامين Global هما <code>v1</code> و <code>v2</code>، ثم في داخل الدالة <code>()myfun</code> عرّفنا متغير محلي Local يدعى <code>v1</code> باستخدام الكلمة المفتاحية <code>local</code>، وعدّلنا قيمة المتغير العام <code>v2</code>، نلاحظ أننا نستطيع استخدام الاسم نفسه لمتغيرات محلية مختلفة في دوال مختلفة.
</p>

<p>
	إذا شغّلنا السكربت الآن سنحصل على النتيجة التالية:
</p>

<pre class="ipsCode">kabary@handbook:~$ ./scope.sh
Before calling myfun(): v1: A, v2: B
Inside myfun(): v1: C, v2: D
After calling myfun(): v1: A, v2: D
</pre>

<p>
	نستنتج من المثال السابق ما يلي:
</p>

<ul>
	<li>
		إذا كان لدينا متغير محلي ومتغير عام لهما الاسم نفسه، فإن المتغير المحلي يتمتع بالأولوية داخل الدالة
	</li>
	<li>
		يمكننا تعديل قيمة متغير عام من داخل الدالة
	</li>
</ul>

<h2 id="recursivefunctions">
	الدوال العودية Recursive Functions
</h2>

<p>
	الدالة العودية recursive function هي دالة تستدعي نفسها مرات عدة حتى الوصول للشرط المطلوب، وهي تفيدنا في التعامل مع المسائل البرمجية التي يمكن تقسيمها إلى مسائل أصغر مشابهة لها.
</p>

<p>
	تُعدّ دالة <a href="https://ar.wikipedia.org/wiki/%D8%B9%D8%A7%D9%85%D9%84%D9%8A" rel="external nofollow">حساب العاملي factorial function</a> مثال تقليدي على التعادوية، يوضحه السكربت factorial.sh التالي:
</p>

<pre class="ipsCode">#!/bin/bash

factorial () {
if [ $1 -le 1 ]; then
echo 1
else
last=$(factorial $(( $1 -1)))
echo $(( $1 * last ))
fi
}

echo -n "4! is: "
factorial 4
echo -n "5! is: "
factorial 5
echo -n "6! is: "
factorial 6
</pre>

<p>
	تبدأ كل دالة عودية بتعريف حالة أساسية أو حدّية base case وعند الوصول إليها تنتهي الاستدعاءات الذاتية للدالة أي تنتهي العودية، والحالة الحدّية للدالة <code>()factorial</code> في مثالنا السابق الواحد حيث أن عاملي العدد 1 هو العدد 1:
</p>

<pre class="ipsCode">if [ $1 -le 1 ]; then
echo 1
</pre>

<p>
	لنوضح الآن الحالة العودية في دالة حساب العاملي: إن حساب العاملي لأي عدد صحيح موجب مثل n يساوي قيمة العدد n مضروبًا بحساب العاملي للعدد الأصغر منه n-1 وهكذا وفق المعادلة التالية:
</p>

<pre class="ipsCode">factorial(n) = n * factorial(n-1)
</pre>

<p>
	وقد استخدمنا هذه المعادلة في كتابة الحالة العودية recursive case للدالة السابقة وفق التالي:
</p>

<pre class="ipsCode">last=$(factorial $(( $1 -1)))
echo $(( $1 * last ))
</pre>

<p>
	لنشغّل السكربت ونتأكد من صحة النتائج:
</p>

<pre class="ipsCode">kabary@handbook:~$ ./factorial.sh
4! is: 24
5! is: 120
6! is: 720
</pre>

<p>
	يمكن تجربة أفكار أخرى لإتقان مفهوم الدوال العودية، لنحاول مثلًا حساب قيمة <a href="https://ar.wikipedia.org/wiki/%D9%85%D8%AA%D8%AA%D8%A7%D9%84%D9%8A%D8%A9_%D9%81%D9%8A%D8%A8%D9%88%D9%86%D8%A7%D8%AA%D8%B4%D9%8A" rel="external nofollow">سلسلة فيبوناتشي</a> لعدد معين، علينا أن نحدد في البداية الحالة الأساسية ثم الحالة العودية وبعدها نكتب السكربت.
</p>

<h2>
	الخاتمة
</h2>

<p>
	وصلنا إلى ختام مقالنا الذي شرحنا فيه أهمية الدوال البرمجية التقليدية والدوال التعاودية وحالات استخدامها في سكربتات باش، نأمل أنه كان مفيدًا، تابع مقالنا التالي والأخير حيث سنطبق فيه كل المبادئ التي تعلمناها على في كافة مقالات <a href="https://academy.hsoub.com/tags/bash%20scripting/" rel="">سلسلة تعلم باش</a>.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://linuxhandbook.com/bash-functions/" rel="external nofollow">Using Functions in Bash</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/devops/linux/%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D8%B4-bash-r831/" rel="">الحلقات في باش Bash</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%A3%D9%86%D8%B4%D8%A6-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC%D9%83-%D8%A7%D9%84%D9%86%D8%B5%D9%8A-%D8%A7%D9%84%D8%A3%D9%88%D9%84-%D8%B9%D9%84%D9%89-%D8%B5%D8%AF%D9%81%D8%A9-%D8%A8%D8%A7%D8%B4-bash-r823/" rel="">أنشئ برنامجك النصي الأول على صدفة باش Bash</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%B3%D9%83%D8%B1%D8%A8%D8%A7%D8%AA-%D8%A7%D9%84%D8%B5%D8%AF%D9%81%D8%A9-shell-scripts-r257/" rel="">استخدام الدوال في سكربات الصدفة Shell Scripts</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D8%B4-bash-r829/" rel="">عمليات السلاسل النصية في باش Bash</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">832</guid><pubDate>Sat, 11 Jan 2025 15:07:03 +0000</pubDate></item></channel></rss>
