تقریباً هر نوشته‌ای درباره این مشکل، حجم را مقصر می‌داند و دقیقاً به همین دلیل رفع آن این‌قدر طول می‌کشد. شما یک bind mount داشتید که کار می‌کرد، VPN شرکتی متصل شد و docker compose up اکنون با مسیر مبدأ گمشده، پوشه خالی یا مانتی که موتور از ساختنش سر باز می‌زند شکست می‌خورد. در محیط‌هایی که من مرتب کردم، فایل Compose تغییر نکرده بود و مسیر درست بود. چیزی که عوض شد این بود که کدام دیمن درخواست را دریافت کرد، و خطای مانت فقط نخستین جایی بود که این تفاوت دیده شد.

حجم هرگز جابه‌جا نشد؛ درخواست جابه‌جا شد

یک -v /srv/app:/app کپی پوشه‌ای نیست که shell شما انجام می‌دهد. کلاینت آن را به درخواست API تبدیل می‌کند و دیمنی که پاسخ می‌دهد /srv/app را بر اساس فایل‌سیستم خودش حل می‌کند. وقتی هر دو طرف روی یک کرنل اجرا می‌شوند، همان مسیری است که در مدیر فایل می‌بینید. وقتی endpoint عوض می‌شود، همان رشته روی ماشینی جست‌وجو می‌شود که هرگز نامی از پوشه پروژه شما نشنیده است، و دیمن یا مبدأ را گمشده اعلام می‌کند یا بی‌صدا پوشه‌ای خالی به جای آن می‌سازد.

به همین دلیل است که حجم نام‌دار به کار خود ادامه می‌دهد ولی bind mount نه. -v appdata:/app به فضایی ذخیره‌سازی اشاره می‌کند که دیمن مالک آن است، پس هیچ مسیر میزبان لازم نیست جای خاصی وجود داشته باشد. اگر تبدیل یک سرویس از bind mount به حجم نام‌دار خطا را از بین ببرد، شما مشکل را حل نکرده‌اید. شما تأیید کرده‌اید که کلاینت و دیمن درباره اینکه مسیر به کدام فایل‌سیستم تعلق دارد اختلاف دارند، و این اختلاف همان لحظه‌ای برمی‌گردد که دوباره به پوشه واقعی میزبان نیاز پیدا کنید.

پیش از دست زدن به مانت، endpoint را چاپ کنید

چند تنظیم می‌توانند کلاینت را به یک endpoint اشاره دهند و همیشه هم‌نظر نیستند: پرچم --context ، DOCKER_CONTEXT، DOCKER_HOST در shell فعلی، و زمینه ذخیره‌شده در پیکربندی کلاینت. به جای استدلال درباره اولویت، همه را چاپ کنید و پاسخ‌ها را مقایسه کنید.

docker context show
docker context ls
docker version
docker info --format "{{.Name}} {{.OperatingSystem}} {{.ServerVersion}}"

پیکربندی کلاینت در ~/.docker/config.json روی Linux و macOS و در %USERPROFILE%\.docker\config.json روی Windows قرار دارد، و مقداری که در ترمینال فعلی export شود، مقدار ذخیره‌شده را فقط برای همان نشست بازنویسی می‌کند. اگر endpoint به جای سوکت محلی یک نام میزبان باشد، حل نام و مسیرها تعیین می‌کنند کجا فرود بیاید، و این‌ها دقیقاً چیزهایی هستند که کلاینت VPN بازنویسی می‌کند. تونلی که مسیری اضافه می‌کند یا resolver را عوض می‌کند می‌تواند نامی را که پیش‌تر به این لپ‌تاپ اشاره داشت به نشانی داخل شبکه شرکتی بفرستد.

وضعیت را مقایسه کنید، نه پیکربندی را. اجرا کنید docker ps -a و docker images را با تونل قطع، و سپس دوباره با تونل وصل. فهرست متفاوتی از کانتینرها شاهد قوی‌تری از هر فایل پیکربندی است، چون نشان می‌دهد دیمن دیگری به درخواست شما پاسخ داده است.

از دیمن بپرسید آیا مسیر آنجا وجود دارد

وقتی دانستید به کدام دیمن رسیده‌اید، کاری کنید که به پوشه نگاه کند. قالب --mount با پیام روشن‌تری از پرچم کوتاه شکست می‌خورد، چون پیش از راه‌اندازی کانتینر مبدأ را اعتبارسنجی می‌کند:

docker run --rm --mount type=bind,source=/srv/app,target=/app alpine ls -la /app

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

یک مسیر دیگر کاملاً جای دیگری اجرا می‌شود: کشیدن یک image کار دیمن است، نه کار کلاینت. کشیدن موفق فقط ثابت می‌کند دیمن می‌تواند به registry برسد، و bind mount سالم فقط ثابت می‌کند مسیری حل شده است. هر مسیر را همان‌جا که واقعاً اجرا می‌شود آزمایش کنید، وگرنه یک بعدازظهر را صرف مسیر اشتباه می‌کنید.

نام‌های اینترانت به دلیل دیگری شکست می‌خورند

نیمه دوم همان گزارش معمولاً این است که کانتینر نمی‌تواند به intranet.corp.exampleبرسد. این سازوکار جداگانه‌ای است، نه مشکل دوم حجم. کانتینر /etc/resolv.conf خود را هنگام شروع می‌گیرد، که از تنظیمات resolver دیمن ساخته می‌شود، پس سرور DNSی که VPN پس از ساخت کانتینر اضافه کرده، تا زمانی که کانتینر بازسازی نشود در آن ظاهر نمی‌شود.

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

services:
  web:
    extra_hosts:
      - "intranet.corp.example:10.20.30.40"

این کار یک ورودی در فایل hosts کانتینر می‌نویسد، پس کانتینر هرگز چیزی درباره تونل یاد نمی‌گیرد، و نشانی را هم ثابت می‌کند. وقتی میزبان داخلی جابه‌جا می‌شود، ورودی تا زمانی که کسی فایل Compose را ویرایش کند اشتباه می‌ماند. آن را برای دستگاهی ثابت یا پایگاه داده‌ای تثبیت‌شده به کار ببرید و نام‌هایی را که جابه‌جا می‌شوند به DNS بسپارید.

نصب دوباره در واقع چه چیزی را تغییر می‌دهد

نصب دوباره Docker Desktop ماشین مجازی، سوکت و فهرست زمینه‌ها را بازسازی می‌کند و مانت دوباره کار می‌کند. این نتیجه واقعی است و چیزی یاد نمی‌دهد، و به همین دلیل همان خرابی پس از به‌روزرسانی بعدی VPN برمی‌گردد. نصب دوباره endpointی را که کلاینت استفاده می‌کرد جایگزین کرد؛ اما مسیری را که پروژه شما نام می‌برد با فایل‌سیستمی که دیمن واقعاً می‌خواند هم‌ساز نکرد.

ترتیب کار من کوتاه است. تأیید کنید کلاینت از کدام endpoint استفاده خواهد کرد. با مقایسه فهرست کانتینرها تأیید کنید کدام دیمن پاسخ داد. از همان دیمن بخواهید مسیر مبدأ را فهرست کند. تنها پس از آن مانت را ویرایش کنید، ورودی hosts اضافه کنید یا نوع حجم را تغییر دهید. اگر فایل Compose را برای تیمی نگه می‌دارید که از طریق VPN کار می‌کند، برای داده‌هایی که دیمن باید مالک آن باشد حجم‌های نام‌دار را ترجیح دهید تا مسیر میزبان هرگز بخشی از قرارداد نشود.

مقالات مرتبط