【问题标题】:How does Docker run a command without invoking a command shell?Docker 如何在不调用命令 shell 的情况下运行命令?
【发布时间】:2019-05-19 00:47:31
【问题描述】:

我目前正在学习 Docker,并通过 Dockerfile reference,特别是 RUN 指令。 RUN 有两种形式 - shell 形式,它在 shell 中运行命令,以及 exec 形式,它“执行不调用命令 shell”(引自 Note 部分)。

如果我正确理解了文档,我的问题是 - Docker 是否以及如何在没有 shell 的情况下运行命令?

请注意,Can a command be executed without a shell? 的答案实际上并不能回答问题。

【问题讨论】:

  • 一旦shell解析了你输入的命令行,你认为shell本身如何调用外部命令? 必须有一个它可以调用的操作系统级接口。 (此外,pass-a-string-to-a-shell 方法存在重大安全漏洞——如果您在环境中或其他 argv 位置带外传递参数,则很容易避免 shell 注入漏洞,但是 @ 987654326@ 也没有提供方便的方法)。

标签: shell docker dockerfile


【解决方案1】:

如果我正确理解您的问题,您是在询问如何在不调用命令 shell 的情况下运行某些东西(特别是在 docker 的上下文中)。

在linux内核中运行的方式通常是使用the exec family of system calls.

例如,您将要运行的可执行文件的路径以及需要通过 execl 调用传递给它的参数传递给它。


这实际上是您的 shell(sh、bash、ksh、zsh)在后台所做的。如果您运行 strace -f bash -c "cat /tmp/foo"

之类的内容,您可以自己观察这一点

在该命令的输出中,您会看到如下内容:

execve("/bin/cat", ["cat", "/tmp/foo"], [/* 66 vars */]) = 0

真正发生的事情是 bash 在$PATH 中查找cat,然后发现cat 实际上是在/bin/cat 上可用的可执行二进制文件。然后它只是通过execve 调用它。以及您在上面看到的正确参数。

您可以轻松编写一个 C 程序来做同样的事情。 这是这样一个程序的样子:

#include<unistd.h>

int main() {

    execl("/bin/cat", "/bin/cat", "/tmp/foo", (char *)NULL);

    return 0;
}

每种语言都提供了自己与这些系统调用交互的方式。 C 可以,Python 可以,而 Go 也可以,这是大部分用于编写 Docker 的工具。当您点击docker build 时,docker 中的RUN 指令可能会转换为这些exec 调用之一。您可以在日志中运行strace -f docker build,然后运行grep for exec 调用,看看魔术是如何发生的。


通过 shell 和直接运行某些东西的唯一区别是你会失去 shell 将为你做的所有花哨的事情,例如变量扩展、可执行文件搜索等。

【讨论】:

  • 我对此不确定(这就是我搜索this question 的原因)。您是在告诉我,可以在没有 shell 的情况下运行命令?
  • @dayuloli,确实如此。稍后我会更新一个示例。
  • 看完你的回答我明白了很多,谢谢!
  • 几年后回到这个问题,这里有一个明显的错误:strace -f docker build 实际上不会显示 exec 系统调用,因为docker cli 客户端实际上并没有运行构建本身, docker 守护进程会。因此,在发出 docker build 命令之前,必须将 strace 附加到 docker 守护进程的 PID 以实际查看执行情况。
【解决方案2】:

一个程序可以在没有 shell 的情况下执行另一个程序;您只需创建一个新进程并将可执行文件加载到它上面,因此您不需要 shell。用户需要 shell 来启动程序,因为它是系统的用户界面。此外,程序无法在没有 shell 的情况下运行像 cd 或 rm 这样的内置命令,因为找不到可执行文件(认为有其他方法,但并不那么简单)。

【讨论】:

  • 你需要 shell 来运行脚本,因为解释器包含在其中。
  • 非常感谢您的回答,它为我澄清了很多。所以在这里,当我构建我的 Docker 镜像时,如果我使用 RUN 的 shell 形式,它将产生一个新的 shell,让 shell 解释命令,然后运行解释的命令。如果我使用 exec 表单,正在运行的当前进程 docker run 将创建一个新进程,并将可执行文件传递给它。
  • 没错。因此,在没有外壳的情况下运行一些东西更轻量级,但功能更少。如果您要运行的命令是可执行文件(二进制,而不是脚本),并且您不需要在命令行参数中转换某些内容(如环境变量),那么 shell 将只是一个负担。
【解决方案3】:

一般来说 - docker run 将使用其默认进程启动容器,当 docker exec 允许您在容器内运行您想要的任何进程时。

例如docker run microsoft/iis运行microsoft/iis容器会运行默认进程,即powershell。

但是你可以通过运行docker exec -i my_container cmd来运行cmd

See this answer for more details.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-25
    • 1970-01-01
    • 2014-12-17
    • 2012-02-05
    • 2014-06-10
    • 2017-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多