حاوية Docker غير قابلة للوصول على VPN؟ قارن الشبكات الفرعية
يمنح Docker شبكة فرعية تطالب بها الشبكة الافتراضية الخاصة أيضًا. قارن خطتي العنونة ثم اختر إصلاحًا يصمد بعد إعادة الاتصال.
تستجيب الحاويات على شبكة المكتب المحلية، ثم تتوقف عن الاستجابة في اللحظة التي تتصل فيها شبكة VPN الخاصة بالشركة. لم يتغير شيء داخل الحاوية: لا إعادة تشغيل، ولا تحديث صورة، ولا تعديل إعدادات. الذي تغيّر هو جدول التوجيه في المضيف، حيث أصبح مسار أضافه النفق يغطي العنوان الذي وزّعه Docker. قبل أن تعدّل شبكة فرعية أو تطلب استثناءً من VPN، ضع خطتي العنونة جنبًا إلى جنب، لأن الإصلاحات تكلّف أزمنة مختلفة جدًا.
اقرأ المسار قبل أن تغيّر الإعدادات
ابدأ بالشبكة التي بناها Docker فعليًا. كتلة IPAM هي خطة العنونة، والشبكة الفرعية هي السطر المهم:
docker network inspect project_default --format '{{json .IPAM.Config}}'
# [{"Subnet":"172.18.0.0/16","Gateway":"172.18.0.1"}]يخصص Compose شبكة /16 لكل مشروع جديد افتراضيًا، لذا يمكن أن تستقر خدمتان داخل نطاق من 65,536 عنوانًا. هذا الاتساع هو سبب شيوع التداخل: فشبكة /16 المختارة عشوائيًا كبيرة بما يكفي لابتلاع موقع تصل إليه عبر النفق، ولا يلاحظ أحد ذلك حتى يرتفع النفق.
ثم قارن اختيار المسار لعنوان حاوية واحد، مرة والنفق مغلق ومرة وهو مفتوح:
ip route get 172.18.0.7
# 172.18.0.7 dev br-3f2a9c src 172.18.0.1شكلان للإجابة يقودان إلى أماكن مختلفة. إذا ذكر الناتج tun0، wg0 أو جهاز نفق آخر، فإن بادئة VPN لا تقل تحديدًا عن مسار الجسر و الحزمة لا تصل إلى الحاوية أبدًا. وإذا كان لا يزال يذكر الجسر، فإن المضيف يستطيع الوصول إلى الحاوية، وعطلك هو التصفية أو DNS أو مسار عودة يفشل في اتجاه واحد فقط.
لماذا يصعب تفادي تصادم /16
يستمد Docker شبكات الجسر من التجمعات الخاصة، والتجمع الافتراضي يأتي من كتلة 172.16.0.0/12 بشرائح /16. تستخدم الشبكات المؤسسية المساحة الخاصة نفسها كثيرًا، ويدفع VPN الوصول عن بُعد تلك البادئات إلى كل عميل يتصل. وعندما يعتقد الطرفان أنهما يملكان 172.18.0.0/16، فإن جدول توجيه واحد يحمل ادعاءين متناقضين.
طول البادئة هو ما يحدد الفائز، لا ترتيب ظهور المسارات. نفق يدفع 10.0.0.0/8 لا يتغلب على مسار جسر 172.18.0.0/16، لأن /8 أقل تحديدًا. أما نفق يدفع 172.18.0.0/16، أو 172.18.5.0/24 داخلها، فيتغلب. لهذا يعمل حاسوب محمول على شبكة المكتب ويفشل على Wi-Fi الفندق مع الشبكة نفسها: فالعميل يتلقى بادئات مختلفة.
الجدول نفسه يحكم الاتجاه الآخر. الحاوية التي تحتاج إلى مضيف داخلي على 10.8.4.20 ترسل الحزمة إلى بوابة الجسر، ثم يوجّهها المضيف إلى النفق. يعمل هذا المسار عادة لأن المضيف يقوم بـ NAT، لكنه يفشل عندما يقبل الجانب المؤسسي عنوان VPN الخاص بالحاسوب المحمول فقط، أو عندما تُسقط تصفية المسار العكسي الرد غير المتماثل.
قارن الإصلاحات الثلاثة
لا توجد سوى ثلاثة مخارج، وهي تختلف في من يجب أن يوافق عليها وما الذي ينكسر أثناء تطبيق أحدها.
| Repair | Who owns it | What it costs |
|---|---|---|
| Renumber the container network | You | Recreates the network, restarts services, invalidates literal addresses in configs and scripts |
| Exclude your range from the tunnel | Network team | Changes what every laptop sees, needs a change request and a client profile update |
| Reach the service through a published port | You and the client | One port only, no direct container-to-container routing across the tunnel |
إعادة الترقيم هي الإصلاح الوحيد الذي يمكن إنجازه دون تذكرة، ولهذا تتوقف معظم الكتابات عندها. إنها رخيصة في مشروع فتٍّ ومكلفة بمجرد أن تشير أداة ترحيل أو سلسلة اتصال مخزنة إلى عنوان حرفي. أحصِ تلك المراجع قبل تعديل YAML، لا بعده.
الاستثناء من النفق هو أنظف إجابة عندما يقع التداخل في نطاق تملكه شركتك فعلًا. وهو أيضًا الأبطأ، وهو عديم الفائدة لمتعهد لا يستطيع تغيير ملف تعريف العميل. المنفذ المنشور أو وكيل عكسي صغير يتجنب الجدال: يتحدث العميل إلى عنوان يوجّهه النفق بشكل صحيح بالفعل، ولا يجب أن يكون قابلًا للوصول سوى ذلك المنفذ.
ثبّت نطاقًا عن قصد
لمشروع واحد، أعلن الشبكة الفرعية في ملف compose حتى يتوقف المشروع عن السحب من التجمع المشترك:
networks:
default:
ipam:
config:
- subnet: 10.201.7.0/24خُذ ذلك النطاق من سجل يحتفظ به فريقك، لا من مثال وجدته أثناء البحث. افحصه مقابل البادئات التي يدفعها VPN، وشبكات LAN المنزلية التي يعمل منها الناس، وشبكات الحاويات الأخرى على المضيف نفسه. ثم خطط لإعادة الإنشاء، لأن الشبكة الموجودة تحتفظ بشبكتها الفرعية القديمة: docker compose down، ثم up -d. docker network rm يرفض بينما تكون نقاط النهاية متصلة، وأي ipv4_address إدخال في الملف يجب أن يقع داخل النطاق الجديد.
بالنسبة للشبكات التي ينشئها Docker من تلقاء نفسه، عيّن تجمعًا للخادم الخفي في /etc/docker/daemon.json:
{
"default-address-pools": [
{ "base": "10.201.0.0/16", "size": 24 },
{ "base": "10.202.0.0/16", "size": 24 }
]
}ينطبق التجمع على الشبكات المنشأة بعد إعادة تشغيل الخادم الخفي، أما الجسور الموجودة بالفعل فتحتفظ بعناوينها. هذا مريح ويسهل نسيانه، لذا تعامل مع مشروع قديم على أنه لا يزال متداخلًا حتى يعيد أحدهم إنشاء شبكته. توثيق Docker لـ شبكات المحرك يصف قواعد التخصيص إذا احتجت إلى الدفاع عن التجمع في المراجعة.
جسر Linux وDocker Desktop يفشلان بشكل مختلف
على مضيف Linux، تستقر الحاويات على جسور داخل نطاق أسماء المضيف. جدول واحد يقرر كل شيء، لذا فالمقارنة أعلاه هي القصة كاملة: اقرأ مسار الجسر، واقرأ مسار النفق، وانظر أيهما يفوز لعنوان حاوية حقيقي.
يضع Docker Desktop الحاويات خلف جهاز ظاهري له NAT خاص به. لا يملك المضيف أي مسار إلى عنوان الحاوية إطلاقًا؛ بل يصل إلى الخدمة عبر منفذ منشور، وغالبًا يكون مرتبطًا بـ 127.0.0.1. لذلك يمكن لعميل VPN أن يعطّل الخدمة دون أي تداخل في الشبكات الفرعية، إما بتثبيت مسار للنطاق الداخلي للجهاز الظاهري أو بحجب حركة loopback أثناء اتصال النفق. وإذا أبلغ ip route get على المضيف عن عدم وجود مسار لعنوان الحاوية، فهذا متوقع وليس العطل.
النتيجة العملية هي من أين تبدأ البحث. على Linux، ابدأ من جدول التوجيه. على Desktop، ابدأ من المنفذ المنشور وقواعد جدار الحماية لعميل VPN، ثم افحص ما إذا كانت الشبكة الفرعية الداخلية للجهاز الظاهري تتصادم مع شيء يدفعه النفق.
تحقّق والنفق مفتوح
أعد توصيل VPN مرتين قبل أن تعتبره مصلحًا، لأن ملف تعريف يدفع المسارات عند الاتصال ويسحبها عند الانقطاع قد يبدو صحيحًا بعد المحاولة الأولى. ثم شغّل الفحوصات التي يشغلها العميل فعلًا:
ip route getمن المضيف، باستخدام العنوان الحقيقي للحاوية.- طلب من حاوية أخرى، لإثبات أن الخدمة نفسها سليمة.
curl -sv http://127.0.0.1:8080/من العميل والنفق مفتوح، ثمtraceroute -nإلى عنوان الحاوية من جهاز على شبكة LAN.sysctl net.ipv4.conf.all.rp_filterإذا وصل الطلب إلى المضيف لكن الرد اختفى.
أخيرًا، اكتب النطاق المختار في السجل نفسه الذي استخدمته لاختياره، مع التاريخ والمشروع الذي يملكه. سيتأتى التداخل التالي من مشروع لم يوثقه أحد، والدفاع الوحيد هو أن النطاق حُجز عن قصد لا انتُقي عشوائيًا.