كتابة أول ملف docker-compose.yml خطوة بخطوة

دقائق القراءة: 5

كتابة أول ملف docker-compose.yml خطوة بخطوة

عندما ينتقل المشروع من حاوية واحدة إلى عدة خدمات مترابطة، يصبح التشغيل اليدوي عبر أوامر docker run مرهقاً ومعرضاً للأخطاء. هنا تظهر قيمة Docker Compose كطبقة توصيف declarative تسمح لك بتجميع التطبيق كاملاً داخل ملف واحد مفهوم وقابل للتكرار.

إذا كنت قد قرأت سابقاً ما هو Docker Compose؟ ولماذا نحتاجه لتشغيل المشاريع المعقدة؟ فهذه المقالة تمثل التطبيق العملي المباشر. سنبني ملف docker-compose.yml بطريقة هندسية، مع فهم كل سطر بدلاً من النسخ الأعمى.

هذا الأسلوب مهم جداً في بيئات DevOps لأنه يسهّل دمج بيئة التشغيل ضمن أنابيب CI/CD، ويجعل إعداد بيئات الاختبار والإنتاج أكثر اتساقاً. ولمن يريد الخلفية الأشمل، يمكن الرجوع إلى ما هو DevOps؟ ولماذا تدفع الشركات ثروات لمهندسي الأتمتة السحابية؟.

متى تحتاج إلى ملف docker-compose.yml؟

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

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

المتطلبات قبل البدء

  • تثبيت Docker Engine وميزة Docker Compose.
  • فهم أساسي للصور Images والحاويات Containers.
  • وجود مجلد مشروع يحتوي تطبيقاً أو خدمة ويب جاهزة للتغليف.

إذا لم تكن جهزت البيئة بعد، فابدأ من تثبيت Docker وإعداد بيئة العمل على Linux و Windows. وإذا لم تفهم بعد آلية الصور، فاقرأ فهم صور دوكر (Docker Images) وكيفية سحبها وإدارتها.

هيكل المثال الذي سنبنيه

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

ولو كنت قد أنشأت صورة تطبيقك مسبقاً عبر Dockerfile، فمقال كتابة أول Dockerfile: تحويل سكربت Python إلى صورة (Image) معزولة أو تحويل سيرفر Node.js متكامل إلى حاوية Docker قابلة للنقل سيكونان أساساً ممتازاً قبل ربط الخدمات معاً.

أول نسخة عملية من الملف

أنشئ داخل جذر المشروع ملفاً باسم docker-compose.yml، ثم أضف التكوين التالي:

services:
  app:
    build: .
    container_name: myapp
    ports:
      - "8000:8000"
    environment:
      APP_ENV: development
      DB_HOST: db
      DB_PORT: 5432
      DB_NAME: appdb
      DB_USER: appuser
      DB_PASSWORD: secret123
    depends_on:
      - db

  db:
    image: postgres:15
    container_name: mydb
    restart: unless-stopped
    environment:
      POSTGRES_DB: appdb
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: secret123
    ports:
      - "5432:5432"
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

شرح الملف سطراً بسطر

القسم services

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

الخدمة app

  • build: . تعني أن الصورة ستُبنى من Dockerfile الموجود في المجلد الحالي.
  • ports تربط منفذ الجهاز المضيف بمنفذ الحاوية.
  • environment تمرر متغيرات البيئة للتطبيق.
  • depends_on تضمن بدء خدمة قاعدة البيانات قبل التطبيق من ناحية الترتيب، لكنها لا تضمن الجاهزية الكاملة للخدمة.

الخدمة db

  • image تستخدم صورة جاهزة من Docker Hub.
  • restart: unless-stopped مفيدة لتقليل التوقف غير المتوقع بعد إعادة تشغيل الخادم.
  • volumes تمنع فقدان البيانات عند حذف الحاوية أو إعادة إنشائها.

ولفهم أهمية هذا الجزء عملياً، راجع التخزين الدائم (Docker Volumes): كيف نمنع ضياع قواعد البيانات عند توقف الحاوية؟.

لا تضع كلمات المرور الحساسة مباشرة داخل ملف docker-compose.yml في المشاريع الحقيقية. استخدم ملف .env أو حلول إدارة الأسرار مثل Secrets Manager لتقليل مخاطر التسريب داخل المستودعات البرمجية.

تشغيل المشروع لأول مرة

بعد حفظ الملف، استخدم الأمر التالي لبناء الخدمات وتشغيلها:

docker compose up --build

هذا الأمر ينفذ ثلاث مهام: بناء صورة التطبيق من Dockerfile، إنشاء الشبكة الافتراضية للمشروع، ثم إطلاق جميع الخدمات مع ربطها ببعضها تلقائياً.

ولتشغيل المشروع في الخلفية استخدم:

docker compose up -d

أما لإيقاف البيئة كاملة:

docker compose down

كيف تتخاطب الخدمات داخلياً؟

من أكثر ميزات Compose جمالاً أنه ينشئ شبكة داخلية تلقائياً. هذا يعني أن التطبيق لا يحتاج إلى عنوان IP ثابت لقاعدة البيانات، بل يكفيه استخدام اسم الخدمة مثل db.

لهذا كتبنا المتغير DB_HOST=db. داخل شبكة المشروع، هذا الاسم يُحل تلقائياً عبر DNS الداخلي الذي يديره Docker.

إضافة ملف .env لتنظيف التكوين

في المشاريع الناضجة، من الأفضل فصل القيم المتغيرة عن الملف الرئيسي. أنشئ ملف .env ثم ضعه بجوار ملف Compose:

DB_NAME=appdb
DB_USER=appuser
DB_PASSWORD=secret123
APP_PORT=8000

بعدها يمكن تعديل الملف ليصبح أكثر نظافة وقابلية للصيانة:

services:
  app:
    build: .
    ports:
      - "${APP_PORT}:8000"
    environment:
      DB_HOST: db
      DB_NAME: ${DB_NAME}
      DB_USER: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

أخطاء شائعة عند كتابة أول ملف

  • الخلط بين المسافات البادئة في YAML، لأن أي خطأ بسيط في indentation قد يكسر الملف بالكامل.
  • الاعتقاد أن depends_on يضمن جاهزية قاعدة البيانات، بينما هو يضمن ترتيب البدء فقط.
  • ربط قاعدة البيانات بمنفذ خارجي دون حاجة، ما يزيد سطح الهجوم الأمني.
  • عدم استخدام volumes مع قواعد البيانات، وهو خطأ يؤدي لفقدان البيانات بسهولة.

في بيئات الإنتاج، لا تنشر جميع المنافذ إلى الإنترنت بشكل مباشر. اجعل الوصول يتم عبر Reverse Proxy أو Load Balancer، وفعّل المراقبة والسجلات المركزية لتقليل مخاطر Downtime وصعوبة التشخيص.

لماذا يعتبر هذا مهماً في مشاريع CI/CD؟

عندما تملك ملف docker-compose.yml واضحاً، يصبح من السهل على أدوات مثل Jenkins أو GitHub Actions تشغيل بيئة اختبار كاملة مؤقتاً، ثم حذفها بعد انتهاء الفحوصات.

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

الخلاصة

كتابة أول ملف docker-compose.yml ليست مجرد خطوة تشغيلية، بل هي انتقال من إدارة حاويات منفردة إلى توصيف معماري منظم للتطبيق. أنت لا تكتب أوامر فقط، بل تصف البنية، العلاقات، التخزين، ومتغيرات البيئة في وثيقة تشغيل واحدة.

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

14 comments

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *