Bind mount в удалённом Docker: почему демон не видит каталог с ноутбука

Одна и та же команда поднимает контейнер на ноутбуке и падает на тестовом сервере с bind source path does not exist. Папка есть, автодополнение её находит, а переданные кавычки позволяют пути с пробелами дойти до Docker целиком. Проверьте, где запущен Docker Engine. Если вы переключили контекст на сервер, локальный терминал уже может отправлять команды другой машине. Путь по-прежнему вычисляет ваш shell, а открывать этот путь будет сервер.

Контекст определяет, где живёт источник

Рассмотрим команду из локальной папки проекта. "$PWD/config" оболочка подставляет ещё до обращения к Docker. При работе с удалённым демоном команда передаёт ему строку, например /home/anna/project/config. Содержимое каталога по этой строке не передаётся. Сервер проверяет собственную файловую систему и ищет на ней такой же путь. Если путь там есть, но принадлежит другой копии проекта, команда может успешно запуститься с чужой конфигурацией.

Посмотрите, с каким endpoint связан контекст. Здесь staging — имя уже настроенного контекста, а все команды только читают сведения.

docker context show
docker context inspect staging --format '{{.Endpoints.docker.Host}}'
docker --context staging ps --format '{{.Names}}'

Endpoint вида ssh://ops@staging.example означает, что путь нужно проверять на этом сервере. В команде из истории ищите также --context и --host. При запуске без явного флага проверьте переменные DOCKER_CONTEXT и DOCKER_HOST в том же терминале или задании CI. Для следующего опыта укажите --context staging явно, чтобы запуск и диагностика точно обращались к одному демону. В Docker Desktop демон тоже работает в отдельной Linux-VM, но Desktop поддерживает передачу файлов с рабочей машины. Этот механизм относится к Desktop и не делает файлы доступными произвольному удалённому Engine.

Как увидеть ошибку до запуска приложения

Предположим, на staging должен лежать каталог /srv/report/config, а в нём публичный файл app.conf. Запустите на тестовом сервере одноразовую проверку с тем же bind mount. Она прочитает сведения о файле и завершится, содержимое конфигурации в вывод не попадёт.

docker --context staging run --rm --pull=never --network none \
  --mount type=bind,src=/srv/report/config,dst=/check,readonly \
  alpine:3.22 sh -ec 'test -f /check/app.conf && ls -ld /check/app.conf'

Пример требует, чтобы тестовый образ alpine:3.22 уже был на staging. Если отсутствует исходный каталог, ошибку вернёт Docker до запуска sh. Если каталог примонтировался, но нужного файла в нём нет, с ненулевым кодом завершится test. Вывод ls означает, что файл виден внутри контейнера. Сравнить его с ожидаемой версией поможет хеш, когда у вас есть ожидаемое значение. Для проверки прав используйте тот же UID, под которым работает приложение, и проверяйте возможность чтения от его имени.

Для этой диагностики важна форма --mount. По умолчанию она сообщает об отсутствующем источнике. Короткая форма -v может создать отсутствующий путь как каталог на стороне демона. После этого исходная ошибка превращается в менее очевидную: приложение стартует с пустой конфигурацией или получает каталог там, где ожидало файл.

Где держать конфигурацию в следующем релизе

Если файл одинаковый у всех установок и меняется вместе с кодом, добавьте его через COPY на этапе сборки. Тогда его версия едет вместе с образом, а задача запуска сводится к выбору нужного образа. При обычной локальной сборке Docker передаёт выбранный контекст сборщику. Эта передача относится к docker build, тогда как bind mount в docker run открывает существующий путь у демона.

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

Начните с пробного урока про рабочую среду и первый запуск. Затем откройте программу курса Docker и Docker Compose. На локальных лабораторных вы соберёте собственный образ, настроите файлы и тома, а в итоговом проекте подготовите приложение с PostgreSQL к запуску другим разработчиком.

Продолжить на практике

  • Docker и Docker Compose: контейнеризация приложений ↗

    Практический курс Docker для разработчиков: упакуйте своё приложение, подключите PostgreSQL и запускайте окружение через Compose. Научитесь находить поломки, обновлять сервис и восстанавливать данные. 6 Dockerfile-задач с автопроверкой и 42 локальные лабораторные.