API خصوصی در مرورگر دسکتاپ باز می‌شود و در کانتینر توسعه شکست می‌خورد. این تناقض نیست و به‌ندرت خود VPN مقصر است. درخواست مرورگر و درخواست ناموفق را دو فرایند متفاوت ساخته‌اند، با resolverها، proxyها و مسیرهای متفاوت، و اغلب از فضاهای نام شبکه کاملاً متفاوت. «روی لپ‌تاپ من» می‌تواند میزبان Windows، یک توزیع WSL، ماشین مجازی Docker Desktop و یک کانتینر درون همان VM باشد — چهار بافت که هرکدام می‌توانند به یک نام میزبان پاسخ متفاوت بدهند. پیش از دست‌زدن به حتی یک تنظیم، بفهمید فرایند ناموفق از کدام‌یک استفاده می‌کند.

پیش از تغییر تنظیم، فرایند را پیدا کنید

از موتور کانتینر شروع کنید نه از کلاینت VPN. بپرسید خط فرمان با کدام بافت صحبت می‌کند و آن موتور روی کدام سیستم‌عامل اجرا می‌شود، زیرا کانتینر Linux که یک VM دسکتاپ آن را مدیریت می‌کند مسیر متفاوتی از کانتینری دارد که یک daemon محلی مدیریتش می‌کند:

docker context ls
docker info --format '{{.OperatingSystem}} / {{.Name}}'
docker run --rm alpine ip route

در Windows، wsl -l -v می‌گوید توزیع زیر WSL 2 اجرا می‌شود یا نه، که آن را پشت سوئیچ مجازی خودش قرار می‌دهد. همین جزئیات تعیین می‌کند کانتینر جدول مسیریابی میزبان را شریک است یا پشت NATی است که برای رسیدن به میزبان به پورت فورواردشده یا نام میزبان نیاز دارد.

یک درخواست را ثابت نگه دارید و مقایسه کنید

یک endpoint بی‌خطر روی میزبان خصوصی انتخاب کنید و آن را برای هر مقایسه به کار ببرید: از مرورگر، از ترمینال میزبان، از توزیع WSL و از درون کانتینر. برای هر تلاش چهار چیز ثبت کنید: نشانی حل‌شده، اینکه proxy استفاده شده یا نه، کلاس خطا و زمان سپری‌شده. جست‌وجوی نام، برقراری اتصال، اعتبارسنجی TLS و وضعیت HTTP چهار مشکل متفاوت‌اند و مخلوط‌کردن آن‌ها در «کار نمی‌کند» یک صبح را تلف می‌کند.

مقایسه را درون کانتینری که شکست می‌خورد اجرا کنید، با یک timeout تا فایروال خاموش ترمینال را دقیقه‌ها نگه ندارد. کانتینر تازه بافت شبکه موتور را بازتولید می‌کند اما فضای نام، متغیرهای proxy یا resolver کانتینر برنامه را نه، بنابراین docker exec به کانتینر واقعی آزمون معتبر است و کانتینر کناری فقط راهی برای جدا کردن این است که تفاوت در موتور است یا در کانتینر:

docker exec -it app /bin/sh -c "getent hosts api.internal; curl -v --max-time 8 https://api.internal/ 2>&1 | tail -20"
docker run --rm --network container:app curlimages/curl:latest -sv --max-time 8 https://api.internal/ 2>&1 | tail -20

تصویر پایه alpine بسته busybox دارد و curl ندارد، و این دقیقاً همان جزئیاتی است که یک بلوک تشخیصی کپی‌شده را به شکست می‌کشاند: تصویری با کلاینت انتخاب کنید یا آن را در همان اجرا اضافه کنید. مقایسه با سایت‌های عمومی بی‌ربط چیزی را جدا نمی‌کند. اگر https://example.com از کانتینر کار کند، فقط فهمیده‌اید کانتینر دسترسی اینترنت دارد و هیچ‌چیز درباره اینکه آیا از طریق تونل به نشانی خصوصی می‌رسد. به‌همین‌ترتیب، بازکردن ترمینال جدید فقط ثابت می‌کند ترمینال جدید کار می‌کند، نه اینکه محیط اجرای مشترکی با برنامه‌ای که شکست خورد دارد.

Docker Desktop می‌تواند با VPN کار کند

مستندات خود Docker درباره VPN و اتصال به میزبان این را پیکربندی پشتیبانی‌شده توصیف می‌کند و خلاصه صادقانه این است که کار می‌کند نه اینکه به‌طور خودکار خراب باشد. فرض نکنید VM دسکتاپ به‌حکم طراحی تونل میزبان را دور می‌زند؛ در بسیاری از پیکربندی‌ها ترافیک درست از تونل میزبان پیروی می‌کند و به همین دلیل افسانه «Docker به VPN توجه نمی‌کند» توصیه‌های مطمئن و غلط تولید می‌کند.

چیزی که هنوز مهم است backend مورد استفاده، سیاست split-tunnel کلاینت و پیکربندی resolverی است که VM دریافت کرده است. کلاینت VPNی که مسیرها را نصب می‌کند اما DNS را نه، یا DNS سروری را push می‌کند که VM به آن نمی‌رسد، خطای جست‌وجوی نام درون کانتینر تولید می‌کند در حالی که میزبان کار می‌کند چون resolver اضافی یا پاسخ کش‌شده دارد. این مشکلی از resolver با لباس کانتینر است. از هر اتصال مجدد VPN که امتحان کنید جان سالم به در می‌برد، چون تونل از قبل همان ترافیکی را حمل می‌کند که خواسته‌اید.

هم‌پوشانی نشانی‌ها و MTU خطاهای متفاوتی هستند

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

docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'
ip route get 10.20.30.40

دومی مشکل MTU است. اگر اتصال برقرار شود و TLS کامل شود اما پاسخ‌های بزرگ‌تر گیر کنند، مشکل اندازه بسته دارید نه مجوز، و جایگزینی رکوردهای DNS هرگز آن را درست نمی‌کند. پیش از تغییر هر چیزی با یک درخواست کوچک و یک درخواست بزرگ آزمایش کنید و هر دو نتیجه را ثبت کنید.

راه‌حل هم‌پوشانی یک زیرشبکه کانتینر متفاوت یا یک مسیر محدود است، که مالک طرح نشانی‌ها انتخاب می‌کند. راه‌حل گیرکردن MTU فشردن یا کاهش MTU تونل یا رابط به مقداری است که در مسیر دوام بیاورد، و در لایه‌ای اعمال می‌شود که کنترلش می‌کنید.

پیکربندی proxy به‌ازای فرایند است، نه ماشین

درون کانتینر، localhost به بافت شبکه خود همان کانتینر اشاره دارد، پس proxyای که روی میزبان اجرا می‌شود با آن نام در آنجا در دسترس نیست. Docker Desktop برای دسترسی به میزبان host.docker.internal را فراهم می‌کند و در Linux آن را به‌صراحت با --add-host=host.docker.internal:host-gateway اضافه می‌کنید. در هر حال، آن نام مسیری به میزبان می‌دهد؛ سرویس proxy نمی‌سازد و مجوز استفاده از یکی را هم نمی‌دهد.

نشانی و پورت proxyای را که برای محیط واقعی داده شده به کار ببرید و بررسی کنید چه کسی آن را می‌خواند. متغیرهایی که در یک shell تعاملی export می‌شوند به‌طور خودکار هر افزونه ویرایشگر، سرور زبان، ابزار build و daemon پس‌زمینه را پیکربندی نمی‌کنند. آنچه مهم است فرایندی است که درخواست ناموفق را ساخته، پس محیط آن را بررسی کنید نه محیط ترمینال خودتان.

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

مقالات مرتبط