Docker COPY не видит файл: контекст сборки, путь Dockerfile и .dockerignore
Сборка падает на COPY ../shared/config.yaml, хотя файл виден в проводнике. Путь Dockerfile и корень доступных сборщику файлов — разные вещи. Последний аргумент docker build задаёт контекст. COPY читает из этого контекста, а не из произвольного каталога вокруг Dockerfile. Проверим границу на небольшом дереве, не меняя порядок слоёв ради кэша.
Восстанавливаем команду и дерево проекта
Пусть в репозитории есть такой набор файлов. Команды ниже выполняются из его корня:
project/
deploy/
Dockerfile
app/
main.py
shared/
config.yaml
.dockerignoredocker build -f deploy/Dockerfile -t context-lab .Точка задаёт context project. Флаг -f выбирает deploy/Dockerfile, но не переносит context в deploy. Поэтому инструкция COPY shared/config.yaml /app/config.yaml обращается к project/shared/config.yaml. Если запустить docker build -f Dockerfile . из deploy, context станет другим и shared окажется снаружи, хотя инструкция выглядит похожей.
Делаем пример без внешнего base image
Для проверки доступности файла достаточно образа scratch. Содержимое deploy/Dockerfile:
FROM scratch
COPY shared/config.yaml /app/config.yamlОн создаёт файловый образ без команды запуска; запускать его не требуется. В shared/config.yaml запишите mode: demo. Успешная сборка подтверждает, что указанный файл вошёл в context и не был исключён. Попытка COPY ../shared/config.yaml не является способом читать родительский каталог за пределами context: навигация к родителю не расширяет разрешённый набор файлов.
Такая ошибка часто встречается в монорепозитории, где Dockerfile лежит в services/api, а общая библиотека — в shared. Задайте корень репозитория как context и скорректируйте COPY относительно него. Не переносите секретный или огромный корень файловой системы в context только ради одного файла. Лучше выбрать минимальный общий каталог или осознанно использовать named context.
Проверяем фильтрацию .dockerignore
.dockerignore в корне context удаляет совпавшие пути из набора файлов перед передачей сборщику. Если там есть shared/, COPY не увидит файл, даже когда локально он существует. Сравните регистр и имя: на Linux config.yaml и Config.yaml отличаются, хотя локальная файловая система могла прощать различие.
**
!shared/
!shared/config.yamlЭто минимальный пример исключения почти всего с явным возвратом каталога и файла. Порядок правил с ! имеет значение: последнее подходящее правило определяет результат. Проверьте файл, а не только наличие строки с его именем. Dockerfile и ignore-файл имеют особое назначение при передаче, но это не означает, что их можно затем произвольно COPY при исключении.
Рядом с конкретным Dockerfile может лежать Dockerfile-specific ignore: для deploy/Dockerfile имя будет deploy/Dockerfile.dockerignore. Такой файл имеет приоритет над корневым .dockerignore. При расследовании нужно найти оба, иначе исправление общего ignore никак не изменит используемый фильтр. Это особенно заметно, когда тестовая и рабочая сборки используют разные Dockerfile.
Сверяем Compose и CI
Если сборка запускается через Compose, проверьте build.context и build.dockerfile в итоговой модели:
services:
api:
build:
context: .
dockerfile: deploy/DockerfileКонтекст здесь задаётся относительно расположения Compose-конфигурации с учётом правил нескольких файлов. Dockerfile ищется относительно context. В CI проверьте каталог checkout и рабочую директорию шага: та же точка в конце команды может обозначать другой набор файлов. Отдельно убедитесь, что нужный файл вообще появился после checkout или предыдущего этапа генерации.
Подтверждаем причину исправления
Запустите учебную сборку с явным -f и context, затем сравните ошибочный вызов из другого каталога. Не используйте --no-cache как основное лечение not found: он меняет повторное использование слоёв, но не расширяет доступ к файлам. Размер переданного context полезен как подсказка о фильтрации, однако сам по себе не доказывает наличие конкретного файла.
Сохраните в инструкции проекта одну команду с обозначенной рабочей директорией и объясните основание путей COPY. Тогда новый разработчик сможет собрать тот же набор файлов без догадок о расположении Dockerfile. Исправлением считается воспроизводимая граница входных данных сборки, а не случайно успешный запуск из одного терминала.
Ещё по теме
- Обновление приложения в Docker Compose: фиксируем версию и готовим откат
- Файл в образе есть, но запуск даёт no such file: загрузчик и библиотеки после multi-stage
Продолжить изучение
Хотите разобраться в контейнеризации целиком, от образа до работающего окружения? Сборка, сети, данные и запуск приложений входят в программу «Docker и Docker Compose: контейнеризация приложений».
Продолжить на практике
- Docker и Docker Compose: контейнеризация приложений ↗
Упакуйте приложение в Docker и запустите его с PostgreSQL через Compose. Образы, сети, тома, диагностика поломок, обновление версий и восстановление данных — с практикой и итоговым проектом.
