كان إعداد كل مشروع جديد جحيماً: كيف أنقذنا Docker Compose من فوضى “لكنها تعمل على جهازي”؟

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

قبل كم سنة، انضم لفريقنا مبرمج جديد، شب اسمه “أمجد”، شاطر ومتحمس. كالعادة، أول يوم إله كان مخصص لإعداد جهازه عشان يبدأ يشتغل على المشروع. أعطيناه المستودع (repository) وملف الـ README اللي فيه “شرح” من عشرين خطوة لكيفية تنصيب كل شيء: “نصّب Python نسخة 3.8.5 بالضبط، بعدين PostgreSQL نسخة 12، وما تنسى Redis، وطبعاً لازمك مليون مكتبة بتنزلها بـ pip، وكل وحدة فيهم بدها حكاية”.

قضى أمجد أول يوم وهو بحاول. ثاني يوم، ثالث يوم… والزلمة غرقان في بحر من الأخطاء اللي ما إلها أول من آخر. كل شوي يجينا على المكتب ويقول: “يا جماعة، طلعلي خطأ غريب… المكتبة الفلانية مش راضية تنزل”، أو “قاعدة البيانات ما بتشبك”. واحنا، بكل برود، كنا نرد عليه الرد المشهور: “غريبة والله… شغّالة عندي!“.

في هذيك اللحظة، أدركت إنه المشكلة مش في أمجد ولا فينا. المشكلة في طريقتنا كلها. إحنا بنبني أنظمة معقدة، وبنتوقع من كل واحد يركّبها على جهازه المختلف (واحد بشتغل على Windows، والثاني على Mac، والثالث على توزيعة Linux ما حدا سمع فيها) بنفس الطريقة وتشتغل! هالحكي مش منطقي. كان لازم نلاقي حل جذري، وهنا بدأت رحلتنا مع Docker و Docker Compose.

ما هي مشكلة “لكنها تعمل على جهازي”؟

قبل ما ندخل في الحل، خلينا نفصّص المشكلة. ليش بيئة التطوير على جهازك بتختلف عن جهازي؟

  • أنظمة التشغيل المختلفة: الأوامر، مسارات الملفات، وحتى طريقة تعامل النظام مع الشبكات تختلف بين Windows, macOS, و Linux.
  • إصدارات البرمجيات: مشروعك قد يتطلب نسخة Node.js 16، لكن زميلك عنده نسخة 18 على جهازه. هذا كفيل بتكسير كل شيء.
  • التبعيات (Dependencies) الخفية: أحياناً يعتمد الكود على مكتبة مثبتة على مستوى النظام (system-level library) نسيت توثيقها، ولا أحد يعلم بوجودها إلا جهازك.
  • إعدادات الخدمات: إعدادات قاعدة البيانات، خادم الويب، وخدمات التخزين المؤقت (caching) قد تختلف من جهاز لآخر.
  • ملفات README.md المنسية: التعليمات المكتوبة لإعداد المشروع تصبح قديمة بسرعة، ويصبح صيانتها عبئاً بحد ذاتها.

هذه الفوضى لا تضيع الوقت فقط، بل تقتل حماس أي مبرمج جديد وتخلق بيئة عمل محبطة.

الحل يبدأ من هنا: Docker

أول قطعة في الأحجية كانت Docker. ببساطة شديدة، Docker يسمح لنا بـ “تغليف” تطبيقنا وكل ما يحتاجه للعمل داخل صندوق معزول اسمه “حاوية” (Container). هذه الحاوية تشبه جهاز كمبيوتر مصغر وخفيف الوزن، يحتوي على نظام تشغيل مصغّر، لغة البرمجة، المكتبات، والكود الخاص بتطبيقك.

الجميل في الأمر أن هذه الحاوية تعمل بنفس الطريقة تماماً على أي جهاز مثبت عليه Docker. وداعاً لاختلافات أنظمة التشغيل!

الـ Dockerfile: وصفة بناء الحاوية

لكي نبني هذه الحاوية، نكتب تعليمات البناء في ملف اسمه Dockerfile. هذا الملف هو بمثابة وصفة طبخة. تخيل أنك تبني تطبيق Node.js، قد يبدو الـ Dockerfile الخاص به كالتالي:

# استخدم صورة رسمية لـ Node.js كقاعدة
FROM node:16

# أنشئ مجلد العمل داخل الحاوية
WORKDIR /usr/src/app

# انسخ ملفات package.json و package-lock.json
COPY package*.json ./

# قم بتثبيت تبعيات المشروع
RUN npm install

# انسخ باقي ملفات الكود إلى مجلد العمل
COPY . .

# عرّض البورت 3000 ليكون متاحاً خارج الحاوية
EXPOSE 3000

# الأمر الذي سيتم تشغيله عند بدء الحاوية
CMD [ "node", "server.js" ]

هذا رائع! الآن صار عنا طريقة موحدة لبناء وتشغيل تطبيقنا. لكن ماذا عن قاعدة البيانات؟ ماذا عن Redis؟ هل سنقوم بإنشاء وتشغيل كل حاوية يدوياً وربطها ببعضها؟ هنا يأتي دور البطل الحقيقي.

الأوركسترا الكاملة مع Docker Compose

معظم التطبيقات الحديثة ليست مجرد خدمة واحدة. هي عبارة عن مجموعة من الخدمات التي تتحدث مع بعضها (microservices). تطبيق الويب يحتاج قاعدة بيانات، وقد يحتاج خدمة تخزين مؤقت مثل Redis، وربما خادم للبحث مثل Elasticsearch.

Docker Compose هو “المايسترو” الذي يدير هذه الأوركسترا. هو أداة تسمح لك بتعريف وتشغيل تطبيقات متعددة الحاويات باستخدام ملف واحد بسيط بصيغة YAML اسمه docker-compose.yml.

باستخدام هذا الملف، يمكنك وصف كل خدمات تطبيقك (الويب، قاعدة البيانات، إلخ)، الشبكات التي تربطها، والمجلدات المشتركة (volumes) التي تحفظ البيانات.

مثال عملي: لنبني بيئة تطوير حقيقية

لنفترض أن لدينا مشروع بسيط يتكون من:

  1. backend: تطبيق API مكتوب بـ Node.js.
  2. db: قاعدة بيانات PostgreSQL.

هكذا سيبدو ملف docker-compose.yml الخاص بنا:

version: '3.8'

services:
  # خدمة تطبيق الـ Backend
  backend:
    build: .
    ports:
      - "3000:3000"
    volumes:
      # هذا السطر هو السحر: يربط الكود على جهازك بالكود داخل الحاوية
      # أي تغيير تحفظه في الكود يظهر فوراً داخل الحاوية بدون إعادة بناء
      - .:/usr/src/app
    environment:
      - DATABASE_URL=postgres://user:password@db:5432/mydatabase
    depends_on:
      - db

  # خدمة قاعدة البيانات
  db:
    image: postgres:13
    environment:
      POSTGRES_DB: mydatabase
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    ports:
      - "5432:5432"
    volumes:
      # هذا السطر يحفظ بيانات قاعدة البيانات على جهازك
      # حتى لو أوقفت الحاويات وأعدت تشغيلها، بياناتك لن تضيع
      - postgres_data:/var/lib/postgresql/data

# تعريف الـ Volume لضمان استمرارية البيانات
volumes:
  postgres_data:

شرح بسيط للملف:

  • services: نعرّف خدماتنا هنا (backend و db).
  • backend.build: يطلب من Compose بناء الحاوية باستخدام الـ Dockerfile الموجود في نفس المجلد.
  • backend.ports: يربط البورت 3000 على جهازك بالبورت 3000 داخل الحاوية.
  • backend.volumes: هذا هو سر الإنتاجية! يربط مجلد المشروع الحالي (.) بالمجلد /usr/src/app داخل الحاوية. أي تعديل على الكود ينعكس فوراً.
  • backend.depends_on: يخبر Compose أن خدمة backend تعتمد على خدمة db، فيقوم بتشغيل db أولاً.
  • db.image: بدلاً من البناء من الصفر، نستخدم صورة جاهزة لـ PostgreSQL من Docker Hub.
  • db.volumes: نستخدم ما يسمى بـ “Named Volume” لحفظ بيانات قاعدة البيانات بشكل دائم. هذا مهم جداً!

الأوامر التي غيرت حياتنا

الآن، بدلاً من عشرين خطوة في ملف الـ README، أصبح إعداد المشروع بأكمله عبارة عن أمرين فقط يقوم بهما المبرمج الجديد:

  1. تنزيل الكود من Git.
  2. كتابة أمر واحد في الطرفية (Terminal):
docker-compose up

هذا الأمر يقوم بقراءة ملف docker-compose.yml، بناء الصور اللازمة، تنزيل الصور الجاهزة، إنشاء وتشغيل كل الحاويات، وربطها ببعضها. المشروع بأكمله يعمل الآن على جهاز المبرمج الجديد تماماً كما يعمل على جهازك.

بعض الأوامر المفيدة الأخرى:

  • docker-compose up -d: لتشغيل الحاويات في الخلفية.
  • docker-compose down: لإيقاف وحذف الحاويات والشبكات.
  • docker-compose down -v: نفس الأمر السابق، لكنه يحذف الـ volumes أيضاً (احذر استخدامه مع بيانات مهمة!).
  • docker-compose logs -f backend: لمشاهدة سجلات (logs) خدمة الـ backend بشكل حي.
  • docker-compose build: لإعادة بناء الصور إذا قمت بتعديل الـ Dockerfile.

نصائح من خبرة أبو عمر: من الميدان

على مدار السنين، تعلمت كم شغلة بتخلي استخدام Docker Compose أسهل وأكثر فعالية. تفضلوا:

  • استخدموا ملفات .env: لا تكتبوا كلمات السر أو المفاتيح الحساسة مباشرة في ملف docker-compose.yml. ضعوها في ملف اسمه .env. سيقوم Docker Compose بقرائته تلقائياً.
  • لا تهملوا .dockerignore: تماماً مثل .gitignore، هذا الملف يخبر Docker ما هي الملفات التي يجب تجاهلها عند بناء الصورة. أضفوا مجلدات مثل node_modules و .git لتسريع عملية البناء وتقليل حجم الصورة.
  • افهموا الـ Volumes جيداً: هناك فرق بين ربط مجلد (bind mount) مثل .:/usr/src/app الذي نستخدمه للكود، وبين الـ “named volume” مثل postgres_data الذي نستخدمه لبيانات قاعدة البيانات. الأول للتطوير السريع، والثاني للحفاظ على البيانات.
  • فصل ملفات Compose: للمشاريع الكبيرة، يمكنكم فصل الإعدادات. ملف docker-compose.yml أساسي، وملف docker-compose.override.yml لإعدادات التطوير المحلية، وملف docker-compose.prod.yml لإعدادات الإنتاج.

الخلاصة: وداعاً للفوضى، أهلاً بالإنتاجية ✅

التحول إلى Docker Compose لم يكن مجرد تغيير تقني، بل كان تغييراً في ثقافة العمل في الفريق. لقد حررنا من ساعات لا تحصى من التنصيب وحل المشاكل، وأعاد التركيز إلى ما يهم حقاً: كتابة كود رائع وحل مشاكل حقيقية.

الآن، عندما ينضم عضو جديد، يستغرق إعداد بيئة عمله دقائق معدودة بدلاً من أيام. عبارة “لكنها تعمل على جهازي” اختفت تقريباً من قاموسنا، وحل محلها الثقة والإنتاجية.

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

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

اختبارات الاداء والجودة

تغطية اختبارات 100% وكود مليء بالعلل: كيف أنقذنا “الاختبار الطفري” من الثقة الزائفة

كنا نظن أن تغطية الاختبارات بنسبة 100% هي درعنا الحصين، لكن الواقع كان صادماً. اكتشف كيف كشف لنا "الاختبار الطفري" (Mutation Testing) ضعف اختباراتنا وأنقذ...

23 مايو، 2026 قراءة المزيد
أتمتة العمليات

مراجعات الكود حلبة مصارعة؟ كيف أنقذتنا خطاطيف ما قبل الإيداع (Pre-commit Hooks) من جحيم الجدالات

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

23 مايو، 2026 قراءة المزيد
نصائح برمجية

كانت دوالنا البرمجية هرمًا من الجحيم: كيف أنقذتنا ‘الشروط الحارسة’ (Guard Clauses) من فوضى الـ if المتداخلة؟

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

23 مايو، 2026 قراءة المزيد
ذكاء اصطناعي

كانت نماذجنا تلتهم موارد السيرفر: كيف أنقذنا ‘تكميم النماذج’ (Model Quantization) من جحيم فواتير الحوسبة؟

أشارككم قصة حقيقية من قلب المعركة مع فواتير الحوسبة السحابية، وكيف كانت تقنية "تكميم النماذج" (Model Quantization) هي طوق النجاة الذي أنقذنا. سنتعلم معاً كيف...

23 مايو، 2026 قراءة المزيد
خوارزميات

كنا نرهق قاعدة بياناتنا بأسئلة ‘هل هذا موجود؟’: كيف أنقذنا ‘مرشح بلوم’ (Bloom Filter) من جحيم الاستعلامات غير الضرورية؟

في هذه المقالة، أشارككم قصة حقيقية عن كيفية مواجهتنا لمشكلة استعلامات "التحقق من الوجود" التي كانت ترهق قاعدة بياناتنا، وكيف كان "مرشح بلوم" (Bloom Filter)...

23 مايو، 2026 قراءة المزيد
تجربة المستخدم والابداع البصري

كانت واجهاتنا تتغير مع كل تحديث للمتصفح: كيف أنقذنا ‘نظام التصميم’ من جحيم الفوضى البصرية؟

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

22 مايو، 2026 قراءة المزيد
البودكاست