使用docker run命令在Dockerfile中传递参数给CMD

90
我对Docker还不熟悉,但我很难按照自己的意愿设置docker容器。我的nodejs应用程序可以在启动时接受两个参数。例如,我可以使用“node server.js 0 dev”或“node server.js 1 prod”来在生产模式和开发模式之间切换,并确定是否应打开集群。现在我想创建一个具有参数的docker映像,以执行类似的操作。到目前为止,我唯一能做的就是调整Dockerfile,添加以下命令:
CMD [ "node", "server.js", "0", "dev"]
然后通过“docker build -t me/app .”构建docker。最后使用“docker run -p 9000:9000 -d me/app”运行docker。
但是,如果我想切换到生产模式,我需要更改Dockerfile CMD为:
CMD [ "node", "server.js", "1", "prod"]
并且我需要杀死在端口9000上监听的旧实例并重新构建映像。我希望可以有类似于“docker run -p 9000:9000 environment=dev cluster=0 -d me/app”的东西,以使用“environment”和“cluster”参数运行nodejs命令,这样我就不需要再更改Dockerfile并重新构建docker了。如何实现这一点?

我来到这里,发现了聪明而有趣的想法,然而正确的方法是用文档中的此部分所示的ENTRYPOINT - Merlin
8个回答

133

请确保您的Dockerfile声明了一个环境变量使用ENV

ENV environment default_env_value
ENV cluster default_cluster_value

ENV <key> <value> 表单可以 内联替换

然后,您可以使用 docker run 传递环境变量。请注意,每个变量都需要特定的 -e 标志才能运行。

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

或者你可以通过compose文件设置它们

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

你的 Dockerfile CMD 可以使用该环境变量,但是,正如 问题 5509 中所提到的那样,你需要以 sh -c 的形式来使用它:

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

解释是shell负责扩展环境变量,而不是Docker。当您使用JSON语法时,您明确要求您的命令绕过shell直接执行。
Builder RUN(也适用于CMD)相同的思路:
与shell表单不同,exec表单不会调用命令shell。 这意味着不会发生常规的shell处理。 例如,RUN [ "echo", "$HOME" ] 将不会对$HOME进行变量替换。如果要使用shell处理,则使用shell表单或直接执行shell,例如:RUN [ "sh", "-c", "echo $HOME" ]。 在使用exec表单并直接执行shell的情况下,就像使用shell表单一样,是shell在执行环境变量扩展,而不是docker。

正如Pwnstar评论中所指出的:

当您使用shell命令时,该进程将不会id=1启动。


我该如何在Docker文件CMD中使用ENV,你能给我一个例子吗? - Jaaaaaaay
@Jaaaaaaay ENV是你的Dockerfile中的一个声明(请阅读https://docs.docker.com/engine/reference/builder/#/env),然后可以通过`docker run -e myvar=myvalue`来赋值。 - VonC
请注意使用Shell命令,以免该进程以id = 1启动。 - Pwnstar
1
@Pwnstar 很好的观点。我已经将您的评论包含在答案中以增加可见性。 - VonC

73

另一个选项是使用ENTRYPOINT来指定要运行的可执行文件为node,并使用CMD提供参数。文档中有一个在Exec form ENTRYPOINT example中的示例。

使用这种方法,你的Dockerfile看起来会像这样:

FROM ...

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

在dev环境下运行时使用相同的命令

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

在生产环境中运行时,您需要将参数传递给运行命令。

docker run -p 9000:9000 -d me/app 1 prod
你可能希望完全省略 CMD 并始终将 0 dev1 prod 作为参数传递给运行命令。这样,您不会意外地在开发中启动生产容器或在生产中启动开发容器。

16

选项1)使用环境变量

Dockerfile

# we need to specify default values
ENV ENVIRONMENT=production
ENV CLUSTER=1

# there is no need to use parameters array
CMD node server.js ${CLUSTER} ${ENVIRONMENT}

Docker运行

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

选项2) 传递参数

Dockerfile

# use entrypoint instead of CMD and do not specify any arguments
ENTRYPOINT node server.js

Docker运行

在Docker镜像名称后传递参数

$ docker run -p 9000:9000 -d me/app 0 dev

6

在Docker容器中通常的做法是通过传递环境变量来实现:

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

5

晚来一步,这里有一个巧妙的技巧,可以用来设置默认命令行参数,并支持使用自定义参数覆盖默认参数:

步骤1:在您的Dockerfile中以以下方式调用程序:

  ENV DEFAULT_ARGS "--some-default-flag=123 --foo --bar"
  CMD ["/bin/bash", "-c", "./my-nifty-executable   ${ARGS:-${DEFAULT_ARGS}}"]

第二步 现在,我们可以这样调用 docker 镜像:
  # this will invoke it with DEFAULT_ARGS
  docker run mydockerimage   

  # but this will invoke the docker image with custom arguments
  docker run   --env  ARGS="--alternative-args  --and-then-some=123"   mydockerimage 

您还可以根据需要调整此技术以执行更复杂的参数评估。Bash支持许多种一行构造,以帮助您实现这个目标。

希望这种技术能够帮助某些人节省几个小时的头痛。


4

有点跑题,构建参数存在的目的是允许你在构建时传入参数,这些参数会表现为环境变量,用于你的docker镜像构建过程中:

$ docker build --build-arg HTTP_PROXY=http://10.20.30.2:1234 .

3

不确定这是否有帮助,但我以这种方式使用过它,并且运行得非常好。

CMD ["node", "--inspect=0.0.0.0:9229", "--max-old-space-size=256", "/home/api/index.js"]


2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接