◀ العودة إلى المدونة
Docker

دليل الشبكات في Docker

نشر في 08 Jun 2024· 7 min قراءة
#Docker#Réseau#Infrastructure

أوضاع الشبكة في Docker

يقدّم Docker عدة مشغّلات (drivers) للشبكة، يناسب كلٌّ منها حالات استخدام محددة.

فهم هذه المشغّلات يجنّبك معظم مشاكل الاتصال التي تصادفها مع الحاويات: تطبيق لا يجد قاعدة بياناته، أو منفذ لا يمكن الوصول إليه من الخارج، أو على العكس قاعدة بيانات مكشوفة على الإنترنت دون قصد. أكثر المشغّلات استخداماً هي bridge وhost وoverlay؛ ويوجد أيضاً none الذي يقطع كل وصول إلى الشبكة، وmacvlan الأقل شيوعاً. الأوامر الأساسية لاستكشاف الموجود:

docker network ls
docker network inspect bridge
docker network inspect mon-reseau --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'

Bridge (الوضع الافتراضي)

ينشئ وضع bridge شبكة معزولة للحاويات على المضيف نفسه:

docker network create --driver bridge mon-reseau
docker run --network mon-reseau --name app myapp
docker run --network mon-reseau --name db mysql

يمكن للحاويات الموجودة على شبكة bridge نفسها أن تتواصل باستخدام اسم الحاوية كاسم مضيف (hostname).

تفصيل مهم: لا يعمل هذا التحليل بالاسم إلا على شبكات bridge التي ينشئها المستخدم. أما الشبكة الافتراضية bridge، التي تُلحق بها الحاويات المشغّلة دون الخيار --network، فلا توفّر DNS بين الحاويات. وهذا سبب كافٍ لإنشاء شبكاتك الخاصة دائماً، وهو ما يفعله Docker Compose تلقائياً: يحصل كل مشروع على شبكة باسم <project>_default.

على شبكة bridge، تملك الحاويات عناوين خاصة ولا يمكن الوصول إليها من الخارج. ولجعل خدمة ما قابلة للوصول، ننشر منفذاً: يضيف Docker حينها قاعدة NAT من المضيف نحو الحاوية. افتراضياً يُفتح المنفذ على جميع واجهات المضيف؛ حدّد عنواناً لتقييده:

# Reachable from outside on host port 8080
docker run -d -p 8080:80 --name web nginx:alpine

# Reachable only from the host itself
docker run -d -p 127.0.0.1:8081:80 --name admin nginx:alpine

# Attach an existing container to a second network
docker network connect mon-reseau web

انتبه: قواعد iptables التي ينشئها Docker للمنافذ المنشورة تُطبَّق قبل قواعد جدار حماية مثل UFW. لذا فالمنفذ المنشور على 0.0.0.0 يمكن الوصول إليه حتى لو بدا أن UFW يحجبه.

Host

يلغي وضع host عزل الشبكة. تستخدم الحاوية شبكة المضيف مباشرة:

docker run --network host nginx

مفيد للحصول على أقصى أداء، لكن دون أي عزل.

لا يوجد NAT ولا منفذ يجب نشره: يستمع Nginx مباشرة على المنفذ 80 للمضيف، وتُتجاهل خيارات -p. في المقابل، لا يمكن لحاويتين استخدام المنفذ نفسه، وترى الحاوية كل واجهات الشبكة في الجهاز. يُبرَّر هذا الوضع للأدوات التي تحتاج إلى مراقبة شبكة المضيف (وكلاء المراقبة، بعض أدوات VPN) أو لأحمال العمل التي يهم فيها أدنى قدر من زمن الاستجابة. وهو مدعوم بالكامل على Linux؛ أما مع Docker Desktop على macOS وWindows، فتعمل الحاويات داخل آلة افتراضية ولا يكون السلوك نفسه.

None

مع --network none لا تملك الحاوية سوى واجهة loopback. هذا مفيد لمعالجة لا تحتاج إلى أي وصول إلى الشبكة، مثل تحويل ملفات غير موثوقة: حتى لو اختُرقت العملية، فلا يمكنها تسريب أي شيء.

docker run --rm --network none -v "$PWD/input:/data:ro" alpine ls -l /data

Overlay

بالنسبة لعناقيد Docker Swarm، تتيح شبكة overlay التواصل بين الحاويات الموجودة على عُقد مختلفة:

docker network create --driver overlay --attachable mon-overlay

تتطلب شبكة overlay أن يكون المضيف عقدةً في Swarm (docker swarm init). تُغلَّف حركة المرور بين العقد بتقنية VXLAN: يجب فتح المنافذ 2377/tcp (إدارة العنقود) و7946/tcp وudp (اكتشاف العقد) و4789/udp (البيانات) بين الأجهزة. يسمح الخيار --attachable للحاويات المشغّلة بـ docker run، وليس فقط لخدمات Swarm، بالانضمام إلى الشبكة. حركة مرور الإدارة مشفّرة افتراضياً؛ ولتشفير حركة مرور التطبيقات بين العقد أيضاً، أضف --opt encrypted.

Macvlan

يمنح المشغّل macvlan الحاوية عنوان MAC خاصاً بها وعنوان IP على الشبكة الفعلية، كأنها جهاز مستقل. يُستخدم أساساً لدمج تطبيقات قديمة تتوقع عنوان IP مخصصاً. خاصية يجب معرفتها: افتراضياً، لا يستطيع المضيف نفسه التواصل مع حاويات macvlan الخاصة به.

DNS الداخلي

يدمج Docker خادم DNS يتيح التحليل باسم الخدمة:

services:
  app:
    networks:
      - frontend
      - backend
  nginx:
    networks:
      - frontend
  db:
    networks:
      - backend

networks:
  frontend:
  backend:

داخل كل حاوية، يمكن الوصول إلى خادم DNS هذا على العنوان 127.0.0.11. وهو يحلّل أسماء الخدمات وأسماء الحاويات والأسماء المستعارة (aliases) المصرّح بها، لكن فقط للحاويات التي تتشارك شبكة. في المثال أعلاه، يستطيع app الوصول إلى nginx وdb، بينما لا يستطيع nginx تحليل db: فلا يملك Nginx مخترَق أي طريق مباشر إلى قاعدة البيانات. وإذا وُسِّعت خدمة على عدة حاويات، يعيد اسمها عدة عناوين.

وللذهاب أبعد، صرّح بشبكة backend على أنها داخلية (دون وصول إلى الخارج)، وشارك مع الوكيل العكسي شبكةً أُنشئت خارج المشروع:

networks:
  frontend:
  backend:
    internal: true
  proxy:
    external: true

لتشخيص مشكلة في التحليل أو الاتصال، شغّل حاوية أدوات على الشبكة نفسها بدلاً من تثبيت حزم في صورك:

docker run --rm -it --network mon-reseau nicolaka/netshoot
# Then, inside the container:
dig db
nc -zv db 3306

تعارض الشبكات الفرعية

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

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

ملخّص المشغّلات

المشغّلالنطاقحالة الاستخدام
bridgeمضيف واحدالحالة العامة، تطبيقات Compose
hostمضيف واحدأقصى أداء، أدوات الشبكة
noneحاوية واحدةمعالجة دون وصول إلى الشبكة
overlayعدة مضيفينعناقيد Docker Swarm
macvlanالشبكة الفعليةتطبيقات تتطلب عنوان IP مخصصاً

الممارسات الجيدة للشبكة

  • اعزل الخدمات حسب الشبكة (frontend، backend، monitoring)
  • استخدم شبكات داخلية للخدمات غير المكشوفة
  • وثّق بنية شبكاتك
  • قلّص المنافذ المكشوفة إلى الحد الأدنى الضروري
  • استخدم أسماء الخدمات بدلاً من عناوين IP التي تتغير مع كل إعادة إنشاء للحاوية
  • اربط بـ 127.0.0.1 المنافذ التي لا يُراد استخدامها إلا محلياً

لأمن الحاويات فيما يتجاوز الشبكة، راجع مقال تأمين حاويات Docker.