Docker Compose depends_on لا ينتظر؟ إليك الحل (2026)
٢٧ يوليو ٢٠٢٦

إن depends_on العادية تنتظر فقط حتى يعمل الحاوية (container is running) الخاصة بالتبعية، وليس حتى تصبح الخدمة بداخلها جاهزة (ready) — لذا يتسابق تطبيقك مع قاعدة البيانات وينتهي الأمر بانهياره. الحل: استخدام الصيغة الطويلة مع condition: service_healthy بالإضافة إلى healthcheck حقيقي على التبعية.12
ملخص: depends_on: [db] يبدأ db قبل web ولكنه لا ينتظر حتى يبدأ Postgres في قبول الاستعلامات. انتقل إلى الصيغة الطويلة (depends_on: { db: { condition: service_healthy } }) وامنح db فحص حالة healthcheck مثل pg_isready. اضبط start_period بقيمة كافية حتى لا يتم اعتبار قاعدة البيانات التي تبدأ ببطء غير صحية في وقت مبكر جدًا. إذا رأيت dependency failed to start: container ... is unhealthy، فهذا يعني أن فحص حالة التبعية قد فشل أو أنه مفقود. يتم احترام depends_on بواسطة Docker compose up ولكن يتم تجاهلها بواسطة Docker stack deploy الخاصة بـ Swarm.
ما ستتعلمه
- لماذا لا ينتظر
depends_onحتى تصبح خدمتك جاهزة - الفرق بين
service_startedوservice_healthyوservice_completed_successfully - كيفية إضافة
healthcheckوربط عملية التشغيل بها (مع مثال عملي) - لماذا تعلق الحاوية في حالة
health: startingولا تتقدم أبدًا - ماذا يعني
dependency failed to start: container is unhealthyوكيفية إصلاحه - قيم
intervalوtimeoutوretriesوstart_periodوstart_intervalالتي يجب استخدامها - ما إذا كان
depends_onيعمل معDocker compose runوDocker stack deploy - متى تلجأ إلى استخدام
wait-for-it.shأو إعادة المحاولة على مستوى التطبيق بدلاً من ذلك
لماذا لا ينتظر Docker compose depends_on خدمتي؟
لأن "بدأت" لا تعني "جاهزة". التوثيق الرسمي لـ Docker صريح في ذلك: "عند التشغيل، لا ينتظر Compose حتى تصبح الحاوية 'جاهزة'، بل فقط حتى تبدأ في العمل."2 مع الصيغة القصيرة، يقوم Compose بإنشاء التبعية أولاً ثم يبدأ الخدمة المعتمدة عليها فوراً — فهو لا ينتظر حتى تصبح التبعية صحية (healthy).1
هذا التمييز مهم جداً للخدمات التي تحفظ الحالة (stateful services). حاوية Postgres تبلغ عن أنها "تعمل" في غضون أجزاء من الثانية، ولكن عملية قاعدة البيانات تحتاج إلى وقت إضافي قبل أن تقبل استعلامات SQL. يحاول تطبيقك الاتصال في هذه الفجوة الزمنية ثم ينهار. هذه الصيغة القصيرة هي الفخ:
services:
web:
build: .
depends_on:
- db # only waits for the container to START, not to be READY
db:
image: postgres:18
يضمن Compose إنشاء db قبل web، وهذا كل شيء. وكما ورد في التوثيق: "مع الصيغة القصيرة، لا ينتظر Compose خدمات التبعية حتى تصبح 'صحية' قبل بدء الخدمة المعتمدة عليها."1 لانتظار الجاهزية، تحتاج إلى الصيغة الطويلة بالإضافة إلى فحص الحالة (healthcheck).
ما الفرق بين service_started و service_healthy و service_completed_successfully؟
إنها القيم الثلاث لـ condition في صيغة depends_on الطويلة، وهي تحدد معنى "تم الاستيفاء" بالنسبة للتبعية:12
service_started— حاوية التبعية قيد التشغيل. هذا يطابق الصيغة القصيرة: الترتيب فقط، دون التحقق من الجاهزية.service_healthy— أبلغ الـhealthcheckالخاص بالتبعية أنها سليمة (healthy). استخدم هذا لقواعد البيانات، والوسطاء (brokers)، والمخازن المؤقتة (caches) التي تحتاج وقتاً للإحماء.service_completed_successfully— انتهت التبعية من التشغيل وخرجت بالكود 0. استخدم هذا لمهام التهيئة أو الترحيل (migration) التي تُنفذ لمرة واحدة ويجب أن تنتهي قبل بدء التطبيق.
| الشرط | ينتظر حتى | هل يحتاج healthcheck؟ | الاستخدام النموذجي |
|---|---|---|---|
service_started | تشغيل الحاوية | لا | خدمات غير مرتبطة بقوة |
service_healthy | نجاح الـ Healthcheck | نعم | قواعد البيانات، الوسطاء، المخازن المؤقتة |
service_completed_successfully | كود الخروج 0 | لا | الترحيلات (Migrations)، الـ seeders، مهام التهيئة |
فقط service_healthy هو الذي ينتظر الجاهزية الفعلية، وهو يعمل فقط إذا كانت التبعية تحدد healthcheck. بدون ذلك، لا توجد حالة صحية لاستيفائها — وهذا هو السبب الأكثر شيوعاً لكون بوابات بدء التشغيل "لا تفعل شيئاً".1
كيف أجعل depends_on ينتظر حتى تصبح الخدمة سليمة (healthy)؟
خطوتان: أضف healthcheck إلى التبعية، ثم أشر إليها باستخدام condition: service_healthy. هذا هو المثال الرسمي لـ Docker، حيث تنتظر خدمة web وصول db لحالة سليمة، وبدء تشغيل Redis:2
services:
web:
build: .
depends_on:
db:
condition: service_healthy
restart: true
redis:
condition: service_started
redis:
image: redis
db:
image: postgres:18
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
retries: 5
start_period: 30s
timeout: 10s
الآن ينتظر Compose نجاح الـ healthcheck الخاص بـ db قبل إنشاء web.2 فحص pg_isready يعيد نتيجة "نجاح" فقط عندما يكون Postgres يقبل الاتصالات فعلياً، وبذلك يختفي سباق التشغيل. الرمز $$ يقوم بعمل escape للمتغير بحيث يمرر Compose قيمة $POSTGRES_USER إلى الشل (shell) داخل الحاوية بدلاً من معالجتها بنفسه.
الخيار الفرعي الاختياري restart: true (الذي تم تقديمه في Docker Compose 2.17.0) يعني أنه إذا تمت إعادة تشغيل db بواسطة عملية Compose صريحة مثل Docker compose restart، فسيتم إعادة تشغيل web أيضاً، ليعيد إنشاء اتصالاته.1 وهناك خيار فرعي مرتبط، وهو required: false (Docker Compose 2.20.0)، والذي يخفض درجة التبعية المفقودة من "خطأ" إلى "تحذير".1 كل هذا محدث حتى إصدار Docker Compose v5.3.1، الذي صدر في 7 يوليو 2026.3
لماذا تظل الحاوية الخاصة بي عالقة في حالة "health: starting"؟
تظل الحاوية في حالة health: starting حتى ينجح أول فحص صحي (healthcheck) أو حتى تنتهي فترة start_period وتبدأ الإخفاقات في الاحتساب. إذا لم تبدأ الخدمة التابعة أبداً وكانت التبعية عالقة في حالة "starting"، فإن الأسباب المعتادة هي:
- التبعية لا تحتوي على
healthcheckعلى الإطلاق، ولكن هناك شيء يشير إليها باستخدامcondition: service_healthy. بدون وجود فحص (probe)، لا يمكنها أبدًا الوصول إلى حالةhealthy، لذا تظل البوابة في حالة انتظار — أو تفشل — إلى الأبد.1 - أمر فحص الصحة (healthcheck command) خاطئ — الملف الثنائي (binary) غير موجود في الصورة، أو أن الاختبار لا يعيد أبدًا القيمة exit 0.
start_periodقصيرة جدًا، لذا فإن قاعدة البيانات التي تحتاج إلى 40 ثانية للتهيئة يتم تعليمها بأنها غير صحية (unhealthy) قبل أن تنتهي.
حالات الصحة في Docker هي starting، و healthy، و unhealthy، وتعتمد على كود الخروج الخاص بالفحص: 0 يعني صحي، و 1 يعني غير صحي. الإخفاقات التي تحدث خلال start_period لا تُحتسب ضمن حد إعادة المحاولة، والنجاح خلال تلك الفترة ينهي فترة السماح مبكرًا. تحقق من الحالة الحالية باستخدام Docker inspect --format '{{.State.Health.Status}}' <container> أو تابعها مباشرة عبر Docker compose ps.
ماذا يعني "dependency failed to start: container is unhealthy"؟
هذا يعني أن بوابة condition: service_healthy قد فشلت: استنفد فحص الصحة الخاص بالتبعية عدد retries (إعادة المحاولات) دون أن ينجح أبدًا (أو أن التبعية لا تحتوي على فحص صحة لتحقيق الشرط).4 يقوم Compose الحديث بالفشل سريعًا هنا بدلاً من التعليق إلى أجل غير مسمى. لإصلاح ذلك، تعامل مع التبعية نفسها:
- تأكد من أن التبعية تحتوي فعليًا على
healthcheck. إذا كانت مشار إليها بـservice_healthyولكنها لا تحدد أي فحص، قم بإضافة واحد. - قم بتشغيل الفحص يدويًا داخل الحاوية:
Docker compose exec db pg_isready -U postgres. إذا فشل هناك، فإن أمر الاختبار الخاص بك خاطئ. - ارفع قيمة
start_periodللخدمات التي تستغرق وقتًا في التشغيل. غالبًا ما تحتاج خدمة Postgres الباردة أو خدمة JVM إلى 60 ثانية أو أكثر قبل أول فحص ناجح. - خفف قيود
retries/intervalإذا كانت الخدمة صحية ولكنها غير مستقرة تحت الضغط أثناء التشغيل.
هذا الخطأ هو عرض لحالة صحة التبعية، وليس الخدمة المعتمدة عليها — قم دائمًا بتصحيح أخطاء الخدمة المذكورة في الرسالة.
ما هي قيم interval و retries و start_period التي يجب أن أستخدمها؟
ابدأ من القيم الافتراضية لـ HEALTHCHECK في Docker Engine وقم بضبطها من هناك. القيم الافتراضية هي interval 30s، و timeout 30s، و retries 3، و start_period 0s.5 القيمة الافتراضية لـ start_period التي تساوي صفرًا هي السبب في اعتبار قواعد البيانات غير صحية عند التشغيل البارد — قم بتعيينها دائمًا.
نقاط بداية منطقية حسب نوع الخدمة:
- قواعد البيانات (Postgres, MySQL):
interval: 10s,timeout: 5s,retries: 5,start_period: 30s–60s. - واجهات HTTP APIs: اختبار
curl -f http://localhost:PORT/health,interval: 15s,timeout: 5s,retries: 3,start_period: 20s. - خدمات JVM الثقيلة / خدمات الذاكرة المخبئية الباردة: ارفع
start_periodإلى 60s–120s.
هناك أيضًا start_interval، والتي تحدد عدد مرات تشغيل الفحص خلال start_period — بحيث يمكنك الفحص كل ثانية عند التشغيل ثم العودة إلى interval الأبطأ بمجرد أن تصبح الخدمة صحية. يتطلب ذلك Docker Compose 2.20.2+ و Docker Engine 25.0+:6
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 30s # steady-state cadence
timeout: 5s
retries: 5
start_period: 60s # grace window; failures here don't count
start_interval: 1s # poll every 1s during the grace window
يفضل استخدام صيغة exec ["CMD", "bin", "arg"] عندما لا تحتاج إلى shell، وصيغة ["CMD-SHELL", "..."] عندما تحتاج إلى pipes أو متغيرات بيئة. استخدم test: ["NONE"] لتعطيل فحص الحالة (healthcheck) الذي ورثته الصورة من صورتها الأساسية.5
هل يعمل depends_on مع Docker compose run و Docker stack deploy؟
ليس في كل مكان. depends_on هي ميزة تنسيق خاصة بـ Compose، وهناك سياقان لا يلتزمان بها بنفس الطريقة التي يفعلها Docker compose up:
Docker stack deploy(وضع Swarm) يتجاهلdepends_onتماماً — دون أي تحذير. لا يمتلك Swarm مفهوماً لبدء التشغيل المرتب، لذا يتم إسقاط التبعيات المعلنة بصمت.7 في Swarm، يجب عليك جعل الخدمات مرنة تجاه التبعيات المفقودة بدلاً من ذلك.Docker compose run <service>يبدأ خدمات depends_on ولكن لا ينتظر تقارير فحص الحالة لتكون سليمة (healthy)، بينما يقوم--no-depsبتخطي بدء تشغيلها تماماً.8
كما أن depends_on تقوم بترتيب الخدمات داخل مشروع Compose واحد فقط — ولا يمكنها ترتيب الحاويات التي تشغلها بأوامر Docker run منفصلة أو في مشروع آخر.
مقارنة بين depends_on و wait-for-it.sh وإعادة المحاولة على مستوى التطبيق (app-level retries)
الثلاثة يحلون مشكلة "انتظار الجاهزية"، ولكن في طبقات مختلفة:
depends_on+service_healthyهو الخيار التصريحي المدمج. وهو الخيار الافتراضي الصحيح لأن فحص الجاهزية يكون مرتبطاً بالتبعية وكل تابع يرثه.2- سكربتات التغليف (Wrapper scripts) مثل
wait-for-it.sh، أوwait-for، أوdockerizeتقوم بالتوقف عند منفذ TCP مفتوح قبل تشغيل تطبيقك. هي بسيطة، ولكن فتح المنفذ أضعف من فحص الحالة الحقيقي: فمثلاً Postgres يقبل اتصالات TCP قبل أن يقبل الاستعلامات، لذا قد ينجح فحص المنفذ في وقت مبكر جداً. - إعادة المحاولة على مستوى التطبيق (Application-level retries) هي الطبقة الأكثر متانة. إرشادات Docker طويلة الأمد هي جعل التطبيق مرناً — إعادة الاتصال مع تراجع تدريجي (backoff) — لأن ترتيب بدء التشغيل وحده لا يمكنه ضمان الجاهزية عند إعادة التشغيل في منتصف العمل. استخدام قاطع دائرة (circuit breaker) أو غلاف إعادة محاولة ضروري في بيئة الإنتاج بغض النظر عن إعدادات Compose الخاصة بك.
أفضل الممارسات في 2026: استخدم service_healthy لبدء تشغيل نظيف محلياً وفي بيئة CI، ولا تزال بحاجة لإضافة منطق إعادة المحاولة في التطبيق حتى لا يتوقف service عند حدوث failover لقاعدة البيانات في الساعة 3 فجراً.
الخلاصة
إن depends_on بمفردها تقوم فقط بترتيب بدء تشغيل الحاويات — وهي لا تنتظر أبداً الجاهزية. انتقل إلى الصيغة الطويلة، واعتمد على condition: service_healthy، وامنح كل تبعية ذات حالة (stateful) فحص صحة healthcheck حقيقي مع start_period سخي، وحافظ على منطق إعادة المحاولة (retry logic) داخل التطبيق للحالات التي لا يستطيع Compose تغطيتها. إذا كنت تقوم بربط هذا في بيئة إنتاج، فادمجها مع إعداد Docker Compose HTTPS محصن، وتعامل مع فحوصات الصحة بنفس الطريقة التي تتعامل بها مع فحوصات الجاهزية في Kubernetes عند النشر بدون توقف. وبالنسبة لتبعيات قواعد البيانات تحديداً، فإن وجود طبقة تجميع اتصالات (connection-pooling layer) مرنة لا يقل أهمية عن ترتيب بدء التشغيل.
Footnotes
-
وثائق Docker — "تعريف الخدمات في Compose"،
depends_on(الصيغة القصيرة والطويلة؛ تم تقديمrestartفي الإصدار 2.17.0، وrequiredفي الإصدار 2.20.0). https://docs.Docker.com/reference/compose-file/services/#depends_on ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 -
وثائق Docker — "التحكم في ترتيب التشغيل والإغلاق في Compose" (Compose لا ينتظر حتى يصبح الحاوية "جاهزة"؛
service_started/service_healthy/service_completed_successfully؛ مثال نموذجي). https://docs.Docker.com/compose/how-tos/startup-order/ ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 -
إصدارات GitHub لـ Docker/compose — الإصدار v5.3.1، صدر في 7 يوليو 2026 (المستقر حالياً). https://GitHub.com/Docker/compose/releases/tag/v5.3.1 ↩
-
مشكلة Docker/compose رقم 11474 وتقارير المجتمع — "فشل بدء التبعية: الحاوية ... غير سليمة (unhealthy)" تحدث عندما يفشل فحص الحالة (healthcheck) لتبعية
service_healthyفي محاولات إعادة التشغيل أو يكون مفقوداً. https://GitHub.com/Docker/compose/issues/11474 ↩ ↩2
وثائق Docker — مرجع Dockerfile HEALTHCHECK (القيم الافتراضية: الفاصل الزمني 30 ثانية، المهلة 30 ثانية، عدد المحاولات 3، فترة البدء 0 ثانية؛ الخروج 0 = سليم، 1 = غير سليم؛ CMD مقابل CMD-SHELL مقابل NONE). https://docs.Docker.com/reference/dockerfile/#healthcheck ↩ ↩2 ↩3 ↩4
مشكلة Docker/compose رقم 10830 — يتطلب start_interval إصدار Docker Compose 2.20.2+ وإصدار Docker Engine 25.0+. https://GitHub.com/Docker/compose/issues/10830 ↩
مشكلة moby/moby رقم 40364 ومشكلة Docker/compose رقم 13819 — يتم تجاهل depends_on بواسطة Docker stack deploy في وضع Swarm. https://GitHub.com/moby/moby/issues/40364 ↩ ↩2
مشكلة Docker/compose رقم 7681 — يقوم Docker compose run بتشغيل التبعيات ولكنه لا ينتظر فحوصات الحالة (healthchecks) الخاصة بها؛ بينما يقوم --no-deps بتخطيها. https://GitHub.com/Docker/compose/issues/7681 ↩