Используйте команду запуска docker для передачи аргументов CMD в Dockerfile

Я новичок в Docker, и мне сложно установить контейнер докеров, как я хочу. У меня приложение nodejs может принимать два параметра при запуске. Например, я могу использовать

node server.js 0 dev

или

node server.js 1 prod

для переключения между режимом работы и режимом dev и определить, нужно ли включить кластер. Теперь я хочу создать изображение докеров с аргументами, чтобы сделать подобное, единственное, что я могу сделать до сих пор, - настроить Dockerfile на строку

CMD [ "node", "server.js", "0", "dev"]

и

docker build -t me/app ., чтобы построить докер.

Затем docker run -p 9000:9000 -d me/app для запуска докера.

Но если я хочу перейти в режим prod, мне нужно изменить Dockerfile CMD на

CMD [ "node", "server.js", "1", "prod"],

и мне нужно убить старого, прослушивающего порт 9000, и перестроить изображение. Хотел бы я иметь что-то вроде

docker run -p 9000:9000 environment=dev cluster=0 -d me/app

чтобы создать образ и запустить команду nodejs с аргументами "среда" и "кластер", поэтому мне больше не нужно менять файл Docker и перестраивать докеры. Как я могу это сделать?

Ответ 1

Убедитесь, что ваш файл Dockerfile объявляет переменную окружения с ENV:

ENV environment default_env_value
ENV cluster default_cluster_value

Форма ENV <key> <value> может быть заменена встроенной.

Затем вы можете передать переменную окружения с запуском docker

docker run -p 9000:9000 -e environment=dev -e cluster=0 -d me/app

Или вы можете установить их через свой файл компоновки:

node:
  environment:
    - environment=dev
    - cluster=0

Ваш Dockerfile CMD может использовать эту переменную среды, но, как упоминалось в issue 5509, вам нужно сделать это в sh -c форма:

CMD ["sh", "-c", "node server.js ${cluster} ${environment}"]

Объяснение заключается в том, что оболочка отвечает за расширение переменных среды, а не Docker. Когда вы используете синтаксис JSON, вы явно запрашиваете, чтобы ваша команда обошла оболочку и выполнялась напрямую.

Такая же идея с Builder RUN (относится также к CMD):

В отличие от формы оболочки форма exec не вызывает командную оболочку.
Это означает, что нормальной обработки оболочки не происходит.

Например, RUN [ "echo", "$HOME" ] не будет выполнять замену переменных на $HOME. Если вы хотите обработать оболочку, то либо используйте форму оболочки, либо выполните оболочку напрямую, например: RUN [ "sh", "-c", "echo $HOME" ].

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

Ответ 2

Другой вариант - использовать ENTRYPOINT, чтобы указать, что node - исполняемый файл для запуска, и CMD, чтобы предоставить аргументы. В документах есть пример в Exec form ENTRYPOINT пример.

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

FROM ...

ENTRYPOINT [ "node",  "server.js" ]
CMD [ "0", "dev" ]

Запуск в dev будет использовать ту же команду

docker run -p 9000:9000 -d me/app

и запустив его в prod, вы передадите параметры команде запуска

docker run -p 9000:9000 -d me/app 1 prod

Вы можете полностью опустить CMD и всегда передавать в 0 dev или 1 prod в качестве аргументов команды запуска. Таким образом, вы случайно не запускаете контейнер prod в dev или dev-контейнере в prod.

Ответ 3

Типичный способ сделать это в контейнерах Docker - передать переменные среды:

docker run -p 9000:9000 -e NODE_ENV=dev -e CLUSTER=0 -d me/app