После обновления образа Dockerfile кладёт файлы не туда: проверяем WORKDIR

Приложение собиралось из корпоративного базового образа и запускалось из /app. После обновления базы Dockerfile проходит сборку, но команда запуска не находит файл по абсолютному пути. Проверьте, куда шаги сборки действительно положили файлы. Инструкция WORKDIR app выглядит как короткая запись каталога приложения, однако итоговый путь зависит от рабочей папки, которую оставил автор базового образа.

Воспроизведите изменение на двух локальных стадиях

В примере исходный файл app.conf должен попасть в /app/app.conf. Положите рядом с учебным Dockerfile обычный файл с одной строкой mode=demo. Сохраните Dockerfile в отдельном каталоге и используйте пробный тег, свободный на вашей машине.

FROM alpine:3.22 AS base
WORKDIR /opt/runtime

FROM base AS release
WORKDIR app
COPY app.conf .
RUN pwd && ls -l ./app.conf
CMD ["cat", "/app/app.conf"]
docker build --progress=plain -t workdir-probe:demo .
docker image inspect workdir-probe:demo --format '{{.Config.WorkingDir}}'
docker run --rm --pull=never --network none workdir-probe:demo

Вторая стадия начинается из /opt/runtime, потому что она сделана через FROM base. Относительный WORKDIR app устанавливает /opt/runtime/app. Точка в COPY app.conf . относится к текущему рабочему каталогу, поэтому RUN находит файл рядом с собой. Инспекция покажет /opt/runtime/app, а запуск завершится с ошибкой: cat ищет абсолютный /app/app.conf. Если лог шага RUN свернулся из-за кэша, рабочий каталог итогового образа всё равно доступен через image inspect.

Посмотрите и на исходный Dockerfile в своём проекте. У COPY есть две стороны: откуда взять файл и куда его положить. В примере сборщик уже успешно прочитал app.conf из контекста. Путь назначения определила рабочая папка второй стадии. Именно поэтому пересборка без кэша или перенос Dockerfile в другое место не устраняет зависимость от базового образа.

Закрепите абсолютный каталог там, где он нужен

В стадии release замените WORKDIR app на WORKDIR /app. Повторите сборку и три проверки: теперь RUN и image inspect укажут на /app, а cat напечатает содержимое файла. Дальше относительные рабочие каталоги можно использовать внутри уже заданного корня. Например, WORKDIR /app, затем WORKDIR migrations означают /app/migrations. Важно, чтобы выбор этого корня был виден в Dockerfile.

При нескольких стадиях проверьте их по отдельности. FROM alpine:3.22 AS release запускает стадию из метаданных Alpine, даже если перед ней была стадия сборки с WORKDIR /build. FROM build AS release наследует рабочую папку стадии build. В COPY --from=build /build/output /app/output абсолютный путь источника позволяет видеть, из какого места другой стадии забирается артефакт. Абсолютная цель, в свою очередь, показывает, где он окажется в итоговом образе.

Если в образе рабочая папка уже правильная, но при запуске относительные пути всё равно ведут не туда, посмотрите конфигурацию запуска. docker run --workdir и working_dir в Compose могут задать папку контейнера при старте. Такой параметр не перемещает файлы, которые уже попали в образ при сборке. Проверка image inspect и рабочего каталога запущенного процесса отвечает на два разных вопроса.

Сделайте ошибку видимой до публикации

На актуальном Buildx добавьте docker build --check . к проверкам Dockerfile. Правило WorkdirRelativePath обращает внимание на первую относительную инструкцию в стадии, если до неё не был задан абсолютный путь. В учебной сборке это полезная подсказка. Для релиза добавьте ещё проверку результата: на тестовом запуске команда должна открыть файл там, где ожидает приложение. Так вы заметите и ошибочную настройку назначения, которую проверка относительных путей сама по себе не видит.

Посмотрите программу курса Docker и Docker Compose. Вы напишете Dockerfile для учебного HTTP-сервиса, разберёте многоэтапную сборку и проверите Dockerfile в заданиях на платформе. Перед этим можно открыть пробный урок про образы, теги и экземпляры контейнера, чтобы отработать различие между собранным образом и запуском из него.

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

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

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