3.1ملف compose.yml : الخدمات (services) والمنافذ (ports) والمجلدات (volumes)
الأساسي في 30 ثانية
- صرّح بالحالة المطلوبة، لا بالأوامر : كل خدمة (service) لها صورتها (image) (أو بناؤها build)، ومنافذها، ومتغيراتها، ومجلداتها (volumes) وشبكتها (network) — كومبوز ينشئ كل شيء من هذا الملف المُصدَر.
- خدمة واحدة = صورة + منافذ + بيئة + مجلدات : المنافذ (ports) بين علامتي تنصيص ("8080:80")، والمجلدات والشبكات تُصرَّح في الأسفل وتُنشَأ تلقائيا، وdepends_on لترتيب بدء التشغيل.
- YAML : الإزاحة هي القانون : مسافات، ولا علامات جدولة أبدا، ومسافتان لكل مستوى — docker compose config يتحقق ويعرض الملف المحلول قبل أي إطلاق.
حالة مصغرة مسلسلة — إدير يستبدل أسطر الـ 200 حرف
إدير، ديفوبس مبتدئ في الجزائر، يطلق نموذج فريقه بسطري docker run من 200 حرف منسوخين من مذكرة — منفذ منسي، والعرض يفشل. يكتب compose.yml من 25 سطرا (وب + قاعدة، ومجلد مسمى، و depends_on)، ويتحقق منه مع docker compose config، ويلتزم به : من الآن، ملف واحد يُعتَمَد عليه وكل الفريق يطلق التطبيق نفسه. انتهت وثيقة « كيف نطلق » المروية من الذاكرة.
هذه الحالة نفسها تعود في الدرس والمخطط والنص الجاهز وتحدي 48 ساعة.
1. التصريح بدل الأمر : الملف الذي يُعتَمَد عليه
يستبدل كومبوز (Compose) أوامر docker run خاصتكم بملف مُصدَر : نصف فيه الحالة المطلوبة (الحالة المرغوبة) للتطبيق (أي خدمات، وأي منافذ، وأي مجلدات، وأي شبكات) وكومبوز ينشئ كل شيء — الشبكات والمجلدات والحاويات (containers) بالترتيب. انتهت أسطر الـ 200 حرف المنسوخة من الذاكرة : فـ compose.yml مُلتزَم به، ويُراجَع كالكود، وهو المُشغِّل الرسمي للتطبيق. إنه العبور من الفعل اليدوي إلى التصريحي.
كل خدمة (service) (وب، وقاعدة بيانات bdd، وذاكرة cache…) تصرح بصورتها (image) (أو بنائها (build) المحلي)، ومنافذها (ports) ("8080:80"، وعلامتا التنصيص واجبتان في YAML)، وبيئتها (environment)، ومجلداتها (volumes) (pgdata:/var/lib/postgresql/data) وشبكتها. وdepends_on يثبت ترتيب بدء التشغيل بين الخدمات. والمجلدات والشبكات تُصرَّح في الأسفل من الملف : ينشئها كومبوز تلقائيا عند أول up.
2. YAML : الإزاحة هي القانون، و config هو الحَكَم
يتكلم كومبوز YAML : مسافات ولا علامات جدولة أبدا، ومسافتان لكل مستوى، و« : » متبوعة بمسافة. فالغالبية الساحقة من أخطاء كومبوز تأتي من هنا : إزاحة مكسورة، ومنافذ دون علامتي تنصيص (8080:80 يُقرَأ عددا ستينيا !)، و« : » دون مسافة. اكتبوا ببطء، وأزيحوا بنظافة : فذلك 80 % من العمل.
المنعكس قبل كل إطلاق : docker compose config. يتحقق هذا الأمر من الملف ويعرض كومبوز المحلول — أو الخطأ الدقيق مع سطره. فإدير لا يطلق أبدا up على ملف غير مُتحقق منه : أولا config، ثم up. ملف واحد لكل تطبيق، مُلتزَم به، ومُتحقق منه : قابل لإعادة الإنتاج في كل مكان.
🔎 مثال ملموس — وب + قاعدة في 25 سطرا، مُتحقق منه قبل الإطلاق
يكتب إدير أول ستاك (stack) وب + قاعدة له، وكلمة السر في متغير.
services:
web:
image: nginx:1.27
ports: ["8080:80"]
depends_on: [bdd]
networks: [front]
bdd:
image: postgres:16.4
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes: [pgdata:/var/lib/postgresql/data]
networks: [front]
volumes:
pgdata:
networks:
front:
# ثم : docker compose config (للتحقق)، وكلمة السر في .env غير مُلتزَم به.
القراءة : خدمتان، ومجلد دائم واحد، وشبكة واحدة، وترتيب بدء تشغيل.
الدرس : هذا الملف المُلتزَم به يعوض كل وثيقة « كيف نطلق ».
مفردات القسم
- خدمة (Service)
- حاوية (container) موصوفة في كومبوز (وب، وقاعدة bdd، وذاكرة cache) : صورة (image) أو بناء (build)، ومنافذ، ومتغيرات، ومجلدات، وشبكة.
مثال بسيط: خدمة الوب عند إدير (nginx، والمنفذ 8080 منشور).
ما ليس كذلك: سطر docker run منسوخ من الذاكرة.
رد فعل: دور تطبيقي واحد = خدمة واحدة مُصرَّح بها.
- depends_on
- ترتيب بدء التشغيل بين الخدمات : يطلق القاعدة قبل الوب، دون انتظار حتى تجهز.
مثال بسيط: الوب web يعتمد depends_on على bdd : تبدأ القاعدة أولا.
ما ليس كذلك: ضمان بأن القاعدة متوفرة.
رد فعل: رتب مع depends_on، ووثّق مع healthcheck وإعادة المحاولة.
- مجلد كومبوز (Volume Compose)
- مجلد مسمى مُصرَّح به في أسفل الملف ويُنشَأ تلقائيا : ديمومة البيانات.
مثال بسيط: المجلد pgdata مركب على /var/lib/postgresql/data.
ما ليس كذلك: بيانات سجينة حاوية زائلة.
رد فعل: كل بيانات تُحفَظ = مجلد مسمى.
- config
- أمر يتحقق من كومبوز ويعرض الملف المحلول — أو الخطأ الدقيق.
مثال بسيط: إدير يمرر config قبل كل up.
ما ليس كذلك: up أعمى على YAML مكسور.
رد فعل: أولا config، ثم up، دائما.
- YAML
- صيغة compose.yml المُزاحة : مسافات (ولا علامات جدولة أبدا)، و« : » + مسافة، والمنافذ بين علامتي تنصيص.
مثال بسيط: "8080:80" مُقتبَس، ومسافتان لكل مستوى.
ما ليس كذلك: علامات جدولة ومنافذ غير مُقتبَسة.
رد فعل: مسافات، اثنتان لكل مستوى، وعلامتا تنصيص على المنافذ.
في التطبيق — كتابة أول ستاك (stack) لكم
- أنشئوا مجلدا مع compose.yml وب + قاعدة أعلاه (كلمة السر في ${POSTGRES_PASSWORD}، و .env محلي غير مُلتزَم به).
- تحققوا منه مع docker compose config : اقرؤوا الملف المحلول المعروض.
- صححوا حتى صفر خطأ (الإزاحة، وعلامتا تنصيص المنافذ، و« : » + مسافة).
- التزموا بالملف : فهو من الآن مُشغِّلكم الرسمي، ويُراجَع كالكود.
نص جاهز للقول — عرض كومبوز على الفريق (30 ثانية)
« انتهت أسطر run من 200 حرف : فـ compose.yml واحد يصف وب + قاعدة — المنافذ والمجلد وترتيب بدء التشغيل — مُتحقق منه مع config ومُلتزَم به. كل واحد يطلق التطبيق نفسه تماما، دون مذكرة. »
يُقال في اجتماع الفريق ؛ واعرضوا المحلول config.
نموذج نسخ-لصق — ستاك وب + قاعدة صالح
services:
web:
image: nginx:1.27
ports:
- "8080:80"
depends_on:
- bdd
networks:
- front
bdd:
image: postgres:16.4
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- front
volumes:
pgdata:
networks:
front:
# .env محلي (غير مُلتزَم به) : POSTGRES_PASSWORD=<قيمة محلية>
مستقل
يكفي compose.yml مُتحقق منه مع config وعرضكم يطلق في كل مكان بتماثل.
مقاولة صغيرة في الجزائر
يلتزم إدير بالستاك وب + قاعدة : يطلق الفريق النموذج نفسه دون مذكرة.
فريق منتج
افرضوا config أخضر + التزام compose قبل كل عرض مشترك.
مبتدئ
هذا الأسبوع : ستاك وب + قاعدة مُتحقق منه بزر config، ثم مُلتزَم به.
✓ ممارسات جيدة
- ضعوا المنافذ بين علامتي تنصيص في YAML ("8080:80")، منهجيا.
- مرروا docker compose config قبل كل up، واقرؤوا المحلول.
- ملف واحد لكل تطبيق، مُلتزَم به ويُراجَع كالكود.
✗ أخطاء شائعة
- علامات الجدولة في YAML : مسافات فقط، اثنتان لكل مستوى.
- منافذ دون علامتي تنصيص (8080:80 يصبح عددا ستينيا).
- الاعتقاد بأن depends_on ينتظر حتى تجهز القاعدة (فلا يفعل سوى الترتيب).
النقاط الأساسية
- كومبوز يصف الحالة المطلوبة، ودوكر ينفذ : والملف المُصدَر يعوض أسطر run.
- خدمة (SERVICE) = صورة (أو بناء) + منافذ مُقتبَسة + بيئة + مجلدات + depends_on للترتيب.
- YAML : مسافات، ولا علامات جدولة أبدا، و« : » + مسافة — 80 % من الأخطاء تأتي من هنا.
- المجلدات (VOLUMES) والشبكات (RÉSEAUX) مُصرَّح بها في الأسفل، وتُنشَأ تلقائيا ؛ و config يتحقق قبل up.
تحدي 48 ساعة — طبّق غدا
استبدلوا إطلاقا يدويا واحدا بـ compose.yml مُتحقق منه (config) ومُلتزَم به، مع كلمة السر في متغير.
- تطبيقي موصوف في ملف مُصدَر واحد.
- منافذي مُقتبَسة و YAML خاصتي دون علامات جدولة.
- الـ config عندي أخضر قبل كل up.
أسئلة شائعة
هل ينتظر depends_on حتى تجهز القاعدة ؟
لا : فـ depends_on يرتب بدء التشغيل (تبدأ القاعدة قبل الوب) دون انتظار توفرها. وإذا اتصل الوب مبكرا جدا، يفشل : فالمتانة تأتي من فحوص الصحة (healthchecks) وإعادة المحاولة التطبيقية (retries) (المستوى 4.2). احفظوا : الترتيب ≠ الانتظار. ولنموذج بسيط، تكفي إعادة إطلاق الوب ؛ ولستاك حقيقي، حلقة إعادة اتصال عند بدء التشغيل جهة التطبيق.
هل نُصدِر compose.yml، وأين نضع الأسرار ؟
نعم، دائما : فـ compose.yml هو المُشغِّل الرسمي للتطبيق، ويُراجَع كالكود ويُلتزَم به. ولا تظهر الأسرار فيه أبدا ثابتة : استعملوا متغيرات (${POSTGRES_PASSWORD}) المغذاة بـ .env محلي غير مُلتزَم به (أو سر مُدار في الإنتاج)، وصَدِّروا .env.example بقيم وهمية لتوثيق المفاتيح المنتظرة.
ماذا يتحقق منه « docker compose config » قبل أي إطلاق ؟