VPN підключено, браузер відкриває внутрішні сайти, а контейнер досі не може розв'язати ті самі імена. Встановлення пакета помирає з помилкою розв'язання імен на кшталт EAI_AGAIN, або застосунок не знаходить хост бази даних, який хост-машина розв'язує без жодної скарги. Контейнер не використовує резолвер, який встановив VPN. Docker скопіював конфігурацію резолвера хоста під час створення контейнера, і після цього її більше ніщо не копіювало.

Два подання резолвера на одній машині

У Linux Docker під час створення дає кожному контейнеру копію конфігурації резолвера хоста, відфільтровує адреси loopback і запускає вбудований DNS-сервер на 127.0.0.11, який пересилає запити назовні. Клієнт VPN, який переписує /etc/resolv.conf після запуску контейнера, нічого всередині нього не змінює. Перезапуск контейнера допомагає лише тоді, коли резолвер хоста в цю мить випадково правильний.

Docker Desktop на macOS або Windows додає шар, бо демон живе у віртуальній машині. Резолвер VPN хоста може ніколи не бути видимим для цієї ВМ, тому перезапуск не змінює геть нічого. Порівняйте два подання, перш ніж обирати рішення: docker exec mycontainer cat /etc/resolv.conf поряд зі списком резолверів хоста.

Контейнер, який перелічує публічний резолвер, тоді як хост перелічує внутрішній, розв'яже публічні імена і зазнає невдачі на внутрішніх. Ця розбіжність і є підписом проблеми.

Варіант A: передати резолвер тунелю кожному контейнеру

Docker приймає налаштування DNS для кожного контейнера, і це найменша робоча зміна. Спочатку прочитайте адреси резолвера VPN: scutil --dns на macOS, resolvectl status або власний вивід стану клієнта в Linux. Потім запустіть контейнер з ними, наприклад docker run --dns 192.168.2.1 --dns 192.168.123.1 myimage. У Compose те саме налаштування — список під сервісом, де воно живе в системі контролю версій, а не в чиїйсь історії команд оболонки.

Масив dns у /etc/docker/daemon.json застосовується до кожного нового контейнера, що звучить охайніше, ніж є. Він застосовується й тоді, коли тунель лежить, і резолвер, доступний лише через VPN, тоді ламає звичайне завантаження образів. Налаштування для окремого контейнера легше обґрунтувати й легше скасувати.

Варіант B: дозволити контейнеру використовувати мережу хоста

Мережа хоста повністю обходить копію, бо контейнер використовує резолвер хоста й тунель хоста. Вона ж прибирає ізоляцію мережі, яку ви, ймовірно, хотіли, і під Docker Desktop поводиться інакше. У Linux це прагматичний вибір для разового інструмента, якому треба розв'язувати внутрішні імена.

Для збірок не годиться жоден варіант, оскільки кроки збірки не успадковують прапорці часу виконання. Збирайте з мережею хоста там, де платформа це дозволяє, або тримайте встановлення залежностей на етапі, якому не потрібен тунель.

Чого не виправляє жоден варіант

Адреса DNS марна, якщо контейнер не має маршруту до неї. Резолвер VPN зазвичай сидить усередині адресного простору тунелю, тому контейнеру потрібен шлях до цього префікса. Стандартний міст Docker зазвичай маршрутизує назовні через хост, і цього достатньо, коли хост має маршрут тунелю і ввімкнено пересилання. Власні мостові мережі зі своїми пулами адрес можуть зіткнутися з підмережею VPN, і тоді запити йдуть зовсім в інше місце.

Перевірте підмережу мережі Docker проти підмережі VPN, перш ніж налагоджувати сам резолвер. Перекриття дає збій, який виглядає точно як зламаний DNS-сервер.

Перевірте всередині контейнера

Тестуйте за допомогою getent hosts db.internal всередині контейнера, а не браузером чи мовним середовищем виконання, бо вони застосовують власну політику DNS і власний кеш. Якщо getent розв'язує ім'я, а застосунок досі падає, резолвер тепер правильний, і решта проблеми на рівні застосунку. Якщо він не розв'язує, крок DNS не завершено.

Перестворіть контейнер після зміни його налаштувань DNS. Редагування /etc/resolv.conf у працюючому контейнері зникає разом із ним і приховує проблему конфігурації рівно настільки, скільки комусь потрібно, щоб його перезапустити.

Схожі статті