Docker Compose أداة تصف بها تطبيقك متعدّد الحاويات في ملف نصّي واحد اسمه docker-compose.yml، ثم تشغّله كلّه بأمر واحد docker compose up -d بدل تكرار عشرات أوامر docker run الطويلة يدويًا. داخل هذا الملف تعرّف "خدمات" (services)، كل خدمة حاوية لها صورة (image) ومنافذ (ports) وأحجام (volumes) ومتغيّرات بيئة (environment)، وتُنشئ Compose تلقائيًا شبكة خاصّة تصل بها الحاويات فتنادي بعضها باسم الخدمة لا بعنوان IP. هذا يجعل تشغيل ستاك كامل — تطبيق ويب + قاعدة بيانات + كاش — أمرًا نظيفًا قابلًا للنسخ والمشاركة عبر Git. ملاحظة مهمّة قبل أن تبدأ: الأمر الحديث هو docker compose (بمسافة، إضافة داخل Docker)، أمّا docker-compose القديم (بشرطة) فمُهمَل وأُزيل فعليًا، ومفتاح version: في أعلى الملف صار مُلغى ويُتجاهَل. هذا الدليل يشرح كل ذلك عمليًا لمن يدير VPS ويريد استضافة تطبيقاته بنظافة.
Docker Compose هو الجسر الطبيعي بين "شغّلت حاوية واحدة على جهازي" و"عندي تطبيق حقيقي بعدّة أجزاء يعمل على خادمي". أي تطبيق جدّي اليوم ليس حاوية واحدة: هو تطبيق ويب يتحدّث إلى قاعدة بيانات، ومعهما طبقة كاش، وربّما بروكسي عكسي أمامها. تشغيل هذه القطع يدويًا بأوامر منفصلة مرهق وعُرضة للخطأ، وهنا يدخل Compose ليحوّل كل ذلك إلى ملف واحد تقرؤه، تراجعه، وتشاركه. إن كنت لم تجهّز خادمك بعد فابدأ بدليل إعداد أول خادم VPS، ثم عُد إلى هنا لتشغيل تطبيقاتك بأناقة عبر Compose.
ما هو Docker Compose وما المشكلة التي يحلّها؟
لنبدأ من الحاوية نفسها. الحاوية (container) نسخة معزولة خفيفة من تطبيق مع كل اعتمادياته، تعمل فوق Docker دون أن تشغل نظام تشغيل كاملًا كما تفعل الأجهزة الافتراضية. تشغيل حاوية واحدة سهل: docker run وبعض الأعلام (flags) وانتهى الأمر. المشكلة تبدأ حين يصبح تطبيقك أكثر من حاوية واحدة، وهذا يحدث بسرعة مذهلة.
تخيّل تطبيق ووردبريس بسيطًا: تحتاج حاوية لخادم PHP، وحاوية لقاعدة بيانات MySQL، وربّما حاوية Redis للكاش. كل حاوية تحتاج شبكة مشتركة ليتحدّثوا، وأحجامًا لحفظ البيانات، ومتغيّرات بيئة لكلمات المرور، وترتيبًا صحيحًا في الإقلاع. لو أردت فعل هذا بأوامر docker run منفصلة، فستكتب ثلاثة أوامر طويلة جدًا، تنشئ الشبكة يدويًا، تربط كل حاوية بها، تضبط الأعلام بدقّة، وتتذكّر كل ذلك في كل مرّة تعيد فيها التشغيل. أول خطأ مطبعي في علَم واحد يكسر كل شيء، ولا يوجد "مصدر حقيقة" مكتوب تعود إليه.
Docker Compose يحلّ هذا بالضبط. بدل الأوامر المتناثرة، تكتب ملفًا نصّيًا واحدًا بصيغة YAML يصف الحالة المرغوبة لتطبيقك كاملًا: ما الخدمات، وما صور كل منها، وكيف تتصل، وأين تحفظ بياناتها. ثم أمر واحد — docker compose up -d — يقرأ الملف ويبني كل شيء: ينشئ الشبكة، يشغّل الحاويات بالترتيب، يربط الأحجام، ويطبّق المتغيّرات. وأمر واحد آخر — docker compose down — يوقف كل شيء وينظّفه. الملف نصّي، فتضعه في Git، تراجعه في طلبات الدمج، وتشاركه مع فريقك أو تنسخه إلى خادم آخر فيعمل بالمثل تمامًا.
الفكرة الجوهرية: Compose ليس تقنية جديدة تحت الحاويات، بل طبقة تنسيق تصريحية فوق Docker. أنت تصف ما تريد، وCompose يترجمه إلى أوامر Docker التي كنت ستكتبها يدويًا. هذا التحوّل من "الأمر الإجرائي" إلى "الوصف التصريحي" هو ما يجعله أداة لا غنى عنها لأي أحد يستضيف تطبيقات حقيقية على خادمه.
يستحقّ التوقّف قليلًا عند معنى "التصريحي" هنا لأنه لبّ الفكرة. في النهج الإجرائي أنت تعطي الحاسوب سلسلة خطوات: أنشئ شبكة، ثم شغّل هذه الحاوية، ثم اربطها، ثم شغّل تلك. أي خطوة تنساها أو ترتّبها خطأً تكسر النتيجة، وعليك أن تتذكّر الحالة الراهنة في كل مرّة. أمّا في النهج التصريحي فأنت تصف الوجهة — "أريد هذه الخدمات الثلاث بهذه الإعدادات متّصلة هكذا" — وتترك لـCompose أمر الوصول إليها. لو شغّلت up مرّتين، لا يكرّر Compose العمل بل يقارن الواقع بالوصف ويصلح الفرق فقط. هذه الفكرة نفسها — وصف الحالة المرغوبة بدل خطوات الوصول إليها — هي ما تبني عليه أدوات البنية التحتية الحديثة كلّها، وCompose أبسط مدخل عملي إليها.
لماذا Compose أفضل من تكرار أوامر docker run؟
قد تسأل: ما دام كل ما يفعله Compose هو ترجمة الوصف إلى أوامر Docker، فلماذا لا أكتب سكربت Bash يجمع أوامر docker run وينتهي الأمر؟ الجواب أن الفرق ليس في عدد الأوامر فقط، بل في الوضوح والصيانة والاسترجاع. الجدول التالي يقارن الطريقتين على المحاور المهمّة عمليًا:
| المعيار | أوامر docker run منفصلة | Docker Compose |
|---|---|---|
| مصدر الحقيقة | مبعثر في سكربتات وذاكرة | ملف واحد واضح في Git |
| تشغيل الستاك كاملًا | عدّة أوامر بترتيب يدوي | docker compose up -d |
| إيقاف وتنظيف | إيقاف وحذف كل حاوية يدويًا | docker compose down |
| الشبكة بين الحاويات | تُنشأ وتُربط يدويًا | تلقائية باسم الخدمة |
| مراجعة التغييرات | صعبة (لا ملف مرجعي) | diff نظيف في طلب الدمج |
| النقل لخادم آخر | إعادة كتابة الأوامر | انسخ الملف وشغّله |
| احتمال الخطأ البشري | مرتفع (أعلام طويلة) | منخفض (وصف منظّم) |
لاحظ أن أكبر مكسب ليس السطر الواحد الذي يشغّل كل شيء، بل أن الملف يصير توثيقًا حيًّا لبنية تطبيقك. أي مطوّر جديد يفتح docker-compose.yml فيفهم فورًا: هذه ثلاث خدمات، هذه الصور، هذه المنافذ، هذه البيانات الدائمة. لا حاجة لسؤال "كيف نشغّل هذا محليًا؟" — الجواب مكتوب. وحين تحتاج نقل التطبيق إلى VPS آخر أو ترقية نسخة قاعدة البيانات، تعدّل سطرًا واحدًا في الملف وتعيد التشغيل بدل تفكيك سلسلة أوامر متشابكة.
هناك أيضًا فائدة أقلّ وضوحًا: Compose يفهم العلاقات بين الخدمات. حين تكتب أن الخدمة web تعتمد على db، يعرف Compose أن يشغّل قاعدة البيانات أولًا. وحين تحدّث صورة واحدة فقط، يعيد إنشاء تلك الحاوية وحدها ويترك البقيّة تعمل. هذا الذكاء التنسيقي مستحيل عمليًا مع سكربتات docker run الخام دون كتابة منطق معقّد بنفسك.
كيف يتكوّن ملف docker-compose.yml؟
الملف مكتوب بصيغة YAML، وهي صيغة تعتمد على المسافات البادئة (indentation) للتعبير عن التداخل — لا أقواس ولا فواصل منقوطة، بل مسافتان لكل مستوى. المفتاح الأعلى دائمًا هو services:، وتحته كل خدمة باسمها، وتحت كل خدمة إعداداتها. إليك أبسط ملف ممكن يشغّل خادم ويب واحدًا:
services:
web:
image: nginx:latest
ports:
- "8080:80"
هذا الملف يعرّف خدمة واحدة اسمها web، تستخدم صورة nginx، وتربط المنفذ 8080 على الخادم بالمنفذ 80 داخل الحاوية. لاحظ غياب أي مفتاح version: في الأعلى — هذا مقصود وصحيح في الصيغة الحديثة. الجدول التالي يشرح أهمّ المفاتيح التي ستستخدمها داخل كل خدمة:
| المفتاح | ماذا يفعل | مثال |
|---|---|---|
image | الصورة التي تُبنى منها الحاوية | postgres:16 |
build | بناء صورة من Dockerfile محلّي بدل جلبها | build: . |
ports | ربط منفذ الخادم بمنفذ الحاوية | "8080:80" |
volumes | ربط تخزين دائم أو مجلد بالحاوية | db-data:/var/lib/postgresql/data |
environment | متغيّرات البيئة داخل الحاوية | POSTGRES_PASSWORD: secret |
env_file | تحميل المتغيّرات من ملف .env | env_file: .env |
depends_on | ترتيب الإقلاع بين الخدمات | depends_on: [db] |
restart | سياسة إعادة التشغيل عند التعطّل | unless-stopped |
networks | الشبكات التي تنضمّ إليها الخدمة | networks: [backend] |
healthcheck | فحص دوري لصحّة الحاوية | أمر + فترة زمنية |
فهم صيغة ports مهمّ: القيمة "8080:80" تعني أن الرقم على اليسار هو المنفذ على الخادم (المكشوف للخارج)، والرقم على اليمين هو المنفذ داخل الحاوية. لو لم تحتَج كشف المنفذ للعالم — كما هي حال قاعدة البيانات — فلا تضع ports أصلًا؛ الخدمات تصل بعضها داخليًا دون أي منفذ مكشوف، وهذا أأمن كما سنرى. أمّا volumes فتربط تخزينًا يبقى بعد حذف الحاوية، وenvironment تمرّر إعدادات وأسرارًا، وdepends_on تضبط الترتيب.
ما الأوامر الأساسية في Docker Compose؟
بعد كتابة الملف، تدير دورة حياة تطبيقك بمجموعة صغيرة من الأوامر تُشغَّل من داخل المجلد الذي يحوي docker-compose.yml. ستنفّذ هذه الأوامر غالبًا على خادمك عبر SSH، فإن لم تكن معتادًا عليه فراجع أساسيات SSH للمبتدئين أولًا. الجدول التالي يجمع الأوامر التي ستستعملها يوميًا:
| الأمر | ماذا يفعل |
|---|---|
docker compose up -d | يبني ويشغّل كل الخدمات في الخلفية (detached) |
docker compose down | يوقف ويحذف الحاويات والشبكة (لا الأحجام افتراضيًا) |
docker compose ps | يعرض حالة الخدمات العاملة ومنافذها |
docker compose logs -f | يتابع سجلّات كل الخدمات لحظيًا |
docker compose logs -f web | يتابع سجلّات خدمة واحدة فقط |
docker compose exec web sh | يفتح صدفة داخل حاوية عاملة |
docker compose pull | يجلب أحدث نسخ الصور المعرّفة |
docker compose restart | يعيد تشغيل الخدمات دون إعادة إنشائها |
docker compose stop | يوقف الحاويات دون حذفها |
docker compose config | يتحقّق من صحّة الملف ويطبع النتيجة النهائية |
العلَم -d في up -d يعني "detached" أي التشغيل في الخلفية وإعادة الطرفية إليك بدل تعليقها على السجلّات — وهو ما تريده دائمًا على خادم. أمر logs -f (follow) لا يقدّر بثمن عند التشخيص: يريك ما يطبعه التطبيق لحظة بلحظة، ويمكنك حصره بخدمة واحدة بإضافة اسمها. أمّا exec فيدخلك داخل حاوية عاملة لتفحص ملفًا أو تشغّل أمرًا — مثلًا docker compose exec db psql -U postgres يفتح لك عميل قاعدة البيانات مباشرة.
انتبه لفرق دقيق ومهمّ: docker compose down يحذف الحاويات والشبكة لكنه لا يحذف الأحجام المُسمّاة افتراضيًا، فبياناتك آمنة. لكن لو أضفت العلَم -v (أي down -v) فسيحذف الأحجام أيضًا وتضيع البيانات — استخدمه بحذر شديد ولا تشغّله على الإنتاج إلا وأنت متأكّد. وللترقية النظيفة: docker compose pull ثم docker compose up -d يجلب الصور الجديدة ويعيد إنشاء الحاويات المتأثّرة فقط، دون لمس البقيّة.
مثال عملي: ستاك من تطبيق ويب وقاعدة بيانات وكاش
النظرية تكفي؛ لنبنِ ملفًا حقيقيًا. سنشغّل ستاكًا شائعًا جدًا: تطبيق ويب يعتمد على قاعدة بيانات PostgreSQL وطبقة كاش Redis. هذا نموذج ينطبق على معظم التطبيقات الحديثة، من واجهات Node.js إلى تطبيقات Python وPHP. إليك الملف كاملًا:
services:
web:
image: myapp:latest
restart: unless-stopped
ports:
- "8080:8080"
environment:
DATABASE_URL: "postgres://app:${DB_PASSWORD}@db:5432/appdb"
REDIS_URL: "redis://cache:6379"
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: appdb
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: redis:7
restart: unless-stopped
volumes:
- cache-data:/data
volumes:
db-data:
cache-data:
لنقرأ هذا الملف سطرًا سطرًا. تحت services: ثلاث خدمات: web وdb وcache. خدمة web هي الوحيدة التي تكشف منفذًا للعالم (8080:8080)، لأنها الواجهة التي يصلها المستخدم. لاحظ متغيّرات البيئة فيها: DATABASE_URL تشير إلى المضيف db — وهذا اسم الخدمة نفسه، لا عنوان IP — وREDIS_URL تشير إلى cache. هذا هو جوهر شبكة Compose الذي سنفصّله بعد قليل.
خدمة db لا تكشف أي منفذ للخارج (لا مفتاح ports)، وهذا مقصود وآمن: قاعدة البيانات يجب أن تكون مرئية لخدمة web فقط عبر الشبكة الداخلية، لا للإنترنت. تحفظ بياناتها في حجم مُسمّى db-data مربوط بالمسار الذي يخزّن فيه PostgreSQL ملفاته، فتبقى البيانات حتى لو حذفت الحاوية. وأضفنا healthcheck يفحص جاهزية قاعدة البيانات كل عشر ثوانٍ. خدمة cache مماثلة لكن أبسط: صورة Redis مع حجم لحفظ بياناتها.
أخيرًا، القسم volumes: في أسفل الملف يعلن الحجمين المُسمّيين حتى يديرهما Docker. لاحظ أيضًا depends_on في خدمة web: هي لا تكتفي بذكر أن web تعتمد على db، بل تشترط condition: service_healthy — أي لا تبدأ web حتى ينجح فحص صحّة قاعدة البيانات فعليًا، لا مجرّد بدء الحاوية. هذا يمنع خطأً شائعًا: بدء التطبيق قبل جاهزية قاعدة البيانات فيفشل الاتصال.
الرسم التالي يوضّح ستاكًا حقيقيًا مماثلًا (تشغيل n8n على VPS) بالبنية نفسها: بروكسي وتطبيق وقاعدة بيانات وحجم دائم، وكلّها تتشابك عبر شبكة Compose واحدة:
كيف تتشابك الخدمات عبر اسم الخدمة؟
هذه النقطة تربك المبتدئين أكثر من غيرها، فتستحقّ توضيحًا مستقلًا. حين تشغّل docker compose up، تُنشئ Compose تلقائيًا شبكة افتراضية خاصّة بمشروعك من نوع bridge، وتضمّ إليها كل الخدمات. داخل هذه الشبكة، يشغّل Docker خادم DNS داخليًا يربط كل اسم خدمة بعنوان الحاوية الحالي. فحين يحاول تطبيق web الاتصال بالمضيف db، يستعلم عن الاسم db فيحصل على عنوان حاوية قاعدة البيانات مباشرة.
النتيجة العملية: لا تكتب عناوين IP في أي مكان، ولا تقلق من تغيّرها. عناوين الحاويات قد تتبدّل عند كل إعادة إنشاء، لكن الأسماء ثابتة. تكتب في إعدادات تطبيقك db:5432 بدل 172.18.0.3:5432، ويظلّ يعمل مهما أعدت التشغيل. هذا الاكتشاف التلقائي للخدمات (service discovery) هو ما يجعل ملفات Compose قابلة للنقل: انسخ الملف إلى أي خادم فيعمل، لأن لا شيء فيه مرتبط بعناوين محدّدة.
نقطة دقيقة تستحقّ الانتباه: المنفذ الذي تصل عبره خدمة أخرى داخل الشبكة هو المنفذ الداخلي للحاوية، لا المنفذ المكشوف في ports. أي أن web تصل قاعدة البيانات على db:5432 (المنفذ الداخلي لـPostgreSQL) حتى لو لم يكن هناك أي مفتاح ports في خدمة db إطلاقًا. مفتاح ports مخصّص فقط لكشف منفذ للعالم الخارجي؛ أمّا التواصل الداخلي بين الخدمات فيتمّ عبر الشبكة الخاصّة على المنافذ الداخلية دائمًا. هذا الفصل بين "الداخلي" و"المكشوف" هو أساس بناء ستاك آمن.
يمكنك المضي أبعد وتقسيم خدماتك على أكثر من شبكة للعزل. تصوّر ستاكًا فيه بروكسي عكسي أمامي، وتطبيق ويب، وقاعدة بيانات: تضع البروكسي والتطبيق على شبكة "أمامية"، والتطبيق وقاعدة البيانات على شبكة "خلفية"، فلا يستطيع البروكسي رؤية قاعدة البيانات إطلاقًا لأنهما ليسا على شبكة مشتركة. هذا التقسيم يقلّل سطح الهجوم: لو اخترق مهاجم البروكسي، فهو لا يملك حتى مسارًا شبكيًا إلى قاعدة بياناتك. لست مضطرًا لهذا التعقيد في كل مشروع — الشبكة الافتراضية الواحدة تكفي معظم الحالات — لكن معرفة أن الخيار موجود يمنحك أداة قويّة حين ينمو تطبيقك ويتطلّب فصلًا أمنيًا أدقّ بين طبقاته.
البيانات الدائمة: الأحجام المُسمّاة مقابل ربط المجلدات
الحاويات عابرة بطبيعتها: احذف الحاوية وتضيع كل بيانات نظام ملفاتها الداخلي. هذا مقبول لتطبيق عديم الحالة (stateless) كخادم ويب، لكنه كارثة لقاعدة بيانات. لذلك تفصل البيانات المهمّة عن دورة حياة الحاوية عبر الأحجام. أمامك خياران رئيسيان، والجدول التالي يوازن بينهما:
| المعيار | حجم مُسمّى (named volume) | ربط مجلد (bind mount) |
|---|---|---|
| مكان التخزين | يديره Docker في منطقته | مسار تختاره على الخادم |
| الاستخدام الأمثل | بيانات قواعد البيانات الدائمة | كود التطوير وملفات الإعداد |
| قابلية النقل | مرتفعة (لا مسار ثابت) | مرتبط ببنية مجلدات الخادم |
| الأداء | ممتاز عبر المنصّات | ممتاز على Linux |
| النسخ الاحتياطي | عبر أدوات Docker | نسخ المجلد مباشرة |
| المثال | db-data:/var/lib/... | ./config:/etc/app |
القاعدة العملية بسيطة: لبيانات الإنتاج الدائمة — وأهمّها قواعد البيانات — استخدم الأحجام المُسمّاة، لأنها معزولة، يديرها Docker، وقابلة للنقل دون ارتباط بمسار محدّد على الخادم. أمّا ربط المجلدات (bind mounts) فمثالي حين تريد أن يرى الحاوية مجلدًا حقيقيًا على خادمك: ملف إعداد nginx.conf تعدّله وتعيد تحميله، أو كود تطبيق تطوّره لحظيًا فينعكس داخل الحاوية فورًا. في ملف المثال السابق استخدمنا حجمًا مُسمّى db-data لأنه بيانات قاعدة بيانات دائمة، وهو الخيار الصحيح.
انتبه هنا إلى سلوك الحذف: docker compose down يزيل الحاويات والشبكة لكنه يُبقي الأحجام المُسمّاة، فبياناتك تنجو من عمليات الإيقاف والترقية اليومية. لكن docker compose down -v يحذف الأحجام أيضًا وتضيع البيانات فورًا — لهذا لا تشغّل العلَم -v على خادم إنتاج إلا وأنت متعمّد تمامًا مسح كل شيء والبدء من الصفر. اجعل هذا الفارق راسخًا في ذهنك قبل أن تكتب الأمر على خادم حقيقي.
كيف تدير متغيّرات البيئة وملف .env؟
التطبيقات تحتاج إعدادات: كلمات مرور قواعد البيانات، مفاتيح API، أسماء المضيفين، أوضاع التشغيل. لا تكتب هذه القيم مباشرة في docker-compose.yml، خصوصًا الأسرار، لأن الملف يذهب إلى Git. الحلّ هو ملف .env في المجلد نفسه، يحوي أزواج المفتاح=القيمة، وتشير إليه من داخل الملف بصيغة ${VARIABLE}. لاحظ في ملف المثال أننا كتبنا ${DB_PASSWORD} في موضعين، وتأتي قيمته الفعلية من ملف .env كهذا:
DB_PASSWORD=a-strong-random-password
حين تشغّل docker compose up، تقرأ Compose ملف .env تلقائيًا وتعوّض كل ${DB_PASSWORD} بقيمته. الفائدة مزدوجة: تفصل الإعدادات عن التعريف، وتُبقي الأسرار خارج الملف المرفوع. القاعدة الذهبية: أضف .env إلى .gitignore كي لا يُرفع أبدًا، واحتفظ بملف .env.example بلا قيم حقيقية ليعرف فريقك المتغيّرات المطلوبة دون كشف أسرارها. هذا الفصل بين "بنية التطبيق" (في docker-compose.yml المُشارَك) و"أسراره" (في .env المحلّي) ممارسة أساسية.
تحتاج موارد مخصّصة وتحكّمًا كاملًا؟
خطط VPS من wpressly بموارد مضمونة وصلاحية root، مُدارة وغير مُدارة، مع دعم عربي للإعداد والتأمين من اليوم الأول.
استعرض خطط VPSمتى تستخدم Compose ومتى تحتاج غيره؟
Docker Compose أداة رائعة، لكنها ليست الأداة الصحيحة لكل موقف. فهم حدودها يوفّر عليك تعقيدًا لا تحتاجه أو محدوديّة تعرقلك. الجدول التالي يوضّح متى تختار كل نهج:
| الأداة | متى تناسب | مثال |
|---|---|---|
حاوية واحدة (docker run) | تطبيق بسيط بجزء واحد بلا حالة | خادم ويب ثابت أو أداة سطر أوامر |
| Docker Compose | ستاك متعدّد الحاويات على خادم واحد | ويب + قاعدة بيانات + كاش على VPS |
| Docker Swarm | توزيع بسيط على عدّة خوادم | عدّة نسخ من خدمة مع موازنة حِمل |
| Kubernetes | تنسيق واسع مع تحجيم تلقائي وتعافٍ | منصّة كبيرة بعشرات الخدمات وفرق متعدّدة |
القاعدة العملية: إن كان تطبيقك حاوية واحدة عديمة الحالة، فحاوية docker run بسيطة قد تكفي. أمّا حين تصبح عدّة حاويات متعاونة على خادم واحد — وهي الحالة الأغلب لمن يستضيف تطبيقاته على VPS — فـCompose هو الخيار الأمثل بلا منازع. يبدأ الأمر يتجاوز Compose حين تحتاج توزيع التطبيق على عدّة خوادم مع تحجيم تلقائي، وتعافٍ ذاتي من الأعطال، وتحديثات متدرّجة بلا توقّف. هنا يدخل التنسيق (orchestration): Kubernetes للمشاريع الكبيرة المعقّدة، أو Docker Swarm لحاجة أبسط بمنحنى تعلّم أقلّ.
لكن لا تقفز إلى Kubernetes قبل أن تحتاجه فعلًا؛ فهو تعقيد ضخم لا يبرّره تطبيق يعمل بسعادة على VPS واحد بـCompose. الغالبية العظمى من المشاريع — بما فيها متاجر ووردبريس ومنصّات SaaS الصغيرة والمتوسّطة وأدوات الأتمتة المستضافة ذاتيًا مثل تثبيت n8n على VPS — تعمل ممتازة بـCompose على خادم واحد جيّد الموارد. اترقِ إلى تنسيق أثقل فقط حين تفرض حاجة حقيقية للتوسّع الأفقي نفسها.
ما نصائح الإنتاج المهمّة في Docker Compose؟
كتابة ملف يعمل على جهازك شيء، وتشغيله بأمان على خادم إنتاج شيء آخر. إليك أهمّ ما يفصل الإعداد الهاوي عن الإنتاجي، وكلّها مطبّقة في ملف المثال السابق.
سياسات إعادة التشغيل (restart policies): بدون سياسة، لن تعود حاويتك للعمل إن تعطّلت أو أعيد إقلاع الخادم. أضف restart: unless-stopped لكل خدمة إنتاجية — يعني "أعِد التشغيل دائمًا إلا إن أوقفتها أنا يدويًا". هذا يضمن أن تطبيقك يعود تلقائيًا بعد أي عطل أو إعادة تشغيل للنظام، تمامًا كما تُدار خدمات النظام عبر systemd؛ إن أردت فهم هذا السياق راجع إدارة خدمات لينكس.
فحوص الصحّة (health checks): الحاوية قد تعمل ظاهريًا لكن تطبيقها بداخلها معطّل. healthcheck يعرّف أمرًا يفحص الصحّة الفعلية دوريًا — مثل pg_isready لقاعدة البيانات أو طلب HTTP لتطبيق ويب. حين تقرن الفحص بـdepends_on: condition: service_healthy، تضمن ألّا تبدأ خدمة قبل جاهزية ما تعتمد عليه فعليًا، لا مجرّد بدء حاويته.
حدود الموارد (resource limits): حاوية بلا حدود قد تلتهم كل ذاكرة الخادم وتُسقط البقيّة. حدّد سقفًا للذاكرة والمعالج لكل خدمة عبر مفتاح deploy.resources.limits كي لا تتسبّب خدمة واحدة في انهيار الستاك كاملًا عند تسرّب ذاكرة أو حِمل مفاجئ.
عدم كشف منافذ الخدمات الداخلية: أخطر خطأ شائع. لا تضع مفتاح ports لقاعدة البيانات أو الكاش أبدًا. الخدمات تتحدّث داخليًا عبر شبكة Compose دون أي منفذ مكشوف. كشف منفذ 5432 لقاعدة PostgreSQL على IP عام يعني أن أي روبوت على الإنترنت يمكنه محاولة الاتصال بها — وهي واحدة من أكثر ثغرات الخوادم استغلالًا.
أخطاء شائعة وكيف تشخّصها
حتى مع ملف سليم، ستقابل مشكلات عند التشغيل. إليك أكثرها شيوعًا وطريقة حلّها بسرعة.
"الخدمة تفشل في الاتصال بقاعدة البيانات": غالبًا سبب أحد أمرين. الأول: بدأ التطبيق قبل جاهزية قاعدة البيانات — الحلّ هو depends_on مع condition: service_healthy كما في المثال. الثاني: كتبت عنوان المضيف خطأً؛ تأكّد أنك تستخدم اسم الخدمة (db) لا localhost ولا IP. داخل الشبكة localhost يعني الحاوية نفسها لا قاعدة البيانات.
"المنفذ مستخدم بالفعل" (port is already allocated): خدمة أخرى على الخادم تحجز المنفذ الذي تحاول كشفه. غيّر الرقم على يسار ports (مثلًا "8081:80" بدل "8080:80")، أو أوقف الخدمة المتعارضة. استخدم docker compose ps وأدوات النظام لمعرفة من يحجز المنفذ.
"بياناتي اختفت بعد إعادة التشغيل": لم تربط حجمًا دائمًا للخدمة، أو شغّلت down -v الذي يحذف الأحجام. تأكّد من وجود مفتاح volumes في الخدمة، ومن إعلان الحجم في قسم volumes: الأعلى، ولا تستخدم -v مع down على الإنتاج.
"التغييرات في الملف لا تظهر": بعد تعديل docker-compose.yml يجب إعادة إنشاء الحاويات المتأثّرة بـdocker compose up -d (يكتشف Compose الفرق ويعيد إنشاء ما تغيّر فقط). أمّا restart وحده فيعيد تشغيل الحاوية بإعداداتها القديمة دون تطبيق التعديل.
"خطأ في صيغة YAML": المسافات البادئة حسّاسة جدًا في YAML — استخدم مسافات لا Tab، وحافظ على مستويين لكل تداخل. شغّل docker compose config قبل كل شيء؛ يتحقّق من الملف ويطبع الصيغة النهائية المفسّرة، فتكتشف أخطاء المسافات فورًا.
تحتاج موارد مخصّصة وتحكّمًا كاملًا؟
خطط VPS من wpressly بموارد مضمونة وصلاحية root، مُدارة وغير مُدارة، مع دعم عربي للإعداد والتأمين من اليوم الأول.
استعرض خطط VPSالخلاصة
Docker Compose يحوّل الفوضى المرهقة لإدارة عدّة حاويات إلى ملف نصّي واحد نظيف تقرؤه وتراجعه وتشاركه. تعرّف خدماتك — كل خدمة بصورتها ومنافذها وأحجامها ومتغيّراتها — ثم تشغّل الستاك كاملًا بأمر واحد docker compose up -d. الجمال في البساطة: شبكة تلقائية تربط الخدمات باسمها فلا حاجة لعناوين IP، أحجام مُسمّاة تحفظ بياناتك بعد حذف الحاويات، وملف واحد يصير مصدر الحقيقة لبنية تطبيقك كاملة. هذا هو النموذج الأمثل لأي أحد يستضيف تطبيقاته على VPS واحد، من متاجر ووردبريس إلى أدوات الأتمتة المستضافة ذاتيًا.
تذكّر النقاط الحاسمة: استخدم الأمر الحديث docker compose بمسافة واحذف مفتاح version: المُلغى؛ اربط دائمًا الأحجام المُسمّاة لأي بيانات مهمّة؛ لا تكشف أبدًا منفذ قاعدة بياناتك للعالم؛ وأضف سياسات إعادة تشغيل وفحوص صحّة قبل الإنتاج. مع هذه الأسس، يصبح تشغيل تطبيقات معقّدة على خادمك عملية مضبوطة قابلة للتكرار بدل مغامرة يدوية هشّة. Compose لا يجعل الأمر أسهل فقط، بل يجعله موثوقًا — وهذا ما تحتاجه حين يعتمد عملك على أن يظلّ الخادم يعمل.
الأسئلة الشائعة
ما الفرق بين Docker وDocker Compose؟
Docker يشغّل حاوية واحدة عبر أمر docker run. Docker Compose طبقة فوقه تدير عدّة حاويات معًا عبر ملف docker-compose.yml واحد وأمر docker compose up. Docker هو المحرّك، وCompose هو المنسّق التصريحي فوقه.
هل docker-compose (بشرطة) لا يزال يعمل؟
لا، الأداة القديمة V1 المكتوبة بـPython أُزيلت فعليًا ولم تعد مدعومة. استخدم docker compose بمسافة (الإضافة V2 المدمجة في Docker). الصيغة الحديثة أسرع ومتوافقة مع الملفات القديمة بلا تغيير يُذكر.
هل أحتاج مفتاح version في أعلى الملف؟
لا. مفتاح version: صار مُلغى (obsolete) ويُتجاهَل تمامًا في الصيغة الحديثة، ويطبع Compose تحذيرًا إن وجده. احذفه وابدأ ملفك مباشرة بـservices:.
كيف تتصل الحاويات ببعضها في Compose؟
عبر شبكة خاصّة تنشئها Compose تلقائيًا، تصل فيها كل خدمة الأخرى باسمها كأنه اسم مضيف. فتكتب db:5432 أو cache:6379 في إعدادات تطبيقك، ويترجم Docker الاسم إلى عنوان الحاوية الصحيح دون تدخّل منك.
أين تُحفَظ بيانات قاعدة البيانات؟ في حجم مُسمّى (named volume) تربطه بمسار بيانات القاعدة داخل الحاوية. الحجم مستقلّ عن دورة حياة الحاوية، فتبقى البيانات حتى لو حذفت الحاوية وأعدت إنشاءها. لا تعتمد على نظام ملفات الحاوية الداخلي لأي بيانات دائمة.
هل يحذف docker compose down بياناتي؟
لا افتراضيًا. down يحذف الحاويات والشبكة لكن يُبقي الأحجام المُسمّاة، فبياناتك آمنة. لكن down -v يحذف الأحجام أيضًا وتضيع البيانات — استخدمه بحذر شديد ولا تشغّله على الإنتاج إلا متعمّدًا.
كيف أرقّي الصور إلى أحدث نسخة؟
شغّل docker compose pull لجلب أحدث نسخ الصور المعرّفة، ثم docker compose up -d ليعيد إنشاء الحاويات المتأثّرة فقط بالصور الجديدة. Compose يترك الخدمات غير المتغيّرة تعمل دون انقطاع.
كيف أرى أخطاء خدمة معطّلة؟
استخدم docker compose logs -f اسم-الخدمة لمتابعة سجلّاتها لحظيًا، أو docker compose ps لرؤية حالة كل الخدمات. لو كان الملف نفسه مشكوكًا فيه، فـdocker compose config يتحقّق من صحّته ويطبع الصيغة المفسّرة.
هل Compose مناسب للإنتاج أم للتطوير فقط؟ مناسب للإنتاج على خادم واحد تمامًا، وهو الخيار الأشيع لتطبيقات VPS. أضف سياسات إعادة تشغيل، فحوص صحّة، حدود موارد، ولا تكشف منافذ الخدمات الداخلية. تحتاج ما هو أثقل (Swarm أو Kubernetes) فقط عند التوزيع على عدّة خوادم بتحجيم تلقائي.
متى أنتقل من Compose إلى Kubernetes؟ حين تحتاج توزيع تطبيقك على عدّة خوادم مع تحجيم تلقائي، تعافٍ ذاتي، وتحديثات متدرّجة بلا توقّف. طالما يعمل تطبيقك بسعادة على VPS واحد، فـCompose أبسط وأكفأ، ولا يبرّر تعقيد Kubernetes الضخم قبل أن تفرض حاجة التوسّع نفسها فعلًا.