DOCKER::ROOT الإقليم VI · veth · bridge · iptables kernel live
REGION VI الوصل لا العزل build docker0 by hand

الشبكة: كيف تتكلّم المعزولات؟

في الإقليم I عزلنا الشبكة بسطرٍ واحد فصارت العملية وحيدةً بلا منفذ. قلنا «العزل سهل، الوصل فن». هنا نتعلّم الفن: نبني شبكة Docker الافتراضية بأكملها بأيدينا. حين ننتهي، docker0 لن يكون اسماً غامضاً بل شيئاً صنعتَ مثله بيدك.

النبذة

net namespace يعطي العملية مكدّس شبكةٍ خاصاً بالكامل، لكنه يولد معزولاً تماماً: لا واجهة إلا lo. لنصل عالمين نحتاج ثلاث بدائيات: veth (كابلٌ افتراضي يخترق حدود الـ namespaces)، bridge (مبدّلٌ افتراضي يجمع كابلات)، وNAT (بوّابةٌ تترجم العناوين الخاصة للخروج). كل شبكات Docker مشتقّةٌ من تركيب هذه الثلاث.

اللغز المستفزّ — صِل عالمين، ثم العالم كله

الجزء أ — صِل معزولتين:

ip netns add ns1; ip netns add ns2 — كلٌّ جزيرة. اجعل ns1 يـ ping لـ ns2 (عيّن 10.0.0.1/24 و10.0.0.2/24). السؤال: ما الذي له طرفان، طرفٌ في كل جزيرة، فيصير جسراً؟ ابحث في ip link help عن نوع واجهةٍ يُخلَق أزواجاً.

الجزء ب — أخرِج معزولةً للإنترنت:

اجعل ns1 يـ ping 8.8.8.8 عبر المضيف. عنوانه الخاص لا يعود إليه أحد — من يجب أن يبدّل عنوان المصدر بعنوان المضيف، ثم يتذكّر التعيين ليُرجع الردّ للجزيرة الصحيحة؟ اكتشف اسم العملية (تبدأ بـ N) وأي سلسلةٍ في الجدار تطبّقها.

الجزء ج — انشر منفذاً:

خدمةٌ تستمع داخل ns1 على :80، أتِحها على المضيف:8080. معكوس ب: ترجمة عنوان الوجهة (DNAT). هذا هو -p 8080:80 بالضبط.

لحظة «آها» الكبرى

ستكتشف أن docker run -p ليس ميزةً سحرية، بل سطرا iptables تكتبهما أنت في دقيقتين. الموارد: man ip-netns (قسم EXAMPLES)، man ip-link (نوع vethman iptables.

الدرس — البدائيات الثلاث، ومنها كل شبكات Docker

ليش veth: الكابل الذي يخترق الجدران

net namespace جدارٌ عازل. veth pair زوجٌ من الواجهات متّصلتان كطرفي كابل: ما يدخل أحدهما يخرج من الآخر. تضع طرفاً في namespace والآخر في آخر فيصير «سلكاً» رغم الجدار.

bash
$ ip link add veth0 type veth peer name veth1 # كابل بطرفين $ ip link set veth1 netns ns1 # ادفع طرفاً داخل الجزيرة

في Docker لكل كونتينر veth pair: طرفٌ داخله (eth0)، وطرفٌ على المضيف (vethXXXX). شغّل كونتينراً ونفّذ ip link — سترى veth... تظهر؛ احذف الكونتينر تختفِ.

ليش bridge: المبدّل الذي يجمع الكابلات

عشرة كونتينرات = عشرة كابلاتٍ لا يرى بعضها بعضاً. تحتاج مبدّلاً. Linux bridge مبدّلٌ افتراضي L2: تربط به واجهاتٍ فيمرّر الإطارات بينها بـ MAC.

bash
$ ip link add br0 type bridge # مبدّل افتراضي = docker0 بالضبط $ ip link set veth0 master br0 # وصّل طرف كابل ns1 $ ip link set veth2 master br0 # وطرف كابل ns2

عند تثبيت Docker يُنشأ جسرٌ افتراضي docker0 (مثل 172.17.0.1)، وكل كونتينر يُوصَل به بـ veth. الكونتينرات على نفس الجسر ترى بعضها.

ليش — لماذا الـ DNS بالاسم يعمل على الشبكة المخصّصة فقط

على الجسر الافتراضي docker0 تتواصل الكونتينرات بالـ IP لكن لا تتحلّ أسماؤها؛ على شبكة docker network create تتحلّ بالاسم. لأن Docker يشغّل DNS مدمجاً (على 127.0.0.11) فقط على الشبكات المعرّفة من المستخدم. docker0 قديمٌ ولا يفعّله — اشتقاق سلوكٍ يحيّر كثيرين.

ليش NAT: جواز سفر العناوين الخاصة

الكونتينر يحمل عنواناً خاصاً (172.17.x.x) لا يُوجَّه على الإنترنت. ليخرج، يلعب المضيف دور البوّابة ويُجري NAT:

bash افحص بعينك
$ sudo iptables -t nat -L -n -v # MASQUERADE و DNAT التي كتبها Docker $ sudo conntrack -L # جدول التتبّع يربط الجلسات

هذه نفس القواعد التي ستكتبها بيدك. Docker مجرد كاتبٍ آلي لها. (التوزيعات الحديثة تستبدل iptables بـ nftables؛ المفهوم واحد. وبدائل eBPF مثل Cilium تتجاوزه لأداء أعلى — الفكرة الجذرية ثابتة.)

اشتقاق كل شبكات Docker من الثلاث

driverالتركيبمتى
bridgeveth + docker0 + NATالعزل الافتراضي مع خروج
hostلا net ns — يشارك مكدّس المضيفأقصى أداء، صفر عزل شبكي
nonenet ns بلا vethعزلٌ تام — حالة الإقليم I
container:Xيشارك net ns كونتينرٍ آخرهذا هو الـ Pod
macvlanواجهةٌ بعنوان MAC خاص على الـ LANالكونتينر يبدو جهازاً مستقلاً، بلا NAT
overlayVXLAN يغلّف L2 فوق L3شبكةٌ واحدة عبر عنقود مضيفات

لاحظ كيف host وnone وcontainer:X كلها مجرد قرارٍ حول net namespace — حصادٌ مباشر للإقليم I. وoverlay بذرةٌ للأوركستريشن.

اللغز — استنسخ شبكة Docker الافتراضية كاملة

  1. البنية: جسر mybr0 (10.10.0.1/24)، ثلاث namespaces كلٌّ بـ veth إلى الجسر وعنوانٍ في الشبكة. أثبت أن الثلاثة تـ ping بعضها والبوّابة.
  2. الخروج: فعّل ip_forward، أضف MASQUERADE، أثبت ping 8.8.8.8 ثم curl. تتبّع حزمةً في conntrack.
  3. النشر: python3 -m http.server 80 داخل namespace، انشره على :8080 بـ DNAT، أثبت curl localhost:8080 من المضيف.
  4. المقارنة الكاشفة: شغّل كونتينر Docker بـ -p 8080:80، نفّذ iptables -t nat -S وقارن قواعده بقواعدك. ستجدها نفس البنية. اكتب جدول تطابق: قاعدتك ↔ قاعدة Docker. لحظة اليقين.
المصادر

man ip-netns (قسم EXAMPLES كامل)، man iptables-extensions لـ MASQUERADE/DNAT. لفهم DNS المدمج جرّبه على شبكتين (افتراضية vs user-defined) ولاحظ الفرق.

الخلاصة — الوصل أُتقن

veth — الكابل الذي يخترق جدار net namespace bridge — المبدّل الذي يجمع الكابلات (= docker0) NAT — البوّابة التي تترجم (MASQUERADE خروجاً، DNAT نشراً)

كل شبكات Docker صارت تركيباتٍ مفهومةً من هذه الثلاث + قرار net namespace من الإقليم I. -p, --network, docker0, embedded DNS, وقواعد iptables -t nat الغامضة — كلها اشتُقّت بيدك.

بذرة الغموض

في الإقليم III رأيت أن الصورة شجرة Merkle من طبقاتٍ معنونةٍ بالمحتوى. هذا التصميم لم يكن للتخزين فقط — كان للـ توزيع. حين تدفع صورةً يملك الـ registry نصف طبقاتها، فلا تُرسل إلا الناقص. وحين تبني، نفس العنونة تقرّر أي طبقةٍ تُعاد. الإقليم التالي يكشف كيف أن Dockerfile وpull/push وجهان لفكرةٍ واحدة.