如何向Docker容器传递参数

我们在运行 docker 镜像时希望能用下面的命令向容器传递命令行参数

形式一:

docker run <image-name> <command> arg1 arg2

形式二:

docker run <image-name> arg1 arg2

其实只有第一种形式,紧随镜像名后那个总是一个命令,其后才是参数。如果要向 docker 容器传递参数时,Dockerfile 该如何写,这就有必要稍稍了解一下 Dockerfile 中  CMD 和 ENTRYPOINT 这两个指令,并且它们有 exec 和 shell 两种格式的写法。

对于一个 docker 镜像,我们可以这么来理解  ENTRYPOINT 与 CMD 的关系

  1. 如果没有定义 ENTRYPOINT, CMD 将作为它的 ENTRYPOINT
  2. 定义了 ENTRYPOINT 的话,CMD 只为 ENTRYPOINT 提供参数
  3. CMD 可由 docker run <image> 后的命令覆盖,同时覆盖参数

对于 #1 和  #2 更精致的理解是容器运行的最终入口由 ENTRYPOINT 和实际的 CMD 拼接而成。ENTRYPOINT 和 CMD 需转换为实际镜像中的 exec 格式来拼接,合并后的第一个元素是命令,其余是它的参数。

举四个例子进行说明

一, 未定义 ENTRYPOINT, 定义了 CMD

#ENTRYPOINT []
CMD ["echo", "hello"]

 实际入口是它们拼接后还是 CMD 本身,["echo", "hello"]

二, 定义了 ENTRYPOINT 和 CMD

ENTRYPOINT ["echo", "hello"]
CMD ["echo", "world"]

实际入口是它们拼接起来,形成 ["echo", "hello", "echo", "world"], 执行 docker run test 显示为 hello echo world

三, 定义了 ENTRYPOINT, CMD 由 docker run 提供

ENTRYPOINT  ["echo", "hello"]

执行命令 docker run <image> rm -rf /, 实际入口是由 ["echo", "hello"] 与 ["rm", "-rf", "/"] 拼接而成的 ["echo", "hello", "rm", "-rf", "/"], 输出为 hello rm -rf /。看到 rm -rf / 也不用担心,用 ENTRYPOINT 就是让人放心

注:ENTRYPOINT 同样可以被覆盖,如 docker run --entrypoint ls test -l /,将会执行 ls -l / 命令。

四, 如果 ENTRYPOINT 用 shell 格式定义的

ENTRYPOINT java -jar /app.jar
CMD ["hello", "world"]

通过 docker inspect 命令看到镜像中实际的 ENTRYPOINT 是

ENTRYPOINT ["/bin/sh", "-c", "java -jar /app.jar"]

所以与 CMD 连接起来的入口就是 ["/bin/sh", "-c", "java -jar /app.jar", "hello", "world"],"bin/sh" 直接忽略掉后面的 "hello" 与 "world",这就是为什么shell 命令方式无法获取参数。

有了以上几点概念,以及四个实例作为感观认识后,想要怎么往容器传递参数应该很容易确定了。

未定义 ENTRYPOINT

没有定义 ENTRYPOINT 的镜像想怎么来就怎么来,docker run <image> 后面的输入你自己作主。

有定义 ENTRYPOINT

定义了 ENTRYPOINT 的镜像,则是 CMD 或 docker run <image> 后的输入作为  ENTRYPOINT 中命令的附加参数。再次提醒 shell 格式的 ENTRYPOINT 和 CMD  务必要转换为相应 exec  格式来理解。

shell 格式的 ENTRYPOINT

如果是复杂的 shell 命令不容易拆解出一个个参数,而希望用  shell 格式来定义 ENTRYPOINT 的话,也有办法。shell 格式的 ENTRYPOINT 是由 "/bin/sh -c" 启动的,而它是可以解析变量的。另一方面 CMD 或 docker run <image> 的输入第一个元素存成了 $0,其他剩余元素存为 $@, 所以 shell 格式的 ENTRYPOINT 可以这么写

ENTRYPOINT echo hello $0 $@

注:shell 中 $0 表示命令本身,$@ 为所有参数

这样执行下面 docker 命令将可获得所有的参数输入

$ docker run test world and China
hello world and China

如果只是按常规 shell 脚本来对待,想当然的写成

ENTRYPOINT echo hello $@

效果将是

$ docker run test world and China
hello and China

第一个参数将被丢失,docker run <image> 后第一个输入通常是一个命令,所以是 $0, 而 ENTRYPOINT 又希望它是一个普通参数,因此$0 $@ 要同时写上。

直接用 docker inspect <container-id> 查看

最简单且准确的方式就是直接用 docker inspect <container-id> 查看实际启动的命令及参数,不用猜测,docker inspect 也不会撒谎。对于如下 Dockerfile 定义

ENTRYPOINT java -Xmx256M -jar /app.jar
CMD ["echo", "hello"]

运行容器后,docker inspect <container-id>, 注意是容器 ID 而非镜像 ID。在显示的 inspect JSON 结果的最顶端我们可以看到

 

"Path": "/bin/sh",    
"Args": [ 
  "-c", 
  "java -Xmx256M -jar /app.jar", 
  "echo", 
  "hello"
],

 

ENTRYPOINT 和 CMD 如何组成实际入口,从这里的 Path 和 Args 是一目了然,也不用诧异为何 CMD 中的命令部分也变成了 ENTRYPOINT 命令的参数。

环境变量方式

对于 shell 格式的 ENTRYPOINT, 或者显式由 "/bin/sh -c" 来启动的命令,可以通过环境变量传入参数

ENTRYPOINT java $JAVA_OPTS -jar app.jar $0 $@
#或显式的 ENTRYPOINT ["/bin/sh", "-c", "java $JAVA_OPTS -jar /app.jar $0 $@"]

启动容器时的命令用

docker run -e JAVA_OPTS="-Xmx5G -Xms2G" <image-name> aa bb

那么实际执行  java 的完整命令就是

java -Xmx5G -Xms2G -jar /app.jar aa bb

此例结合了环境变更与 $0 $@ 的方式。

总结

最好再重复一遍容器参数传递及 Dockerfile 配置的要领

  1. 容器运行的最终入口由 ENTRYPOINT 和实际的 CMD 拼接而成。
  2. ENTRYPOINT 和 CMD 合并前需转换为 exec 格式(用 docker inspect <image> 查看),合并后(相当于数组) 第一个元素是命令,其他都为参数
  3. CMD 可在 Dockerfile 中配置,在启动容器时会被  docker run <image> 后的参数覆盖
  4. CMD 的 exec 格式中,第一个元素是 shell 的 $0, 其余元素是 shell 的 $@。当 ENTRYPOINT 中用 shell 格式或显式的 sh(bash等)就可以引用 $0, $@
  5. 环境变量的解析是通过 sh(bash 等) 来解析的,所以 ENTRYPOINT ["echo", "$name"] 中的 $name 是不被解析的
  6. 最能说明问题的是 docker inspect <container-id> 看个究竟,Path 和 Args 说明了一切

 

补充(2018-04-06)

为了准确获取输入参数,对于 $0 和 $@ 需要用引号括起来,像下面那样

ENTRYPOINT java $JAVA_OPTS -jar app.jar "$0" "$@"
#或 ENTRYPOINT ["/bin/sh", "-c", "java $JAVA_OPTS -jar /app.jar \"$0\" \"$@\""]
#更时髦一点的 shell exec 方式
# ENTRYPOINT exec java $JAVA_OPTS /-jar app.jar "$0" "$@"

下面解析为何 $0 和 $@  要用引号括起来,比如下面的 ENTRYPOINT

ENTRYPOINT java -jar /app.jar $0 $@

并且 /app.jar 中的 java main 方法如下:

 

public static void main(String[] args) {

        System.out.println("Arguments: " + String.join(", ", args));

  }

 

看下面的执行

$ docker run test "aa bb" cc "dd ee"
Arguments: [aa, bb, cc, dd, ee]

原本希望的是输入的三个参数 aa bbcc 和 dd ee, 可是在 java 里被认为是 5 个参数。通过 inspect 启动的容器

"Path": "/bin/sh",
"Args": [
    "-c",
    "java -jar /app.jar $0 $@",
    "aa bb",
    "cc",
    "dd ee"
],

说明参数的传入是没有问题,问题出在 $0 $@ 处,对 java -jar /app.jar $0 $@ 展开后的效果是

java -jar /app.jar aa bb cc dd ee

所以被打散后失去原有的组织关系,变为 5 个参数了.

而如果给 $0$@ 加上引号后

ENTRYPOINT java $JAVA_OPTS -jar /app.jar "$0" "$@"

构建镜像,重新执行

docker run test "aa bb" cc "dd ee"
Arguments: [aa bb, cc, dd ee]

这次参数的接收也没问题,加引号的方式可以让 java 接收到的参数保持原本的输入格式,即相当于

java -jar /app.jar "aa bb" cc "dd ee"

当然如果你的参数中不含有空格的话加不加引号都不会有事。

### 回答1: 要获取Docker容器启动的参数,可以通过以下几种方式: 1. 使用`docker inspect`命令:通过该命令可以获取到Docker容器的详细信息,包括容器的启动参数。可以通过`docker inspect <容器ID>`或`docker inspect <容器名称>`来获取指定容器的信息。在返回的JSON格式的输出中,可以找到`Args`字段,其中包含了容器启动时使用的命令和参数。 2. 使用`docker container ls`命令:通过该命令可以列出当前正在运行的容器的信息,包括容器的启动参数。可以通过`docker container ls`或`docker ps`命令来列出容器的基本信息。在输出的表格中,可以找到`COMMAND`列,其中包含了容器启动时使用的命令和参数。 3. 进入容器内部查看:可以通过`docker exec`命令进入容器内部,然后查看容器运行时的参数。可以使用`docker exec -it <容器ID> /bin/bash`命令来进入容器的shell环境。在容器内部,可以使用`ps aux`命令查看当前运行的进程和对应的参数。 总之,可以通过`docker inspect`、`docker container ls`命令或进入容器内部查看进程来获取Docker容器启动的参数。 ### 回答2: 要获取Docker容器启动的参数,可以使用docker inspect命令。该命令用于检查Docker对象的详细信息,包括容器、镜像、网络等。下面是获取容器启动参数的步骤: 1. 确保Docker已经安装并且正在运行。 2. 打开终端或命令提示符,然后执行以下命令: ``` docker ps ``` 这将列出所有正在运行的容器。请记住您要获取参数的容器的名称或ID。 3. 执行以下命令以获取该容器的详细信息: ``` docker inspect <容器名称或ID> ``` 替换<容器名称或ID>为您要查看的容器的实际名称或ID。 4. 在输出结果中,您可以找到启动参数的信息。启动参数通常位于"Config"部分,它包含容器的配置信息。在此部分中,您可以找到以下字段: - "Cmd"字段显示了容器启动时所使用的命令。 - "Args"字段显示了传递给启动命令的参数。 通过查看这些字段,您可以获取到容器启动的参数。 使用上述步骤,您可以轻松地获取Docker容器启动的参数信息。请确保您具备足够的权限来执行这些命令。 ### 回答3: 要获取Docker容器的启动参数,可以通过使用Docker命令行工具或Docker API来实现。 使用Docker命令行工具: 1. 执行命令 "docker ps" 列出当前正在运行的容器。 2. 根据容器的名称或ID选择要查看的容器。 3. 执行命令 "docker inspect <容器名称或ID>" 查看该容器的详细信息。 4. 在返回的JSON输出中,可以找到"Args"字段,其中包含了容器启动时的参数。 使用Docker API: 1. 使用适合自己编程语言的Docker客户端库连接到Docker守护进程。 2. 使用Docker API的"GET /containers/{id}/json"接口,其中{id}为容器的ID。 3. 根据返回的JSON响应,可以找到"Args"字段,其中包含了容器启动时的参数。 无论使用哪种方式,获取到的启动参数是一个数组,包含了容器启动时传递的所有参数。这些参数可以是容器指定的命令、环境变量值,或者其他自定义参数。 总之,获取Docker容器启动的参数可以通过使用Docker命令行工具或Docker API来实现,通过查看容器的详细信息并获取"Args"字段的值来获得。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值