【问题标题】:How are CMD and ENTRYPOINT exec forms in a docker file parsed?如何解析 docker 文件中的 CMD 和 ENTRYPOINT exec 表单?
【发布时间】:2020-03-18 17:10:53
【问题描述】:

我在 Docker 容器中运行 Jupyter。以下 shell 形式可以正常运行:

CMD jupyter lab --ip='0.0.0.0' --port=8888 --no-browser --allow-root /home/notebooks

但是 docker 文件上的以下一个不会:

ENTRYPOINT ["/bin/sh", "-c"]
CMD ["jupyter", "lab", "--ip='0.0.0.0'", "--port=8888", "--no-browser", "--allow-root", "/home/notebooks"]

错误是:

usage: jupyter [-h] [--version] [--config-dir] [--data-dir] [--runtime-dir] [--paths] [--json] [subcommand]
jupyter: error: one of the arguments --version subcommand --config-dir --data-dir --runtime-dir --paths is required

显然/bin/sh -c 看到了jupyter 参数,但没有看到下面的参数。

有趣的是,

CMD ["jupyter", "lab", "--ip='0.0.0.0'", "--port=8888", "--no-browser", "--allow-root", "/home/notebooks"]

会正常运行,所以它不能是参数的数量,或者可以吗?

根据https://docs.docker.com/engine/reference/builder/#cmd,CMD的shell形式以/bin/sh -c执行。因此,从我的角度来看,我认为这两个版本几乎没有区别。但原因必须是当 ENTRYPOINT 和 CMD 同时存在时如何评估 exec 表单。

【问题讨论】:

    标签: docker dockerfile


    【解决方案1】:

    在非常低的级别上,Linux 命令是作为一系列“单词”执行的。通常,您的 shell 将采用类似 ls -l "a directory" 的命令行并将其分成三个单词 ls -l a directory。 (注意“a directory”中的空格:在shell形式中需要被引用为同一个词。)

    Dockerfile CMD 和 ENTRYPOINT(和 RUN)命令有两种形式。在您指定的看起来像 JSON 数组的表单中,您明确指定了单词的分解方式。如果它看起来不像 JSON 数组,则将整个内容视为单个字符串,并包装在 sh -c 命令中。

    # Explicitly spelling out the words
    RUN ["ls", "-l", "a directory"]
    
    # Asking Docker to run it via a shell
    RUN ls -l 'a directory'
    # The same as
    RUN ["sh", "-c", "ls -l 'a directory'"]
    

    如果你specify both ENTRYPOINT and CMD 两个单词列表只是组合在一起。对于您的示例而言,重要的是 sh -c 采用 下一个单词 并将其作为 shell 命令运行;任何剩余的单词都可以用作该命令字符串中的$0、$1、...位置参数。

    所以在你的例子中,最后运行的东西或多或少

    ENTRYPOINT+CMD ["sh", "-c", "jupyter", ...]
    # If the string "jupyter" contained "$1" it would expand to the --ip option
    

    另一个重要的推论是,实际上,ENTRYPOINT 不能是裸字符串格式:当 CMD 附加到它时,你会得到

    ENTRYPOINT some command
    CMD with args
    
    ENTRYPOINT+CMD ["sh", "-c", "some command", "sh", "-c", "with args"]
    

    根据相同的规则,所有 CMD 单词都会被忽略。

    实际上,您几乎不需要在 Dockerfile 中显式地放置 sh -c 或 SHELL 声明;改用字符串形式的命令,或者将复杂的逻辑放入 shell 脚本中。

    【讨论】:

    • 感谢大卫如此清晰的解释。希望https://docs.docker.com/engine/reference/builder/#entrypoint 会解释这一点。我的主要收获:sh -c from ENTRYPOINT "获取下一个单词并将其作为 shell 命令运行;任何剩余的单词都可以用作该命令字符串中的 $0、$1、... 位置参数",即命令字符串需要为 command $1 $2 $3 [...] 才能将 CMD 中的其他词“注入”到 shell 命令中。
    猜你喜欢
    • 2017-12-20
    • 2022-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-02
    • 1970-01-01
    • 2018-08-08
    • 1970-01-01
    相关资源
    最近更新 更多