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

با این شروع کنید که چرا دو مسیر متفاوت‌اند، سپس تصمیم بگیرید تونل کجا باید زندگی کند.

چرا میزبان کار می‌کند و کانتینر نه

بالا آوردن تونل به میزبان یک واسط، مجموعه‌ای از مسیرها و معمولاً یک ورودی مفسر نام می‌دهد که VPN تأمین می‌کند. کانتینر از هیچ‌یک از این‌ها ارث نمی‌برد: با یک واسط روی پل موتور و یک مسیر پیش‌فرض که به همان پل اشاره می‌کند شروع می‌شود. مسیرهای نصب‌شده روی میزبان برای هر چیزی که درون آن فضای نام اجرا می‌شود نامرئی است.

به همین دلیل نشانه‌ها ناسازگار به نظر می‌رسند نه صرفاً خراب. میزبان نام خصوصی را حل می‌کند و وصل می‌شود، چون هم نام و هم مسیر آنجا وجود دارند. کانتینر بستهٔ خود را به نشانی خصوصی می‌فرستد که هیچ‌چیز روی مسیر پل به آن نمی‌رسد، و درخواست به پایان زمان می‌خورد. افزودن نام به فایل hosts کانتینر چیزی را تغییر نمی‌دهد، چون خود نشانی از جایی که کانتینر نشسته دست‌نیافتنی است.

از درون کانتینر به میزبان برسید

فرایندی در کانتینر که localhost را می‌گیرد، خود کانتینر را می‌گیرد. Docker Desktop نام host.docker.internal را منتشر می‌کند که به ماشینی که موتور را اجرا می‌کند بازمی‌گردد؛ موتورهای Linux آن را تعریف‌نشده می‌گذارند مگر اینکه سرویس درخواستش کند، و این دقیقاً کاری است که نگاشت پایین انجام می‌دهد. با این کار، برنامه به‌جای نشانی خصوصی یک نام و یک درگاه را هدف می‌گیرد.

services:
  app:
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      INTERNAL_API_HOST: "host.docker.internal:8666"

تونل در سمت میزبانِ این جهش می‌ماند، و درگاه در آن متغیر همانی است که پروکسی روی آن گوش می‌دهد. کانتینری که تونل را از دست بدهد اکنون در یک جهش شناخته‌شده شکست می‌خورد، نه در تلاش مسیری که هرگز نداشته است.

انتخاب کنید تونل کجا زندگی کند

برای سرویس Compose که به API خصوصی نیاز دارد سه شکل ممکن است، و تفاوتشان در این است که کدام فرایند مالک تونل است. کارخواه VPN می‌تواند درون تصویر کانتینر اجرا شود، که مستلزم آشکارسازی دستگاه تونل و دادن توانایی مدیریت شبکهٔ همراه آن است. برنامه می‌تواند فضای نام شبکهٔ یک کانتینر تونل جداگانه را به اشتراک بگذارد، تا هر دو فرایند یک پشته و یک جدول مسیریابی ببینند. یا تونل می‌تواند روی میزبان بماند، با یک پیش‌فرستندهٔ باریک از میزبان به کانتینر.

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

یک مسیر باریک روی میزبان منتشر کنید

از آنچه پیش‌فرستنده ارث می‌برد شروع کنید: آن را با حسابی که مالک تونل است اجرا کنید، تا سوکت‌های خروجی‌اش مسیرهای میزبان و مفسر نام میزبان را بردارند. یک بلوک location در nginx یک مسیر را پوشش می‌دهد.

server {
    listen 8666;
    location /search/ {
        proxy_pass http://10.20.30.40:8090/;
        proxy_set_header Host internal-api.example;
        proxy_read_timeout 60s;
    }
}

نشانی و درگاه را جانشین‌های موقت بگیرید و جفتی را بگذارید که API واقعاً روی آن گوش می‌دهد. هر upstream بعدی بلوک location خودش را می‌خواهد، و هر بلوک باید فقط مسیرهایی را بپذیرد که این سرویس صدا می‌زند، تا پروکسی دروازه‌ای عمومی به شبکهٔ خصوصی نشود. شنونده را برای کارخواهانی که از host-gateway می‌آیند به نشانی پل، و فقط وقتی کارخواه فضای نام شبکهٔ میزبان را شریک است به loopback، و هرگز به 0.0.0.0 ببندید. درگاهی هم انتخاب کنید که هنوز آزاد است: شماره‌ای که پیش‌تر گرفته شده پروکسی را بی‌گوش می‌گذارد در حالی که کانتینر همچنان به پایان زمان می‌خورد، و این از خرابی شبکه‌ای که با آن شروع کردید قابل تشخیص نیست.

درگاه‌ها، نام‌ها و تصمیم TLS

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

TLS تصمیمی است که به تعویق می‌افتد و بعد شکست بعدی را توضیح می‌دهد. اگر پروکسی بایت‌ها را دست‌نخورده بفرستد، کانتینر گواهی خود API را می‌بیند و به CA داخلی در انبار اعتمادش نیاز دارد. اگر پروکسی TLS را پایان دهد، آن اعتماد به میزبان منتقل می‌شود و برنامه شروع می‌کند به ساختن پیوندها از سرآیند host که دریافت می‌کند. یک چیدمان انتخاب کنید و آن را به هر منبعی اعمال کنید، چون تغییرمسیری که دیگری صادر می‌کند به نام میزبانی اشاره دارد که کانتینر راهی برای حلش ندارد.

از درون سرویس بررسی کنید

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

چیدمان برگزیده را تا تازه است در README پروژه بنویسید. وگرنه نفر بعدی که به این پایان زمان برخورد، همان بعدازظهر را صرف کشف دوبارهٔ این می‌کند که تونل روی میزبان زندگی می‌کند.

مقالات مرتبط