如何向 Docker 容器传递参数

原文:https://yanbin.blog/pass-arguments-to-docker-container/#more-8608

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

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

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

对于一个 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> 查看实际启动的命令及参数

<think>好的,我现在需要帮助用户解决如何使用docker-java在启动容器传递参数的问题。用户已经提到他们参考了两篇关于安装Docker和配置docker-java的文章,所以可能需要基于这些前提来回答。 首先,我应该回忆docker-java的基本用法。docker-java是一个Java客户端库,用于与Docker守护进程交互。启动容器通常涉及创建容器配置,设置镜像,然后执行启动命令。传递参数可能涉及多个方面,比如设置环境变量、挂载卷、端口映射等。 用户可能需要一个具体的示例,所以我需要构造一个Java代码示例。需要确保代码中展示如何创建DockerClient实例,配置容器参数,比如Cmd、Env、Volumes、Ports等。同时,要注意引用用户提到的配置,比如使用tcp://0.0.0.0:2375来连接Docker守护进程,这应该在DockerClient的创建中体现出来。 另外,用户提供的引用[1]和[2]中提到需要配置Docker Engine的hosts暴露端口,所以在示例代码中可能需要指定DOCKER_HOST环境变量或通过其他方式连接到守护进程的2375端口。这部分需要注意是否正确配置了DockerClient的连接参数,确保示例代码能够实际运行。 接下来,我需要检查各个参数传递的方式是否正确。例如,使用withEnv添加环境变量,withVolumes挂载卷,withPortBindings设置端口映射。同时,Cmd应该用字符串数组表示,每个参数分开,这样避免shell解析的问题。 最后,需要确保生成的代码符合用户的环境,比如Docker镜像名称是否正确,用户可能需要替换为自己的镜像。此外,异常处理和资源关闭(如client.close())也是代码完整性的重要部分,应该包含在示例中。 在回答中,还需要考虑用户可能遇到的问题,例如连接失败、参数配置错误等,可以建议用户检查Docker守护进程是否在指定端口运行,以及参数是否正确。同时,用户可能需要进一步了解如何配置其他参数,所以生成的相关问题可以涉及更多配置项或高级用法。</think>在使用docker-java启动容器传递参数,主要通过`CreateContainerCmd`对象设置容器配置。以下是具体步骤和示例代码: ### 一、基础配置步骤 1. **创建DockerClient连接** 需指定Docker守护进程的TCP地址(根据引用[2]的端口配置): ```java DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() .withDockerHost("tcp://localhost:2375") // 对应hosts配置的端口 .build(); DockerClient client = DockerClientBuilder.getInstance(config).build(); ``` 2. **构建容器参数** 通过链式调用设置镜像、命令、环境变量等: ```java CreateContainerResponse container = client.createContainerCmd("ubuntu:20.04") .withCmd("/bin/bash", "-c", "echo Hello $NAME") // 执行命令并传递参数 .withEnv("NAME=World") // 设置环境变量 .withVolumes(new Volume("/host/path:/container/path")) // 挂载卷 .withPortBindings(PortBinding.parse("8080:80")) // 端口映射 .exec(); ``` ### 二、完整示例代码 ```java import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.api.model.*; public class DockerJavaDemo { public static void main(String[] args) { // 1. 创建连接(需确保Docker已开启2375端口[^2]) DockerClient client = DockerClientBuilder.getInstance( DefaultDockerClientConfig.createDefaultConfigBuilder() .withDockerHost("tcp://localhost:2375") .build() ).build(); try { // 2. 创建容器传递参数 CreateContainerResponse container = client.createContainerCmd("alpine:latest") .withCmd("sh", "-c", "echo $MESSAGE && tail -f /dev/null") .withEnv("MESSAGE=Hello from docker-java") .withPortBindings(PortBinding.parse("8080:80")) .withVolumes(new Volume("/data")) .exec(); // 3. 启动容器 client.startContainerCmd(container.getId()).exec(); System.out.println("容器ID: " + container.getId()); } catch (Exception e) { e.printStackTrace(); } finally { client.close(); } } } ``` ### 三、关键参数说明 | 参数类型 | 方法 | 示例值 | 作用 | |------------------|---------------------|---------------------------------|--------------------------| | **执行命令** | `.withCmd()` | `"sh", "-c", "echo $MESSAGE"` | 覆盖容器默认启动命令 | | **环境变量** | `.withEnv()` | `"MESSAGE=Hello"` | 向容器进程传递环境变量 | | **端口映射** | `.withPortBindings` | `PortBinding.parse("80:8080")` | 将宿主机端口映射到容器 | | **卷挂载** | `.withVolumes` | `new Volume("/host:/container")`| 持久化存储容器数据 | ### 四、常见问题排查 1. **连接拒绝** 检查Docker Engine是否配置了`"tcp://0.0.0.0:2375"`(引用[2]),并确保防火墙开放该端口。 2. **参数未生效** 使用`docker inspect <容器ID>`查看实际运行的容器配置,验证参数是否正确注入。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值