کانتینر شما هنوز روی پل کنترلی خود پاسخ می‌دهد، اما هر بسته خروجی از macvlan اداری خارج می‌شود. هیچ‌چیز شکست نخورده است. دو اتصال هرکدام یک دروازه می‌دهند و تنها مسیر پیش‌فرض به شبکه‌ای رفت که قصدش را نداشتید، پس سرویس به کار خود ادامه می‌دهد در حالی که ترافیکش مسیر طولانی‌تری می‌پیماید. تا وقتی کسی متوجه شود، شاهد یک جدول مسیریابی است نه یک پیام خطا.

توضیح معمول این است که Docker شبکه را تصادفی انتخاب کرده است. در عمل تصمیم قدیمی از ترتیب الفبایی نام شبکه‌های متصل پیروی می‌کرد که مسیریابی را به عارضه جانبی یک برچسب تبدیل می‌کرد. gw_priority آن را با عددی جایگزین می‌کند که خودتان آگاهانه انتخاب می‌کنید، و پذیرش آن پس از نوشتن چیدمان حدود یک دقیقه طول می‌کشد.

چرا مسیر پیش‌فرض روی شبکه اشتباه می‌افتد

هر شبکه‌ای که سرویسی به آن می‌پیوندد می‌تواند دروازه خودش را عرضه کند، و کانتینر باز هم دقیقاً یک مسیر پیش‌فرض دارد. پیش از Compose 2.33.1 تساوی با مرتب‌سازی نام شبکه‌ها حل می‌شد، پس برچسبی مثل control که پیش از office می‌آید تعیین می‌کرد ترافیک کجا برود. هیچ‌چیز درون کانتینر ثبت نمی‌کند که تصمیم دلبخواهی بوده، و همین است که آن را از بازبینی کد عبور می‌دهد و بعدها به‌صورت مسیری عجیب ظاهر می‌شود. گذاشتن یک سیاست در فایل compose تنها راه قابل‌بررسی کردن این انتخاب است.

دو چیز واقعاً تعیین می‌کند کدام شبکه برنده شود: مقادیر gw_priority که تعیین می‌کنید، و اینکه اصلاً کدام شبکه‌ها متصل باشند. شبکه سومی اضافه کنید و مجموعه نامزدها بزرگ‌تر می‌شود؛ macvlan را حذف کنید و چیزی برای رقابت با پل نمی‌ماند. هیچ‌یک از این دو حرکت در گزارش‌های برنامه دیده نمی‌شود، چون مسیر پیش‌فرض اشتباه به‌ندرت اتصال را کاملاً قطع می‌کند. فقط باعث می‌شود پاسخ‌ها از نشانی‌ای برسند که طرف مقابل شاید انتظارش را ندارد.

دروازه را با gw_priority تثبیت کنید

Compose از نسخه 2.33.1 به بعد gw_priority را روی هر اتصال سرویس به شبکه ارائه می‌کند و عدد بزرگ‌تر برنده می‌شود. کلید دروازه به‌ازای هر سرویس وجود ندارد، پس مقدار به بلوک networks همان سرویس تعلق دارد. مثال زیر مسیر پیش‌فرض را به control می‌دهد و office را برای LAN مستقیماً متصلش قابل‌دسترس نگه می‌دارد.

services:
  vpn:
    networks:
      control:
        gw_priority: 1
      office:
        gw_priority: 0

networks:
  control:
    driver: bridge
    ipam:
      config:
        - subnet: 172.30.50.0/24
  office:
    driver: macvlan
    driver_opts:
      parent: eno1.120
      macvlan_mode: bridge
    ipam:
      config:
        - subnet: 192.168.120.0/24
          ip_range: 192.168.120.192/27
          gateway: 192.168.120.1

بلوک را دو کنترل جداگانه بخوانید: gw_priority مسیر پیش‌فرض را تعیین می‌کند و بخش ipam آدرس‌دهی را. تغییر فقط subnet انتخاب خروجی را دست‌نخورده می‌گذارد، و این رایج‌ترین راهی است که این اصلاح نیمه‌کاره اعمال می‌شود. اولویت‌ها را کنار شبکه‌ای نگه دارید که بر آن حکومت می‌کنند تا خواننده بعدی مجبور به جست‌وجوی تصمیم نباشد.

gw_priority را با ویژگی ساده priority که کنارش هست اشتباه نگیرید. آن یکی ترتیب ساخت اتصال‌ها را تعیین می‌کند و درباره مسیریابی چیزی نمی‌گوید، پس نمی‌تواند دروازه پیش‌فرض را انتخاب کند و نام واسط‌ها را درست نمی‌کند. اگر کسی توصیه کند برای هدایت ترافیک priority را بالا ببرید، آن توصیه نادرست است حتی وقتی ترتیب حاصل تصادفاً درست به نظر برسد.

بررسی کنید کدام شبکه واقعاً مسیر پیش‌فرض را حمل می‌کند

درون کانتینر در حال اجرا بررسی کنید، چون جدول مسیریابی میزبان درباره میزبان است نه فضای نامی که سرویس استفاده می‌کند. نخست بررسی پیکربندی را اجرا کنید تا یک غلط تایپی پیش از راه‌اندازی مجدد شکست بخورد، سپس مسیری را بخوانید که کانتینر واقعاً نصب کرده است.

docker compose config --quiet
docker exec vpn ip -4 route show default
docker exec vpn cat /proc/net/route

نتیجه درست دستگاه پل و 172.30.50.1 را نام می‌برد؛ نتیجه نادرست 192.168.120.1 و دستگاه macvlan را. اگر تصویر بسته iproute2 را نداشته باشد، فایل /proc باز هم مسیر پیش‌فرض را گزارش می‌کند، با مقصد 00000000 و بایت‌های دروازه وارونه. وقتی نام دستگاه‌ها روشن نیست ستون واسط را با docker inspect مقایسه کنید.

macvlan یک پورت فیزیکی است، نه کلونی از پل

macvlan به کانتینر هویت دومی روی سیم می‌دهد و دقیقاً همین است که دسترسی به دستگاه‌های محلی را کار می‌اندازد. همچنین یعنی پورت سوئیچ و هایپروایزر باید یک نشانی MAC اضافه را بپذیرند، و بسیاری از شبکه‌های ابری این را بی‌سروصدا رد می‌کنند. پیش از برنامه‌ریزی برای یک ناوگان یک کانتینر را آزمایش کنید، چون MAC ردشده معمولاً به‌صورت پیوندی ظاهر می‌شود که بالاست و چیزی را عبور نمی‌دهد.

تنظیم macvlan_mode: bridge حالتی از درایور macvlan است، نه پل نرم‌افزاری Docker، و passthru حالتی جداگانه و تخصصی است نه پیش‌فرض. لینوکس همچنین نمی‌گذارد کانتینر macvlan به میزبان خودش برسد از طریق همان والد، و همین دلیل نگه‌داشتن پل کنترلی است به‌جای انتظار اینکه یک اتصال هر دو کار را انجام دهد.

والد VLAN را پیش از آنکه Compose ببیندش نام‌گذاری کنید

Docker والد نقطه‌دار مانند eno1.120 را یک زیرواسط 802.1Q می‌بیند و خودش دستگاه VLAN را می‌سازد. این راحتی محدودیت دارد: نام مشتق‌شده به نام واسط فیزیکی وابسته است و نام‌های بلند می‌توانند نتیجه را از آنچه ابزارهای پیرامون انتظار دارند فراتر ببرند. ساختن یک واسط VLAN نام‌دار در پیکربندی شبکه میزبان و نشانه‌رفتن parent به آن، دو سر را قابل‌پیش‌بینی نگه می‌دارد.

یادتان باشد کلید زیر networks یک نام Compose است، نه یک دستگاه لینوکس. جست‌وجوی میزبان برای واسطی به نام office شکست می‌خورد، چون هسته فقط واسط والد و جفت veth پشت کانتینر را می‌بیند.

بالای آن جا برای VPN باز کنید

کلاینت VPN پس از وجود این شبکه‌ها متصل می‌شود، پس می‌تواند مستقل از gw_priority مسیرهای دقیق‌تر، قواعد سیاست و فیلترها را اضافه کند. پیشوندهای دوردست را در برابر هر دو subnet محلی برنامه‌ریزی کنید، از جمله پاسخ‌هایی که مسیر کنترلی نیاز دارد. وقتی پیشوندی دوردست با LAN اداری هم‌پوشانی دارد، طرح نشانی را درست کنید نه اینکه کل یک بازه خصوصی را از تونل بکنید.

پس از راه‌اندازی VPN و بار دیگر پس از اتصال دوباره‌اش، نشانی‌ها، مسیرها و مسیر سرویس را دوباره بررسی کنید، چون در اتصال دوباره است که مسیری که با دست اضافه شده ناپدید می‌شود. جدول مورد انتظار را پیش از نخستین آزمون بنویسید تا مقایسه بعدی یک diff باشد نه تمرین حافظه. مالکیت پایدار شبکه را در Compose نگه دارید و بگذارید پیکربندی VPN فقط سیاست خودش را داشته باشد.

مقالات مرتبط