كلاود
development

أساسيات نشر تطبيقك بحاويات Docker على VPS سعودي

كلاود|٨ سبتمبر ٢٠٢٦|6 دقائق قراءة|١٥٨ مشاهدة
أساسيات نشر تطبيقك بحاويات Docker على VPS سعودي

الصورة والحاوية وDockerfile وdocker compose بلغة مهندس. كيف تحزم تطبيقك مرة واحدة وتشغّله بنفس السلوك على VPS في الرياض بزمن استجابة 8ms.

ليش Docker يبسّط نشر تطبيقك على VPS؟

Docker يحزم تطبيقك مع كل ما يحتاجه للتشغيل في وحدة واحدة اسمها الصورة (image)، فتشغّلها على أي VPS بنفس السلوك تماماً بلا مفاجآت. المشكلة القديمة كانت جملة «يشتغل عندي على جهازي» ثم ينكسر على السيرفر لأن إصدار Node أو PHP أو مكتبة نظام مختلف. Docker يقتل هذه المشكلة من جذرها لأنه يثبّت البيئة كلها داخل الصورة: نظام التشغيل الأساسي، ولغة البرمجة بإصدارها المحدد، والحزم، وملفات تطبيقك، وأمر التشغيل. تبني الصورة مرة، وتشغّلها في أي مكان.

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

على سيرفر سعودي من كلاود في الرياض، هذا يعني أنك تنشر تطبيقك المحزّم بحاوية وتخدم مستخدميك بزمن استجابة يقارب 8ms، مقابل 100 إلى 120ms لو كان السيرفر خارج المملكة. في هذا المقال نفكّك المفاهيم الأربعة الأساسية: الصورة، والحاوية، وDockerfile، وdocker compose، ونربطها بخطوات نشر فعلية على VPS.

الفرق بين الصورة والحاوية

أكثر خلط عند المبتدئين هو بين الصورة والحاوية، والفرق بسيط جداً لكنه أساسي. الصورة قالب ثابت للقراءة فقط يحوي تطبيقك وبيئته، تشبه ملف تثبيت جاهز. الحاوية هي نسخة حيّة تعمل من تلك الصورة، تشبه العملية (process) قيد التشغيل. من صورة واحدة تقدر تشغّل عشر حاويات متطابقة في نفس اللحظة، كل واحدة معزولة عن الأخرى.

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

ليش العزل مهم عملياً؟

كل حاوية ترى نظام ملفات وشبكة وذاكرة خاصة بها، فلو انهارت حاوية أو امتلأت ذاكرتها لا تسحب البقية معها. تقدر تشغّل تطبيقاً يحتاج Python 3.9 بجانب تطبيق ثانٍ يحتاج Python 3.12 على نفس الـVPS بلا أي تعارض. هذا العزل الخفيف يعطيك مرونة الخوادم المتعددة على خادم واحد، وبتكلفة أقل بكثير لأنه لا يشغّل نظام تشغيل كاملاً لكل تطبيق مثل الأجهزة الافتراضية التقليدية. الحاوية تشارك نواة النظام مع المضيف، فحجمها بالميغابايتات لا الغيغابايتات، وتبدأ خلال جزء من الثانية بينما الجهاز الافتراضي الكامل قد يحتاج دقيقة أو أكثر للإقلاع.

الصورة تُبنى من طبقات

كل صورة Docker مكوّنة من طبقات (layers) مكدّسة فوق بعض، وكل طبقة تمثّل تغييراً في نظام الملفات نتج عن تعليمة في الـDockerfile. الطبقة الأولى نظام أساسي، ثم طبقة الحزم، ثم طبقة كود تطبيقك. هذا التصميم يعطيك ميزتين: الأولى أن الطبقات المشتركة تُخزّن مرة واحدة على القرص فتوفّر مساحة كبيرة لو عندك عدة صور تشترك في نفس الأساس، والثانية أن سحب صورة محدّثة ينقل الطبقات المتغيّرة فقط لا الصورة كاملة، فيقل حجم النقل من مئات الميغابايتات إلى بضعة ميغابايتات أحياناً. فهم الطبقات يفسّر لك لماذا ترتيب التعليمات في الـDockerfile يؤثر مباشرة على سرعة البناء وحجم الصورة النهائي.

Dockerfile: وصفة بناء الصورة

الـDockerfile ملف نصي بسيط يصف خطوة بخطوة كيف تُبنى الصورة. كل سطر تعليمة، وDocker ينفّذها بالترتيب. خذ مثالاً لتطبيق Node بسيط:

  • FROM node:20-alpine ابدأ من صورة جاهزة فيها Node 20 على نظام Alpine الخفيف الذي لا يتعدى حجمه بضعة ميغابايت.
  • WORKDIR /app اجعل مجلد العمل داخل الحاوية هو app.
  • COPY package*.json ./ انسخ ملفات الاعتماديات أولاً.
  • RUN npm ci ثبّت الحزم بنسخة مطابقة تماماً لملف القفل.
  • COPY . . انسخ بقية كود التطبيق.
  • EXPOSE 3000 أعلن أن التطبيق يستمع على المنفذ 3000.
  • CMD ["node", "server.js"] أمر التشغيل عند بدء الحاوية.

لاحظ ترتيب النسخ: نسخنا ملفات الاعتماديات قبل الكود لأن Docker يخزّن كل خطوة في طبقة (layer) مؤقتة. طالما ملف package.json ما تغيّر، Docker يعيد استخدام طبقة تثبيت الحزم المخزّنة بدل إعادة التثبيت من جديد، فتنزل مدة البناء من دقيقتين إلى ثوانٍ في التعديلات اللاحقة. هذا التخزين الطبقي وحده يوفّر لك وقتاً حقيقياً في كل مرة تبني فيها.

أمر البناء والتشغيل

بعد كتابة الملف تبني الصورة بأمر واحد: docker build -t myapp . والراية t تعطي الصورة اسماً. ثم تشغّل الحاوية: docker run -p 80:3000 myapp والراية p توصّل المنفذ 80 على الخادم بالمنفذ 3000 داخل الحاوية، فيصير تطبيقك متاحاً على الإنترنت. سطران فقط بينك وبين تطبيق شغّال، وهذه نفس السطرين تنفّذها على جهازك المحلي وعلى VPS الإنتاج بلا فرق.

docker compose: تشغيل عدة خدمات معاً

معظم التطبيقات الحقيقية ليست خدمة واحدة، بل تطبيق وقاعدة بيانات وربما ذاكرة تخزين مؤقت مثل Redis. تشغيل كل واحدة بأمر docker run منفصل ثم ربطها يدوياً متعب ويكثر فيه الخطأ. هنا يدخل docker compose، وهو أداة تصف كل خدمات تطبيقك في ملف واحد اسمه compose.yaml، ثم تشغّلها كلها بأمر واحد.

في الملف تعرّف كل خدمة: صورتها، ومنافذها، ومتغيرات بيئتها، والأقراص الدائمة لحفظ البيانات، والشبكة التي تربطها. مثلاً تعرّف خدمة web لتطبيقك وخدمة db لقاعدة PostgreSQL، وdocker compose ينشئ شبكة داخلية تلقائياً فيصل تطبيقك بقاعدة البيانات باسمها db مباشرة بلا عناوين IP. الأقراص الدائمة (volumes) هنا حرجة، لأنها تحفظ بيانات قاعدتك خارج الحاوية فلا تضيع لو أعدت بناء الحاوية أو حدّثتها.

أمر واحد لكل شيء

  • docker compose up -d يشغّل كل الخدمات في الخلفية.
  • docker compose down يوقفها ويزيلها بنظافة.
  • docker compose logs -f يتابع السجلات الحيّة لكل الخدمات.
  • docker compose pull يسحب أحدث نسخ الصور قبل التحديث.

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

خطوات النشر الفعلية على VPS سعودي

بعد ما جهّزت Dockerfile وcompose.yaml محلياً، النشر على خادم سحابي من كلاود مباشر. تختار باقة VPS مناسبة، وأصغرها باقة أساس بمعالج افتراضي وذاكرة 2GB بسعر 3,450 ريال سنوياً، تكفي تطبيقاً صغيراً إلى متوسط. بعد الدفع يُفعّل السيرفر تلقائياً خلال ثوانٍ.

  • ثبّت Docker على السيرفر بسكربت التثبيت الرسمي، خطوة واحدة تنتهي في أقل من دقيقتين.
  • انسخ ملفات مشروعك للخادم عبر git clone أو scp.
  • شغّل docker compose up -d فترتفع كل الخدمات.
  • اضبط جدار الحماية ليسمح بالمنفذين 80 و443 فقط.
  • ضع خادماً عكسياً مثل Nginx أمام تطبيقك لشهادة SSL المجانية.

الميزة الحقيقية أنك بنيت الصورة واختبرتها محلياً، فما يعمل عندك يعمل على الإنتاج بنسبة تقارب 100% لأن البيئة واحدة حرفياً، ولأن السيرفر في الرياض، مستخدموك السعوديون يوصلون بزمن استجابة 8ms، وتطبيقك متوافق مع متطلبات سيادة البيانات والنفاذ الوطني لأن كل بياناتك داخل المملكة. التحديث لاحقاً يصير سطرين: تبني صورة جديدة أو تسحبها، ثم up مرة ثانية، فينزل تحديثك بلا توقف يُذكر.

أخطاء شائعة تجنّبها من البداية

ثلاثة أخطاء تتكرر عند من يبدأ مع Docker. الأول تخزين بيانات قاعدة البيانات داخل الحاوية بلا قرص دائم، فتضيع كل البيانات عند أول إعادة بناء، والحل دائماً استخدام volume. الثاني وضع الأسرار مثل كلمات المرور ومفاتيح API نصاً صريحاً داخل Dockerfile، والصحيح تمريرها كمتغيرات بيئة عبر ملف env لا يُرفع للمستودع. الثالث بناء صور ضخمة بلا داعٍ، فاستخدام صورة alpine الخفيفة بدل الصورة الكاملة يخفّض حجم الصورة من مئات الميغابايتات إلى عشرات فقط، ويسرّع كل عملية سحب ونشر.

خطأ رابع أقل شهرة لكنه مكلف: عدم ضبط سياسة إعادة التشغيل للحاويات، فلو أعيد تشغيل الخادم لأي سبب لا ترتفع تطبيقاتك تلقائياً. الحل إضافة سطر restart بقيمة unless-stopped لكل خدمة في compose.yaml، فتضمن أن تطبيقك يعود للعمل بعد أي إعادة تشغيل أو انقطاع كهرباء بلا تدخّل يدوي منك، وخطأ خامس هو إهمال متابعة استهلاك الموارد، فالحاوية بلا حدود مضبوطة قد تلتهم كامل ذاكرة الخادم وتُسقط بقية الخدمات، ولذلك يُنصح بضبط حدود ذاكرة ومعالج لكل خدمة حرجة.

ابدأ صغيراً بتطبيق واحد وقاعدة بيانات واحدة في compose.yaml، وأتقن أوامر البناء والتشغيل والسجلات، ثم توسّع تدريجياً. Docker ليس معقداً بقدر ما يبدو، وبعد أول تطبيق تنشره بحاوية على VPS سعودي بزمن استجابة 8ms وتزويد تلقائي خلال ثوانٍ، صعب ترجع للطريقة اليدوية القديمة.

مشاركة