НейроАгент

Как использовать переменные в ENTRYPOINT Dockerfile JSON

Решение проблемы подстановки переменных в ENTRYPOINT Dockerfile JSON формате без дополнительных слоев и символических ссылок.

Вопрос

Как использовать переменные в ENTRYPOINT Dockerfile в формате JSON?

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

Я хочу создать один параметризованный шаблонный Dockerfile. С именами каталогов проблем нет, но я столкнулся с трудностями на слое ENTRYPOINT.

Если ENTRYPOINT описан в формате JSON (exec), то переменная не подставляется:

ENTRYPOINT ["dotnet", "${APPLICATION}.dll"]

Если использовать shell-форму, то параметризация работает:

ENTRYPOINT ["sh","-c","exec dotnet ${APPLICATION}.dll"]

Но что делать, если принципиально важно, чтобы ENTRYPOINT оставался в формате JSON?

Я нашел временное решение через создание символической ссылки, но оно требует дополнительного слоя и выглядит не очень элегантно:

RUN ln -s "${APPLICATION}.dll" /app/app.dll
ENTRYPOINT ["dotnet","/app/app.dll"]

Вот пример моего Dockerfile (APP - имя сервиса):

dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd __APP__ && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/__APP__/publish .
ENTRYPOINT ["dotnet", "__APP__.dll"]

Как правильно использовать переменные в ENTRYPOINT Dockerfile в формате JSON без создания дополнительных слоев и символических ссылок?

НейроАгент

Docker ENTRYPOINT в формате JSON не поддерживает подстановку переменных на этапе сборки, так как эта форма выполняет команду напрямую через exec(), а не через shell, где происходит обработка переменных. Однако есть несколько элегантных решений для параметризации ENTRYPOINT без создания дополнительных слоев.


Содержание


Основная проблема JSON формата ENTRYPOINT

Как объясняется в документации Docker, когда ENTRYPOINT указан в формате JSON (exec form), команда выполняется напрямую через exec(), а не через shell. Это означает, что переменные окружения не подставляются на этапе сборки:

dockerfile
# ❌ Не работает - переменная не подстанавливается
ENTRYPOINT ["dotnet", "${APPLICATION}.dll"]

В отличие от shell-формы, где переменные обрабатываются:

dockerfile
# ✅ Работает, но не JSON формат
ENTRYPOINT ["sh","-c","exec dotnet ${APPLICATION}.dll"]

Согласно Joe Yates’ блогу, это важное ограничение JSON формата, которое часто вызывает путаницу.


Решение с использованием шаблонизации

Самый чистый способ - использовать шаблонизацию на этапе сборки. Создайте шаблон ENTRYPOINT и замените переменные во время сборки:

Шаг 1: Создайте шаблон entrypoint.template

bash
#!/bin/sh
exec dotnet ${APPLICATION}.dll

Шаг 2: В Dockerfile используйте шаблон

dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd __APP__ && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/__APP__/publish .

# Создаем шаблон с переменными
RUN echo "#!/bin/sh\nexec dotnet \${APPLICATION}.dll" > /entrypoint.template

# Заменяем переменные и создаем исполняемый скрипт
RUN sed -i "s/__APP__/${APPLICATION}/g" /entrypoint.template && \
    chmod +x /entrypoint.template

# Используем скрипт в ENTRYPOINT JSON формате
ENTRYPOINT ["/entrypoint.template"]

Этот подход позволяет сохранить JSON формат ENTRYPOINT и избегать символических ссылок.


Решение через стартовый скрипт

Создайте универсальный стартовый скрипт, который может работать с разными приложениями:

Стартовый скрипт start.sh:

bash
#!/bin/sh
# Определяем имя приложения из переменной или по умолчанию
APP_NAME=${APPLICATION:-app}

# Запускаем приложение
exec dotnet "${APP_NAME}.dll"

Dockerfile:

dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd __APP__ && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/__APP__/publish .
COPY start.sh /app/

# Устанавливаем переменную приложения
ARG APPLICATION=app
ENV APPLICATION=${APPLICATION}

# Делаем скрипт исполняемым и используем его
RUN chmod +x /app/start.sh
ENTRYPOINT ["/app/start.sh"]

Этот подход очень гибкий и позволяет легко добавлять новую логику запуска.


Решение с использованием ARG переменных

Хотя ARG переменные не подставляются напрямую в ENTRYPOINT JSON, можно использовать их для создания динамического Dockerfile:

dockerfile
ARG APPLICATION=app

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd ${APPLICATION} && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/${APPLICATION}/publish .

# Создаем файл с правильным именем приложения
RUN echo "${APPLICATION}.dll" > /app/appname.txt

# Используем скрипт для чтения имени файла
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh:

bash
#!/bin/sh
APP_NAME=$(cat /app/appname.txt)
exec dotnet "${APP_NAME}"

Решение с помощью инструментов сборки

Используйте инструменты сборки для генерации Dockerfile:

С помощью Makefile:

makefile
build:
	@sed "s/__APP__/$(APPLICATION)/g" Dockerfile.template > Dockerfile
	docker build -t myapp:$(APPLICATION) .

run:
	docker run -e APPLICATION=$(APPLICATION) myapp:$(APPLICATION)

Dockerfile.template:

dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd __APP__ && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/__APP__/publish .
ENTRYPOINT ["dotnet", "__APP__.dll"]

Сравнение подходов

Подход Преимущества Недостатки
Шаблонизация Чистое решение, сохраняет JSON формат Требует дополнительных шагов сборки
Стартовый скрипт Гибкий, легко расширяемый Может добавлять небольшой оверхед
ARG + файл Использует стандартные Docker механизмы Требует создания дополнительного файла
Инструменты сборки Максимальная гибкость Требует внешних инструментов

Рекомендуемый подход

Для большинства случаев я рекомендую решение через стартовый скрипт, так как оно:

  1. Сохраняет JSON формат ENTRYPOINT
  2. Не требует дополнительных слоев Docker
  3. Легко поддерживается и расширяется
  4. Работает с любыми переменными окружения
  5. Позволяет добавлять логику обработки ошибок, логирования и т.д.

Пример финального Dockerfile:

dockerfile
ARG APPLICATION=app

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app
ENV DOTNET_NUGET_SIGNATURE_VERIFICATION=false
COPY . .
RUN cd ${APPLICATION} && dotnet publish -c Release -o publish /p:DebugType=None /p:DebugSymbols=false

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/${APPLICATION}/publish .
COPY start.sh /app/

# Устанавливаем переменную приложения
ENV APPLICATION=${APPLICATION}

# Делаем скрипт исполняемым и используем его
RUN chmod +x /app/start.sh
ENTRYPOINT ["/app/start.sh"]

start.sh:

bash
#!/bin/sh
# Обрабатываем переменные окружения
APP_NAME=${APPLICATION:-app}

# Добавляем логирование
echo "Starting application: $APP_NAME"

# Запускаем приложение
exec dotnet "${APP_NAME}.dll"

Этот подход решает вашу проблему элегантно и без создания дополнительных слоев или символических ссылок.


Источники

  1. Dockerfile reference | Docker Docs
  2. Docker ENTRYPOINT, CMD and run Arguments - Joe Yates’ Blog
  3. Integrating Docker Environment Variables in ENTRYPOINT Array | Baeldung on Ops
  4. How do I use Docker environment variable in ENTRYPOINT array? - Stack Overflow
  5. JSONArgsRecommended | Docker Docs
  6. Docker ARG, ENV and .env - a Complete Guide · vsupalov.com