Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use docker run command to pass arguments to CMD in Dockerfile

I'm new to Docker and I'm having a hard time to setup the docker container as I want. I have a nodejs app can take two parameters when start. For example, I can use

node server.js 0 dev

or

node server.js 1 prod

to switch between production mode and dev mode and determine if it should turn the cluster on. Now I want to create docker image with arguments to do the similar thing, the only thing I can do so far is to adjust the Dockerfile to have a line

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

and

docker build -t me/app . to build the docker.

Then docker run -p 9000:9000 -d me/app to run the docker.

But If I want to switch to prod mode, I need to change the Dockerfile CMD to be

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

and I need to kill the old one listening on port 9000 and rebuild the image. I wish I can have something like

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

to create an image and run the nodejs command with "environment" and "cluster" arguments, so I don't need to change the Dockerfile and rebuild the docker any more. How can I accomplish this?

like image 446
Jaaaaaaay Avatar asked Nov 29 '16 18:11

Jaaaaaaay


People also ask

Can you run docker commands in Dockerfile?

You can't run Docker commands from a Dockerfile (and shouldn't as a general rule try to run Docker commands from within Docker containers) but you can write an ordinary shell script on the host that runs the docker build && docker run .

How do I pass ARG in Dockerfile?

ARG instruction defines a variable that can be passed at build time. Once it is defined in the Dockerfile you can pass with this flag --build-arg while building the image. We can have multiple ARG instruction in the Dockerfile. ARG is the only instruction that can precede the FROM instruction in the Dockerfile.

How do I pass a variable in docker run?

With a Command Line Argument The command used to launch Docker containers, docker run , accepts ENV variables as arguments. Simply run it with the -e flag, shorthand for --env , and pass in the key=value pair: sudo docker run -e POSTGRES_USER='postgres' -e POSTGRES_PASSWORD='password' ...

How do I create a Run command in Dockerfile?

The basic syntax for the command is: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] You can run containers from locally stored Docker images. If you use an image that is not on your system, the software pulls it from the online registry.


2 Answers

Make sure your Dockerfile declares an environment variable with ENV:

ENV environment default_env_value ENV cluster default_cluster_value 

The ENV <key> <value> form can be replaced inline.

Then you can pass an environment variable with docker run. Note that each variable requires a specific -e flag to run.

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

Or you can set them through your compose file:

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

Your Dockerfile CMD can use that environment variable, but, as mentioned in issue 5509, you need to do so in a sh -c form:

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

The explanation is that the shell is responsible for expanding environment variables, not Docker. When you use the JSON syntax, you're explicitly requesting that your command bypass the shell and be executed directly.

Same idea with Builder RUN (applies to CMD as well):

Unlike the shell form, the exec form does not invoke a command shell.
This means that normal shell processing does not happen.

For example, RUN [ "echo", "$HOME" ] will not do variable substitution on $HOME. If you want shell processing then either use the shell form or execute a shell directly, for example: RUN [ "sh", "-c", "echo $HOME" ].

When using the exec form and executing a shell directly, as in the case for the shell form, it is the shell that is doing the environment variable expansion, not docker.

like image 104
VonC Avatar answered Sep 22 '22 16:09

VonC


Another option is to use ENTRYPOINT to specify that node is the executable to run and CMD to provide the arguments. The docs have an example in Exec form ENTRYPOINT example.

Using this approach, your Dockerfile will look something like

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

Running it in dev would use the same command

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

and running it in prod you would pass the parameters to the run command

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

You may want to omit CMD entirely and always pass in 0 dev or 1 prod as arguments to the run command. That way you don't accidentally start a prod container in dev or a dev container in prod.

like image 29
Roman Avatar answered Sep 24 '22 16:09

Roman