docker - 常用命令 - 运行任务是参数的传递

运行容器时传参

ENTRYPOINT 和 CMD

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

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

如果要向 docker 容器传递参数时,Dockerfile 该如何写,这就有必要稍稍了解一下 Dockerfile 中 CMDENTRYPOINT这两个指令,并且它们有 exec 和 shell 两种格式的写法。

对于一个 docker 镜像,我们可以这么来理解 ENTRYPOINT 与 CMD 的关系
如果没有定义 ENTRYPOINT, CMD 将作为它的 ENTRYPOINT 定义了 ENTRYPOINT 的话,CMD 只为 ENTRYPOINT 提供参数 CMD 可由 docker run \<image> 后的命令覆盖,同时覆盖参数

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

举四个例子进行说明

  • 一, 未定义 ENTRYPOINT, 定义了 CMD

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

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

  • 二, 定义了 ENTRYPOINT 和 CMD

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

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

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

    1
    ENTRYPOINT  ["echo", "hello"]

    执行命令 docker run \ 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 格式定义的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
          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
    对于 shell 格式的 ENTRYPOINT, 或者显式由 "/bin/sh -c" 来启动的命令,可以通过环境变量传入参数

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

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

1
启动容器时的命令用

docker run -e JAVA_OPTS=”-Xmx5G -Xms2G” aa bb

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

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

1
2
### 使用CMD
- dockerfile 示例

FROM docker.io/python:3.6MAINTAINER tianye

设置容器时间

RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo ‘Asia/Shanghai’ >/etc/timezone
ENV LANG C.UTF-8 # 设置编码
ENV PATH=$PATH:/usr/local/lib/python3.6/
ENV PYTHONPATH $PATH # 配置环境变量

ENV PARAMS=”” # 给我们要传的参数一个初始值

#代码添加到code文件夹
ADD ./tttt/ /test/code/tttt/

#设置code文件夹为工作目录
WORKDIR /test/code/tttt/
CMD python3 ttt.py $PARAMS

1
- 创建镜像并启动容器

docker build -t my_image .
docker run -it -d –name my_container -e PARAMS=”hahaha” my_image # my_image 放最后 这里hahaha 加不加引号 无所谓
docker logs -f –tail 200 my_container
`

Dockerfile中 最后一行 $PARAMS 会解析为一个变量获取其值,也就是 docker run传入的参数 “hahaha”, 在python程序中通过 argv[1] 就可以获取到我们传入的”hahaha” !

需要注意的一点是Dockerfile 中CMD的用法,如果我们不传参那么写法有很多如:

  • CMD [“python3”, “ttt.py”]
  • CMD [python3, ttt.py]   
  • CMD “python3” “ttt.py”   
  • CMD python3 ttt.py

但是要传参的话: 我们的参数 \$PARAMS 是万万不能用 “ “ 的,不然Dockerfile会认为是普通字符串

  • CMD [“python”, “ttt.py”, \$PARAMS] (×) 原因可能是字符串和变量放到一个列表时,字符串优先级高,直接将 $PARAMS当作一个字符串处理
  • CMD [python3, ttt.py, $PARAMS] (×)   
  • CMD “python3” “ttt.py” $PARAMS (√,推荐)
  • CMD python3 ttt.py $PARAMS (√)
    CMD [] 形式,中括号中 必须用逗号分割; 如果不用中括号,不能用逗号分割!

    传参机制

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

参考资料

  1. CSDN博客
  2. 博客
-------------本文结束感谢您的阅读-------------