SSD Nodes Learn 8GB RAM — $66/سنة
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-08-01

شرح شبكات Docker Compose: DNS والمنافذ ووضع host

تعرّف إلى شبكة المشروع الافتراضية، وDNS باسم الخدمة، ومتى يفيد host، وكيف تشارك شبكة بين مشروعين، ولماذا قد يتجاوز المنفذ المنشور UFW.

ما الذي ينشئه Compose قبل بدء تطبيقك

تبدأ شبكات Docker Compose بقاعدة واحدة: ينشئ docker compose up شبكة خاصة بالمشروع، ويربط كل خدمة بها، ويتيح لهذه الخدمات الوصول إلى بعضها باستخدام اسم الخدمة. لا تحتاج إلى كتابة سطر networks: واحد لتحقيق ذلك. ينشأ معظم الالتباس حول شبكات Compose من عدم معرفة أن الشبكة الافتراضية موجودة مسبقًا.

إليك ملفًا صغيرًا. احفظه باسم compose.yaml داخل دليل يسمى shop.

services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example

شغّله وتحقق مما أنشأه Docker:

docker compose up -d
docker network ls

تحتوي القائمة الآن على شبكة باسم shop_default. يسميها Compose باسم <project>_default، ويكون اسم المشروع افتراضيًا هو اسم الدليل بأحرف صغيرة. يمكنك تغييره باستخدام docker compose -p myproject up -d أو باستخدام name: myproject في المستوى الأعلى من الملف. برنامج التشغيل هو bridge، وهو مفتاح تبديل افتراضي داخل المضيف. يحصل كل حاوية على عنوان ضمن شبكة فرعية خاصة، وتُترجم حركة الخروج إلى عنوان المضيف عند مغادرتها.

يحذف docker compose down تلك الشبكة مرة أخرى. لذلك قد تمنع حاوية قديمة من مشروع سابق حذف الشبكة: يرفض Docker العملية مع error while removing network: network shop_default has active endpoints، ويكون الحل بإيقاف الحاوية التي ما زالت متصلة بها أو إزالتها.

إذا كان Compose جديدًا عليك، فمن المفيد قراءة بنية ملف Compose وأوامر دورة الحياة أولًا، لأن كل ما يلي يفترض قدرتك على بدء مشروع وإيقافه.

حل أسماء DNS باسم الخدمة هو الجزء الذي يفوّته المبتدئون

على أي شبكة يعرّفها المستخدم، يشغّل Docker خادم DNS مضمّنًا تراه كل حاوية في 127.0.0.11. يحلّ أسماء الخدمات إلى عناوين الحاويات الحالية. لذلك يصل web إلى قاعدة البيانات باسم المضيف db، عبر المنفذ 5432، من دون أي إعداد.

docker compose exec web getent hosts db

يطبع ذلك سطرًا مثل 172.18.0.2 db. إذا لم يطبع شيئًا، فهاتان الخدمتان ليستا على الشبكة نفسها.

الخطأ الذي يرتكبه الجميع تقريبًا مرة واحدة هو استخدام localhost في إعداد التطبيق. داخل الحاوية، يشير localhost إلى تلك الحاوية، وليس إلى المضيف ولا إلى الخدمة الأخرى. وتعرض عملاء Postgres الخطأ بوضوح:

could not connect to server: Connection refused
	Is the server running on host "localhost" (127.0.0.1) and accepting
	TCP connections on port 5432?

يجب أن تكون سلسلة الاتصال postgresql://postgres:example@db:5432/postgres. جزء المضيف هو اسم الخدمة.

هناك تفصيلان يوفران الوقت لاحقًا. تُحلّ الأسماء إلى ما يعمل حاليًا، لذلك يمنح docker compose up -d --scale web=3 اسمًا واحدًا مع ثلاثة عناوين، وسيثبت العميل الذي يخزّن DNS مؤقتًا إلى الأبد نفسه على حاوية متوقفة. كذلك، لا توفّر شبكة bridge القديمة، التي يستخدمها docker run البسيط من دون --network، حلًا للأسماء على الإطلاق. لذلك لا تتطابق الإرشادات المتعلقة بارتباطات الحاويات من عام 2016 مع ما تراه.

لا تحتاج إلى ports: لربط خدمتين

ينشر ports: منفذ حاوية على المضيف. ويُستخدم لحركة الشبكة الواردة من خارج Docker. ولا علاقة له بحركة الشبكة بين الخدمات، التي تعمل أصلًا عبر نطاق المنافذ بالكامل على شبكة المشروع.

لذلك فإن ports: - "5432:5432" الذي يضيفه كثيرون إلى خدمة قاعدة البيانات لا يفيد، بل يسبب ضررًا حقيقيًا: إذ يكشف Postgres على الواجهة العامة للخادم. احذفه. إذا أردت الوصول إليه من حاسوبك المحمول لإجراء عملية ترحيل، فاربطه بواجهة loopback باستخدام "127.0.0.1:5432:5432"، ثم صِل إليه عبر نفق SSH. يوضّح كيفية عمل المنافذ والم services التي تستمع على Linux الفرق بين مقبس يستمع، ومنفذ منشور، وقاعدة جدار ناري.

يُستخدم expose: للتوثيق فقط في Compose. ولا يفتح أي شيء، لأنه لم يكن هناك ما يُغلق بين الحاويات على الشبكة نفسها.

متى يكون network_mode host مناسبًا وما تكلفته

يلغي وضع المضيف مساحة أسماء الشبكة الخاصة بالحاوية، ويتيح للعملية استخدام واجهات المضيف مباشرة.

services:
  probe:
    image: alpine:3.20
    network_mode: host
    command: sleep infinity

توجد أسباب فعلية لاستخدامه. لا تستطيع عملية تحتاج إلى رؤية حركة البث أو البث المتعدد على الشبكة المحلية، مثل اكتشاف الأجهزة لخادم وسائط أو موزّع أتمتة منزلية، رؤية هذه الحركة من خلف جسر، لأن الجسر لا يمررها إلى الحاوية. ويحتاج عامل مراقبة يقرأ عدادات واجهات المضيف إلى واجهات المضيف. كما أنك تتجاوز خطوة ترجمة العناوين، وهذا مهم عند معدلات الحزم المرتفعة.

التكاليف محددة.

يتوقف ports: عن العمل. يحذّر Docker من تجاهل المنافذ المنشورة عند استخدام وضع شبكة المضيف، وتربط الحاوية أي منافذ تربطها العملية فيها. تتعارض حاويتان تعملان بوضع المضيف وتريدان المنفذ 8080، وتتوقف الثانية مع bind: address already in use.

ينتهي حل الأسماء باستخدام اسم الخدمة في الاتجاهين. لا تكون الحاوية على شبكة المشروع، لذلك لا يمكنها حل db، ولا تستطيع الخدمات الأخرى حل اسمها. ولا تصل إليها إلا عبر المنافذ المنشورة على المضيف، وعادةً عند 127.0.0.1.

ينتهي العزل. تستمع عملية تربط 0.0.0.0 داخل حاوية تعمل بوضع المضيف على كل واجهة في خادمك، بما في ذلك الواجهة العامة، تمامًا مثل حزمة مثبّتة باستخدام apt. ولهذا الأمر فائدة واحدة: تمر هذه الحركة عبر مسار الإدخال المعتاد، لذلك تنطبق عليها قواعد UFW، بخلاف المنافذ المنشورة.

وضع المضيف هو ميزة في Linux Docker Engine. لا يدعمه Docker Desktop إلا بدءًا من الإصدار 4.34، وبعد تمكينه فقط، مع قيود إضافية تمنع الحاويات من ربط عناوين IP الخاصة بالمضيف، ولا تعالج إلا TCP وUDP. إذا كان نصف فريقك يستخدم خوادم Linux والنصف الآخر يستخدم Docker Desktop، فتوقع أن يتصرف الملف نفسه بشكل مختلف.

استخدم وضع المضيف عندما تحتاج إلى واجهات المضيف. لا تستخدمه لإصلاح مشكلة اتصال، لأنه يستبدل عادةً مشكلة واحدة بأخرى أصعب.

وصّل مشروعي Compose باستخدام شبكة خارجية

الشبكة التي ينشئها أحد المشروعين لا تكون مرئية للمشروع الآخر. لذلك لا يستطيع وكيل عكسي في proxy/compose.yaml الوصول إلى تطبيق في app/compose.yaml، حتى على الخادم نفسه. الحل هو استخدام شبكة لا يملكها أي من المشروعين.

أنشئها مرة واحدة يدويًا:

docker network create edge

ثم عرّفها كشبكة خارجية في كل مشروع. جانب الوكيل:

services:
  proxy:
    image: traefik:v3.1
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - edge

networks:
  edge:
    name: edge
    external: true

جانب التطبيق:

services:
  app:
    image: nginx:1.27
    networks:
      - edge
      - internal
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    networks:
      - internal

networks:
  edge:
    name: edge
    external: true
  internal:

يخبر external: true Compose بالاتصال بشبكة موجودة بدلًا من إنشائها، وتركها دون تغيير عند docker compose down. أما المفتاح المنفصل name: فأهم مما يبدو: بدونه يبحث Compose عن شبكة اسمها edge حرفيًا، ومعه يمكنك تسمية الشبكة باسم في ملفك وباسم آخر على المضيف.

إذا لم تكن الشبكة موجودة، يرفض Compose التشغيل ويبلغ بأن الشبكة عُرّفت كخارجية ولكن تعذّر العثور عليها. أنشئها أولًا.

انتبه إلى ما يفعله ملف التطبيق باستخدام internal. توجد قاعدة البيانات على شبكة المشروع المحلية فقط، لذلك لا يستطيع الوكيل الوصول إليها، ولا يستطيع الوصول إليها إلا app. تؤدي إضافة internal: true تحت شبكة إلى خطوة أبعد، إذ تزيل مسارها إلى العالم الخارجي بالكامل. هذا إعداد افتراضي مناسب لقاعدة البيانات، لكن يجب معرفة كلفته قبل تطبيقه: لا يستطيع أي container على شبكة داخلية تنزيل أي شيء، لذلك سيتوقف entrypoint الذي يشغّل apt-get update أو pip install عند بدء التشغيل، ثم يفشل بسبب انتهاء مهلة الاتصال.

للحصول على إعداد كامل يتضمن قواعد التوجيه والشهادات، راجع تشغيل عدة تطبيقات خلف مثيل Traefik واحد.

المنافذ المنشورة تتجاوز UFW

هذا هو الجزء من شبكات Compose الذي يتحول إلى حادثة أمنية. تنشر منفذًا، وتتحقق من أن UFW نشط ويرفض كل شيء باستثناء SSH، ومع ذلك تظل الخدمة قابلة للوصول من الإنترنت.

sudo ufw status
curl http://203.0.113.10:8080

يقول UFW إن المنفذ محظور. لكن curl يعيد الصفحة على أي حال. لا يوجد عطل. يكتب Docker قواعد ترجمة العناوين وإعادة التوجيه الخاصة به مباشرةً في iptables، وتُعاد توجيه حركة المرور المتجهة إلى منفذ حاوية منشور إلى الحاوية بدلًا من تسليمها إلى المضيف. لذلك لا تمر عبر السلسلة التي يديرها UFW لحركة المرور المتجهة محليًا. كما تُطابق قواعد Docker قبل قواعد UFW.

الإصلاح المختصر هو النشر فقط على العناوين التي تحتاج إليها:

    ports:
      - "127.0.0.1:8080:80"

يربط ذلك جانب المضيف بعنوان loopback، ولذلك يمكن الوصول إلى المنفذ من الخادم نفسه ومن خلال نفق SSH، ولا يمكن الوصول إليه من أي مكان آخر. ضع نقطة الدخول العامة خلف وكيل عكسي ينشر المنفذين 80 و443 عن قصد. يوجد الشرح الكامل، بما في ذلك سلسلة DOCKER-USER للحالات التي يجب فيها تصفية منفذ منشور، في لماذا ينشر Docker المنافذ متجاوزًا UFW مباشرةً وكيفية إصلاح ذلك.

كيفية تصحيح المشكلة باستخدام أربعة أوامر

ابدأ بالسؤال عن الشبكة التي يتصل بها كل حاوية فعليًا:

docker network inspect shop_default

تسرد كتلة Containers كل حاوية متصلة وعنوانها. إذا كانت خدمة ما غير موجودة في هذه القائمة، فهي متصلة بشبكة أخرى، أو تعمل في وضع المضيف، أو لا تعمل.

اختبر تحليل الأسماء من حاوية مؤقتة متصلة بالشبكة نفسها، حتى لا تحتاج إلى أي أدوات داخل صورك:

docker run --rm --network shop_default busybox nslookup db
docker run --rm --network shop_default busybox nc -zv db 5432

يشير فشل nslookup إلى مشكلة في تحليل الأسماء أو عضوية الشبكة. إذا نجح nslookup بينما فشل nc، فهذا يعني أن الخدمة تعمل، لكنها لا تستمع على ذلك المنفذ، أو تستمع على 127.0.0.1 داخل حاويتها بدلًا من 0.0.0.0. يحدث ذلك كثيرًا مع خوادم التطوير. ويكون الإصلاح في عنوان الربط الخاص بالتطبيق، وليس في Docker.

هناك فشل آخر يبدو كأنه خطأ في Docker. إذا تمكنت الحاويات من الاتصال بعضها ببعض، لكنها لم تتمكن من الوصول إلى جهاز على شبكة المكتب أو شبكة VPN، فمن المحتمل أن تتداخل الشبكة الفرعية لـ Docker مع تلك الشبكة. يخصص Docker الشبكات بدءًا من 172.17.0.0/16 افتراضيًا. انقل مجموعة العناوين في /etc/docker/daemon.json:

{
  "default-address-pools": [
    { "base": "10.200.0.0/16", "size": 24 }
  ]
}

ثم شغّل sudo systemctl restart docker وأعد إنشاء الشبكات المتأثرة، لأن الشبكة الموجودة تحتفظ بالشبكة الفرعية التي أُنشئت بها.

FAQ

لماذا لا يمكن لحاوياتي الوصول إلى بعضها باستخدام اسم الخدمة؟

لأنها ليست على الشبكة نفسها. يضع Compose كل خدمة تلقائيًا على <project>_default، لكن عند إضافة قائمة networks: إلى خدمة، تصبح هذه القائمة مجموعة الشبكات الكاملة الخاصة بها، ولا تعود الشبكة الافتراضية مُضمَّنة ضمنيًا. شغّل docker network inspect <network> وتحقق من ظهور كلتا الحاويتين في الكتلة Containers. تحقق أيضًا من أن أيًا من الخدمتين لا يستخدم network_mode: host، لأن الحاوية التي تعمل في وضع المضيف لا تكون على أي شبكة Docker، ولا يمكنها تحليل أسماء الخدمات.

هل أحتاج إلى نشر المنافذ لكي تصل خدمة إلى خدمة أخرى؟

لا. على شبكة Compose، يمكن لكل حاوية أخرى على الشبكة الوصول إلى كل منفذ في كل حاوية. يوجد ports: فقط لتمكين الوصول إلى الحاوية من خارج Docker، أما expose: فهو للتوثيق. نشر منفذ قاعدة بيانات عادة شائع ومكلف، لأنه يضع قاعدة البيانات على الواجهة العامة لخادمك.

ما الفرق بين شبكات bridge وhost؟

يمنح bridge الحاوية مساحة أسماء شبكة وعنوانًا خاصين بها على محوّل افتراضي، مع حل تلقائي للأسماء بين الحاويات وترجمة لحركة المرور الصادرة. أما host فيمنح الحاوية مكدس شبكة المضيف مباشرة: من دون عنوان منفصل، ومن دون حل لأسماء الخدمات، ومن دون نشر للمنافذ، ومن دون عزل عن المستمعات الأخرى على المضيف. إن bridge هو الإعداد الافتراضي والخيار الصحيح، ما لم تكن العملية تحتاج إلى واجهات المضيف.

كيف أصل الحاويات الموجودة في ملفي Compose مختلفين؟

أنشئ شبكة مشتركة باستخدام docker network create edge، ثم عرّفها في الملفين باستخدام external: true وألحق بها الخدمات التي تحتاج إلى التواصل. لن ينشئها Compose ولن يحذفها. إذا تخطيت خطوة الإنشاء، يرفض Compose بدء التشغيل ويبلغ عن أن الشبكة معلنة على أنها خارجية لكنها غير موجودة.

لماذا يمكن الوصول إلى حاويتي من الإنترنت مع أن UFW يحظر المنفذ؟

لأن المنفذ المنشور يُعالَج بواسطة قواعد إعادة التوجيه التي يضيفها Docker إلى iptables. تُطابق هذه القواعد قبل قواعد UFW، كما أن حركة المرور المُعاد توجيهها لا تمر عبر السلسلة التي يرشحها UFW أصلًا. اربط جانب المضيف بواجهة loopback باستخدام "127.0.0.1:8080:80"، وضع كل ما هو عام خلف وكيل عكسي على المنفذين 80 و443.