تخيل هذا السيناريو: إعداد Docker Compose يعمل بشكل مثالي على جهازك المحلي. تدفع الكود إلى CI، وفجأة تفشل جميع اختبارات التكامل مع خطأ Connection refused.
حاوية قاعدة البيانات "قيد التشغيل". حاوية API "سليمة". تبدأ عملية الاختبار. ثم لا تستطيع الاتصال بالخدمة التي تحتاجها.
يبدو هذا الفشل عشوائياً حتى تتذكر شيئاً واحداً: شبكات Docker المحلية وشبكات Docker في CI ليست نفس البيئة.
الإعداد المحلي يخدعك
على جهازك، قد تتصل بـ Postgres على localhost:5432.
داخل شبكة Compose، يجب أن تتصل حاوية أخرى عادةً بـ postgres:5432، حيث postgres هو اسم الخدمة.
في CI، قد يكون مشغل الاختبار:
- داخل شبكة Compose
- خارج شبكة Compose على المضيف
- داخل حاوية خدمة CI
- داخل منفذ Docker متداخل
تستخدم هذه الحالات الأربع أسماء مضيف مختلفة.
لهذا السبب يمكن أن يكون رابط الاتصال "صحيحاً" محلياً وخاطئاً في خط الأنابيب.
أولاً، حدد أين تعمل عملية الاختبار
قبل تغيير المنافذ، اسأل سؤالاً واحداً:
هل يعمل أمر الاختبار داخل خدمة Compose أم على مضيف CI؟
إذا كانت الاختبارات تعمل داخل Compose:
DATABASE_URL=postgres://user:pass@postgres:5432/appإذا كانت الاختبارات تعمل على مضيف CI وقامت Compose بنشر المنفذ:
DATABASE_URL=postgres://user:pass@127.0.0.1:5432/appإذا كانت الاختبارات تعمل في حاوية CI منفصلة، فقد لا يعمل أي منهما حتى يتم تكوين شبكة خدمة منصة CI.
يعتمد اسم المضيف الصحيح على مكان وجود مشغل الاختبار. تعمل أسماء الخدمات داخل شبكة Compose. تعمل منافذ localhost المنشورة من المضيف.
لا تثق في depends_on كجاهزية
يمكن لـ depends_on التحكم في ترتيب البدء. لكنه لا يضمن أن Postgres أو Redis أو تطبيقك جاهز لقبول الاتصالات.
النسخة السيئة الشائعة:
services:
api:
depends_on:
- postgresهذا يعني فقط أن حاوية postgres تبدأ قبل api. لا يعني أن الترحيلات تم تشغيلها. لا يعني أن TCP جاهز. لا يعني أن قاعدة البيانات قبلت المصادقة.
استخدم فحوصات الصحة أو سكريبت انتظار صريح.
services:
postgres:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
timeout: 5s
retries: 12
api:
depends_on:
postgres:
condition: service_healthyهذا لا يحل كل مشكلة منصة CI، لكنه يزيل السباق الأكثر شيوعاً.
