Монтування тому Docker не вдається після підключення VPN: перевірте демон
Ваше bind-монтування гаразд. VPN змінює, який демон відповідає клієнту, тому надрукуйте endpoint, доведіть шлях там і припиніть редагувати том.
Майже кожен текст про цю проблему звинувачує том, і саме тому виправлення триває так довго. У вас було робоче bind-монтування, корпоративний VPN підключився, і docker compose up тепер воно падає через відсутній шлях до джерела, порожній каталог або монтування, яке рушій відмовляється створювати. У середовищах, які я прибирав, файл Compose не змінювався, а шлях був правильний. Змінилося те, який демон отримав запит, а помилка монтування була лише першим місцем, де ця різниця стала видимою.
Том ніколи не переміщався; перемістився запит
Прапорець -v /srv/app:/app не є копіюванням каталогу, яке виконує ваша оболонка. Клієнт перетворює його на запит API, а демон, що відповідає, розв'язує /srv/app відносно своєї власної файлової системи. Коли обидві сторони працюють на одному ядрі, цей шлях — той, який ви бачите у файловому менеджері. Коли endpoint змінюється, той самий рядок шукають на машині, яка ніколи не чула про каталог вашого проєкту, і демон або повідомляє, що джерело відсутнє, або тихо створює на його місці порожній каталог.
Ось чому іменований том продовжує працювати, а bind-монтування — ні. -v appdata:/app називає сховище, яким володіє демон, тому жоден шлях хоста не мусить існувати в певному місці. Якщо заміна одного сервісу з bind-монтування на іменований том змушує помилку зникнути, ви не виправили проблему. Ви підтвердили, що клієнт і демон розходяться в тому, якій файловій системі належить шлях, і ця розбіжність повертається в ту мить, коли вам знову потрібен справжній каталог хоста.
Надрукуйте endpoint, перш ніж торкатися монтування
Кілька налаштувань можуть спрямувати клієнт на endpoint, і вони не завжди згодні: прапорець --context , DOCKER_CONTEXT, DOCKER_HOST у поточній оболонці, і контекст, збережений у конфігурації клієнта. Замість міркувань про пріоритет надрукуйте всі й порівняйте відповіді.
docker context show
docker context ls
docker version
docker info --format "{{.Name}} {{.OperatingSystem}} {{.ServerVersion}}"
Конфігурація клієнта міститься в ~/.docker/config.json на Linux і macOS і в %USERPROFILE%\.docker\config.json на Windows, а значення, експортоване в поточному терміналі, перевизначає збережене лише для цієї сесії. Якщо endpoint — це ім'я хоста, а не локальний сокет, розв'язання імен і маршрути вирішують, куди він потрапить, і саме їх переписує клієнт VPN. Тунель, що додає маршрут або підміняє резолвер, може надіслати ім'я, яке колись означало цей ноутбук, на адресу всередині корпоративної мережі.
Порівнюйте стан, а не конфігурацію. Запустіть docker ps -a і docker images з вимкненим тунелем, а потім знову з увімкненим. Інший список контейнерів — сильніший доказ, ніж будь-який файл конфігурації, бо він показує, що на ваш запит відповів інший демон.
Запитайте демон, чи існує шлях там
Коли ви знаєте, до якого демона дісталися, змусьте його подивитися на каталог. --mount форма падає з яснішим повідомленням, ніж короткий прапорець, бо перевіряє джерело перед запуском контейнера:
docker run --rm --mount type=bind,source=/srv/app,target=/app alpine ls -la /app
Якщо він повідомляє, що джерело відсутнє, створіть його на машині, яка відповіла, або спрямуйте сервіс на іменований том і скопіюйте туди дані. Чого робити не варто — це перестворювати томи й переінстальовувати рушій до цієї перевірки. Такі дії достатньо змінюють середовище, щоб приховати невідповідність, але залишають її на місці.
Ще один шлях проходить зовсім в іншому місці: завантаження образу — робота демона, а не клієнта. Успішне завантаження доводить лише те, що демон може дістатися до registry, а робоче bind-монтування — лише те, що шлях розв'язався. Перевіряйте кожен шлях там, де він справді виконується, інакше витратите півдня не на те.
Імена інтранету не працюють з іншої причини
Друга половина того самого звіту зазвичай полягає в тому, що контейнер не може дістатися до intranet.corp.example. Це окремий механізм, а не друга проблема з томом. Контейнер отримує свій /etc/resolv.conf під час запуску, згенерований з налаштувань резолвера демона, тому DNS-сервер, який VPN додав після створення контейнера, не з'явиться там, доки контейнер не буде перестворено.
Вбудований резолвер Docker у мережі, визначеній користувачем, пересилає зовнішні запити на налаштовані сервери хоста, що допомагає, коли VPN переписує резолвер хоста, і шкодить, коли розділений DNS надсилає внутрішню зону на сервер, якого демон більше не бачить. Вузьке, явне зіставлення обходить суперечку для імен, які ніколи не змінюються:
services:
web:
extra_hosts:
- "intranet.corp.example:10.20.30.40"
Це записує запис у файл hosts контейнера, тож контейнер нічого не дізнається про тунель, а також заморожує адресу. Коли внутрішній хост переміщається, запис лишається неправильним, доки хтось не відредагує файл Compose. Використовуйте це для фіксованого пристрою або закріпленої бази даних, а імена, що рухаються, залиште DNS.
Що насправді змінює переінсталяція
Переінсталяція Docker Desktop перестворює VM, сокет і список контекстів, і монтування знову починає працювати. Цей результат реальний і нічого не вчить, тому та сама поломка повертається після наступного оновлення VPN. Переінсталяція замінила endpoint, який використовував клієнт; вона не узгодила шлях, який називає ваш проєкт, із файловою системою, яку демон справді читає.
Мій порядок роботи короткий. Підтвердьте, який endpoint використовуватиме клієнт. Підтвердьте, який демон відповів, порівнявши списки контейнерів. Попросіть той демон перелічити шлях до джерела. Лише після цього редагуйте монтування, додавайте запис hosts або змінюйте тип тома. Якщо ви підтримуєте файл Compose для команди, що працює через VPN, надавайте перевагу іменованим томам для даних, якими має володіти демон, щоб шлях хоста ніколи не став частиною договору.