ARG работает в FROM, но пуст внутри RUN: область действия аргумента сборки

В Dockerfile версия базового образа задаётся через ARG перед FROM. База выбирается правильно, но команда RUN записывает в файл пустую версию. Разработчик проверяет --build-arg и видит ожидаемое значение. Проблема в том, где переменная объявлена: глобальный ARG доступен при разборе FROM, но автоматически не становится аргументом внутри стадии.

Чтобы использовать его после FROM, ARG нужно объявить в этой стадии. Повторное объявление без значения подхватит переданное или глобальное значение по умолчанию. Это позволяет одной настройкой управлять выбором базы и содержимым сборочного шага, сохраняя ясную границу областей действия.

Минимальный Dockerfile для проверки

Сохраните следующий текст как Dockerfile в отдельной папке. Для примера нужен Docker с работающим builder и доступом к выбранному базовому образу.

ARG BASE_VERSION=3.22
FROM alpine:${BASE_VERSION}
ARG BASE_VERSION
RUN printf 'base=%s\n' "$BASE_VERSION" > /build-version.txt
CMD ["cat", "/build-version.txt"]

Соберите его командой docker build --build-arg BASE_VERSION=3.22 -t arg-scope-demo ., затем запустите docker run --rm arg-scope-demo. Ожидаемый текст — base=3.22. Если убрать второе ARG, команда RUN больше не получит это значение из объявления перед FROM.

В качестве версии здесь выбран обычный опубликованный тег для учебного опыта. Для воспроизводимого рабочего выпуска фиксируйте принятую в команде версию базы и при необходимости digest. Выбор способа фиксации базы — отдельное решение, а повторное объявление ARG необходимо независимо от него.

Наследование зависит от связи стадий

Если следующая стадия начинается от предыдущей по имени, объявленные в родительской стадии аргументы доступны её потомку. Две независимые стадии, каждая со своим внешним FROM, не получают общий stage ARG автоматически. Поэтому при чтении большого Dockerfile нужно смотреть не только на одинаковое имя переменной, но и на дерево наследования стадий.

Не переносите ARG в ENV только ради исчезновения пустоты. ENV сохраняется в конфигурации образа и по умолчанию попадает в окружение запущенного контейнера. Если версия нужна лишь для сборки файла, повторное ARG точнее выражает назначение. Если приложение действительно читает это значение во время запуска, тогда ENV может быть осмысленным отдельным решением.

Значения ARG не предназначены для секретов. Они могут оказаться в метаданных, истории или свидетельствах сборки. Область действия переменной не является границей конфиденциальности. Для доступа к закрытым ресурсам используйте механизм build secrets, согласованный с вашим builder, а не токен в аргументе версии.

Проверяйте наблюдаемый результат

Сообщение build-лога о выбранной базе проверяет только FROM. Оно не доказывает, что RUN увидел тот же аргумент. Учебный файл build-version.txt делает результат видимым после сборки и позволяет сравнить два запуска с разными значениями. В реальном проекте таким наблюдаемым результатом может стать версия сгенерированного пакета или manifest сборки.

Если RUN использует exec-форму, shell-подстановки сами не появятся: массив аргументов не запускает оболочку. В данном Dockerfile RUN записан в shell-форме, поэтому $BASE_VERSION раскрывает shell стадии. Учитывайте этот уровень отдельно от подстановки Dockerfile в FROM.

Проверьте сборку со значением по умолчанию и с явным --build-arg. Затем прочитайте файл внутри образа. Это два разных пути настройки, и оба должны приводить к ожидаемому результату без зависимости от локально экспортированной переменной вашего терминала.

Откройте программу курса Docker для разработчика. Продолжение этой задачи — уверенно разделять сборку образа и запуск контейнера, чтобы версия базы, сборочные параметры и настройки приложения менялись в правильном месте.

Ещё по теме

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

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

    Упакуйте приложение в Docker и запустите его с PostgreSQL через Compose. Образы, сети, тома, диагностика поломок, обновление версий и восстановление данных — с практикой и итоговым проектом.