المشكلة: تحضير لازم قبل إقلاع الخدمة
في درس تطبيق متعدّد الحاويات تعرّفت على depends_on — لكن لاحظنا هناك أنه يضبط ترتيب البدء فقط، لا جاهزية الخدمة. فماذا لو احتجت فعليًا تنفيذ خطوة تحضيرية (تشغيل migration لقاعدة البيانات، انتظار توفّر خدمة خارجية، توليد ملف إعداد) قبل أن تبدأ الخدمة نفسها؟
الحل التقليدي كان تعريف خدمة منفصلة كاملة في compose.yaml تُنفّذ المهمة وتخرج، ثم تجعل خدمتك الأساسية تنتظرها بـ:
services:
migrate:
build: .
command: npm run migrate
app:
build: .
depends_on:
migrate:
condition: service_completed_successfully
يعمل هذا الأسلوب، لكن يحمّلك عبء تعريف خدمة كاملة (بصورتها الخاصة، وربما شبكتها) فقط لأجل خطوة تحضير واحدة سريعة.
الحل المدمج: pre_start
Docker Compose أضاف مفتاح pre_start ضمن تعريف الخدمة نفسها: سلسلة خطوات تهيئة تُنفَّذ في حاويات مؤقّتة منفصلة قبل أن تُقلَع حاوية الخدمة الرئيسية — دون الحاجة لتعريف خدمة كاملة إضافية بالملف.
services:
app:
build: .
pre_start:
- command: npm run migrate
- command: ["sh", "-c", "until nc -z redis 6379; do sleep 1; done"]
image: busybox
command: npm start
الصياغة: command إلزامي، image اختياري
كل خطوة تحت pre_start تقبل command (إلزامي) — الأمر الذي يُنفَّذ. أما image فاختياري: لو أغفلته تستخدم الخطوة صورة الخدمة نفسها تلقائيًا (مناسب لخطوات تعتمد على أدوات مشروعك، كـ migration). لو احتجت أداة مختلفة تمامًا (مثل busybox لفحص جاهزية منفذ شبكة) حدّد image صراحةً كما بالمثال أعلاه.
آلية التنفيذ
- كل خطوة تُنفَّذ في حاوية مؤقّتة منفصلة خاصة بها، لا داخل حاوية الخدمة الرئيسية.
- الخطوات تُنفَّذ بالترتيب المُعلَن تسلسليًا — الخطوة التالية لا تبدأ قبل نجاح سابقتها.
- حاوية الخدمة الرئيسية لا تُقلَع إطلاقًا إلا بعد خروج كل خطوة بكود
0(نجاح). أي خطوة تخرج بكود غير صفري توقف إقلاع الخدمة وكل ما يعتمد عليها عبرdepends_on.
💡 لو خطوة
pre_startنجحت سابقًا بنفس تعريفها بالضبط، Compose لا يعيد تنفيذها عندupتالية ولا عند إعادة تشغيل الحاوية بموجبrestart— فقط تعريف تغيّر فعليًا يُعاد تشغيله.
⚠️
pre_startتختلف جوهريًا عنdepends_onمعcondition: service_completed_successfully: الأولى خطوة تهيئة مملوكة لنفس الخدمة بلا تعريف خدمة منفصلة، والثانية تنسيق بين خدمتين مستقلّتين بالملف. اخترpre_startلخطوة تحضير بسيطة تخص خدمة واحدة، واحتفظ بالخدمة المنفصلة لو احتجت هذه الخطوة نفسها من خدمات أخرى أو أردت مراقبتها ككيان مستقل بـdocker compose ps.
هل يحتاج مشروعي هذا فعليًا؟
| الحالة | الأنسب |
|---|---|
| خطوة تحضير سريعة تخص خدمة واحدة فقط (migration، انتظار منفذ) | pre_start |
| خطوة تحضير تريد مراقبتها/إعادة استخدامها من عدّة خدمات | خدمة منفصلة + depends_on مع condition |
| فحص استمراري لصحة الخدمة بعد إقلاعها | HEALTHCHECK (راجع مرجع HEALTHCHECK) — مختلف تمامًا عن pre_start الذي يعمل قبل الإقلاع مرّة واحدة |
🎯 التالي: خلاصة مسار Docker